/* For reverse playback we use a technique that can be used for * any keyframe based video codec. * * Input: * Buffer decoding order: 7 8 9 4 5 6 1 2 3 EOS * Keyframe flag: K K * Discont flag: D D D * * - Each Discont marks a discont in the decoding order. * - The keyframes mark where we can start decoding. * * First we prepend incomming buffers to the gather queue, whenever we receive * a discont, we flush out the gather queue. * * The above data will be accumulated in the gather queue like this: * * gather queue: 9 8 7 * D * * Whe buffer 4 is received (with a DISCONT), we flush the gather queue like * this: * * while (gather) * take head of queue and prepend to decode queue. * if we copied a keyframe, decode the decode queue. * * After we flushed the gather queue, we add 4 to the (now empty) gather queue. * We get the following situation: * * gather queue: 4 * decode queue: 7 8 9 * * After we received 5 (Keyframe) and 6: * * gather queue: 6 5 4 * decode queue: 7 8 9 * * When we receive 1 (DISCONT) which triggers a flush of the gather queue: * * Copy head of the gather queue (6) to decode queue: * * gather queue: 5 4 * decode queue: 6 7 8 9 * * Copy head of the gather queue (5) to decode queue. This is a keyframe so we * can start decoding. * * gather queue: 4 * decode queue: 5 6 7 8 9 * * Decode frames in decode queue, store raw decoded data in output queue, we * can take the head of the decode queue and prepend the decoded result in the * output queue: * * gather queue: 4 * decode queue: * output queue: 9 8 7 6 5 * * Now output all the frames in the output queue, picking a frame from the * head of the queue. * * Copy head of the gather queue (4) to decode queue, we flushed the gather * queue and can now store input buffer in the gather queue: * * gather queue: 1 * decode queue: 4 * * When we receive EOS, the queue looks like: * * gather queue: 3 2 1 * decode queue: 4 * * Fill decode queue, first keyframe we copy is 2: * * gather queue: 1 * decode queue: 2 3 4 * * Decoded output: * * gather queue: 1 * decode queue: * output queue: 4 3 2 * * Leftover buffer 1 cannot be decoded and must be discarded. */ static GstFlowReturn theora_dec_flush_decode (GstTheoraDec * dec) { GstFlowReturn res = GST_FLOW_OK; while (dec->decode) { GstBuffer *buf = GST_BUFFER_CAST (dec->decode->data); GST_DEBUG_OBJECT (dec, "decoding buffer %p, ts %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); /* decode buffer, prepend to output queue */ res = theora_dec_decode_buffer (dec, buf); /* don't need it anymore now */ gst_buffer_unref (buf); dec->decode = g_list_delete_link (dec->decode, dec->decode); } while (dec->queued) { GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data); /* iterate ouput queue an push downstream */ res = gst_pad_push (dec->srcpad, buf); dec->queued = g_list_delete_link (dec->queued, dec->queued); } return res; }
static GstFlowReturn theora_dec_chain_forward (GstTheoraDec * dec, gboolean discont, GstBuffer * buffer) { GstFlowReturn result; result = theora_dec_decode_buffer (dec, buffer); gst_buffer_unref (buffer); return result; }
static GstFlowReturn theora_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * frame) { GstTheoraDec *dec; GstFlowReturn res; dec = GST_THEORA_DEC (bdec); res = theora_dec_decode_buffer (dec, frame->input_buffer, frame); switch (res) { case GST_FLOW_OK: res = gst_video_decoder_finish_frame (bdec, frame); break; case GST_CUSTOM_FLOW_DROP: res = gst_video_decoder_drop_frame (bdec, frame); break; default: gst_video_codec_frame_unref (frame); break; } return res; }
static gboolean theora_dec_set_format (GstVideoDecoder * bdec, GstVideoCodecState * state) { GstTheoraDec *dec; dec = GST_THEORA_DEC (bdec); /* Keep a copy of the input state */ if (dec->input_state) gst_video_codec_state_unref (dec->input_state); dec->input_state = gst_video_codec_state_ref (state); /* FIXME : Interesting, we always accept any kind of caps ? */ if (state->codec_data) { GstBuffer *buffer; GstMapInfo minfo; guint8 *data; guint size; guint offset; buffer = state->codec_data; gst_buffer_map (buffer, &minfo, GST_MAP_READ); offset = 0; size = minfo.size; data = (guint8 *) minfo.data; while (size > 2) { guint psize; GstBuffer *buf; psize = (data[0] << 8) | data[1]; /* skip header */ data += 2; size -= 2; offset += 2; /* make sure we don't read too much */ psize = MIN (psize, size); buf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, psize); /* first buffer is a discont buffer */ if (offset == 2) GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); /* now feed it to the decoder we can ignore the error */ theora_dec_decode_buffer (dec, buf, NULL); gst_buffer_unref (buf); /* skip the data */ size -= psize; data += psize; offset += psize; } gst_buffer_unmap (buffer, &minfo); } GST_DEBUG_OBJECT (dec, "Done"); return TRUE; }