static GstFlowReturn theora_dec_chain (GstPad * pad, GstBuffer * buf) { GstTheoraDec *dec; GstFlowReturn res; gboolean discont; dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); /* peel of DISCONT flag */ discont = GST_BUFFER_IS_DISCONT (buf); /* resync on DISCONT */ if (G_UNLIKELY (discont)) { GST_DEBUG_OBJECT (dec, "received DISCONT buffer"); dec->need_keyframe = TRUE; dec->last_timestamp = -1; dec->granulepos = -1; dec->discont = TRUE; } if (dec->segment.rate > 0.0) res = theora_dec_chain_forward (dec, discont, buf); else res = theora_dec_chain_reverse (dec, discont, buf); gst_object_unref (dec); return res; }
static void theora_dec_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstTheoraDec *dec = GST_THEORA_DEC (object); switch (prop_id) { case PROP_CROP: g_value_set_boolean (value, dec->crop); break; case PROP_TELEMETRY_MV: g_value_set_int (value, dec->telemetry_mv); break; case PROP_TELEMETRY_MBMODE: g_value_set_int (value, dec->telemetry_mbmode); break; case PROP_TELEMETRY_QI: g_value_set_int (value, dec->telemetry_qi); break; case PROP_TELEMETRY_BITS: g_value_set_int (value, dec->telemetry_bits); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean theora_dec_start (GstVideoDecoder * decoder) { GstTheoraDec *dec = GST_THEORA_DEC (decoder); GST_DEBUG_OBJECT (dec, "start"); th_info_clear (&dec->info); th_comment_clear (&dec->comment); GST_DEBUG_OBJECT (dec, "Setting have_header to FALSE"); dec->have_header = FALSE; gst_theora_dec_reset (dec); return TRUE; }
static gboolean theora_dec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query) { GstTheoraDec *dec = GST_THEORA_DEC (decoder); GstVideoCodecState *state; GstBufferPool *pool; guint size, min, max; GstStructure *config; if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (decoder, query)) return FALSE; state = gst_video_decoder_get_output_state (decoder); gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); dec->can_crop = FALSE; config = gst_buffer_pool_get_config (pool); if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) { gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); dec->can_crop = gst_query_find_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL); } if (dec->can_crop) { GstVideoInfo info = state->info; GstCaps *caps; /* Calculate uncropped size */ gst_video_info_set_format (&info, info.finfo->format, dec->info.frame_width, dec->info.frame_height); size = MAX (size, info.size); caps = gst_video_info_to_caps (&info); gst_buffer_pool_config_set_params (config, caps, size, min, max); gst_caps_unref (caps); } gst_buffer_pool_set_config (pool, config); gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); gst_object_unref (pool); gst_video_codec_state_unref (state); return TRUE; }
static void theora_dec_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstTheoraDec *dec = GST_THEORA_DEC (object); switch (prop_id) { case ARG_CROP: g_value_set_boolean (value, dec->crop); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static GstStateChangeReturn theora_dec_change_state (GstElement * element, GstStateChange transition) { GstTheoraDec *dec = GST_THEORA_DEC (element); GstStateChangeReturn ret; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: th_info_clear (&dec->info); th_comment_clear (&dec->comment); GST_DEBUG_OBJECT (dec, "Setting have_header to FALSE in READY->PAUSED"); dec->have_header = FALSE; dec->have_par = FALSE; gst_theora_dec_reset (dec); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; default: break; } ret = parent_class->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: th_info_clear (&dec->info); th_comment_clear (&dec->comment); th_setup_free (dec->setup); dec->setup = NULL; th_decode_free (dec->decoder); dec->decoder = NULL; gst_theora_dec_reset (dec); break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return ret; }
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_stop (GstVideoDecoder * decoder) { GstTheoraDec *dec = GST_THEORA_DEC (decoder); GST_DEBUG_OBJECT (dec, "stop"); th_info_clear (&dec->info); th_comment_clear (&dec->comment); th_setup_free (dec->setup); dec->setup = NULL; th_decode_free (dec->decoder); dec->decoder = NULL; gst_theora_dec_reset (dec); if (dec->input_state) { gst_video_codec_state_unref (dec->input_state); dec->input_state = NULL; } if (dec->output_state) { gst_video_codec_state_unref (dec->output_state); dec->output_state = NULL; } return TRUE; }
static gboolean theora_dec_src_query (GstPad * pad, GstQuery * query) { GstTheoraDec *dec; gboolean res = FALSE; dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { gint64 granulepos, value; GstFormat my_format, format; gint64 time; /* we can convert a granule position to everything */ granulepos = dec->granulepos; GST_LOG_OBJECT (dec, "query %p: we have current granule: %lld", query, granulepos); /* parse format */ gst_query_parse_position (query, &format, NULL); /* and convert to the final format in two steps with time as the * intermediate step */ my_format = GST_FORMAT_TIME; if (!(res = theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT, granulepos, &my_format, &time))) goto error; time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time); GST_LOG_OBJECT (dec, "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time)); if (!(res = theora_dec_src_convert (pad, my_format, time, &format, &value))) goto error; gst_query_set_position (query, format, value); GST_LOG_OBJECT (dec, "query %p: we return %lld (format %u)", query, value, format); break; } case GST_QUERY_DURATION: { GstPad *peer; if (!(peer = gst_pad_get_peer (dec->sinkpad))) goto error; /* forward to peer for total */ res = gst_pad_query (peer, query); gst_object_unref (peer); if (!res) goto error; break; } case GST_QUERY_CONVERT: { GstFormat src_fmt, dest_fmt; gint64 src_val, dest_val; gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); if (!(res = theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) goto error; gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } default: res = gst_pad_query_default (pad, query); break; } done: gst_object_unref (dec); return res; /* ERRORS */ error: { GST_DEBUG_OBJECT (dec, "query failed"); goto done; } }
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; }
/* FIXME : Do we want to handle hard resets differently ? */ static gboolean theora_dec_reset (GstVideoDecoder * bdec, gboolean hard) { gst_theora_dec_reset (GST_THEORA_DEC (bdec)); return TRUE; }
static gboolean theora_dec_src_query (GstPad * pad, GstQuery * query) { GstTheoraDec *dec; gboolean res = FALSE; dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { gint64 value; GstFormat format; gint64 time; /* parse format */ gst_query_parse_position (query, &format, NULL); time = dec->last_timestamp; time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time); GST_LOG_OBJECT (dec, "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time)); if (!(res = theora_dec_src_convert (pad, GST_FORMAT_TIME, time, &format, &value))) goto error; gst_query_set_position (query, format, value); GST_LOG_OBJECT (dec, "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value, format); break; } case GST_QUERY_DURATION: { /* forward to peer for total */ res = gst_pad_peer_query (dec->sinkpad, query); if (!res) goto error; break; } case GST_QUERY_CONVERT: { GstFormat src_fmt, dest_fmt; gint64 src_val, dest_val; gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); if (!(res = theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) goto error; gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } default: res = gst_pad_query_default (pad, query); break; } done: gst_object_unref (dec); return res; /* ERRORS */ error: { GST_DEBUG_OBJECT (dec, "query failed"); goto done; } }
static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event) { gboolean ret = FALSE; GstTheoraDec *dec; dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); GST_LOG_OBJECT (dec, "handling event"); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_FLUSH_STOP: gst_theora_dec_reset (dec); ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_EOS: ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_NEWSEGMENT: { gboolean update; GstFormat format; gdouble rate, arate; gint64 start, stop, time; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); /* we need TIME format */ if (format != GST_FORMAT_TIME) goto newseg_wrong_format; /* now configure the values */ gst_segment_set_newsegment_full (&dec->segment, update, rate, arate, format, start, stop, time); /* We don't forward this unless/until the decoder is initialised */ if (dec->have_header) { ret = gst_pad_push_event (dec->srcpad, event); dec->sent_newsegment = TRUE; } else { gst_event_unref (event); ret = TRUE; } break; } default: ret = gst_pad_push_event (dec->srcpad, event); break; } done: gst_object_unref (dec); return ret; /* ERRORS */ newseg_wrong_format: { GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); gst_event_unref (event); goto done; } }
static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event) { gboolean res = TRUE; GstTheoraDec *dec; dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstFormat format, tformat; gdouble rate; GstEvent *real_seek; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 cur, stop; gint64 tcur, tstop; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); gst_event_unref (event); /* we have to ask our peer to seek to time here as we know * nothing about how to generate a granulepos from the src * formats or anything. * * First bring the requested format to time */ tformat = GST_FORMAT_TIME; if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur))) goto convert_error; if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop))) goto convert_error; /* then seek with time on the peer */ real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, cur_type, tcur, stop_type, tstop); res = gst_pad_push_event (dec->sinkpad, real_seek); break; } case GST_EVENT_QOS: { gdouble proportion; GstClockTimeDiff diff; GstClockTime timestamp; gst_event_parse_qos (event, &proportion, &diff, ×tamp); /* we cannot randomly skip frame decoding since we don't have * B frames. we can however use the timestamp and diff to not * push late frames. This would at least save us the time to * crop/memcpy the data. */ GST_OBJECT_LOCK (dec); dec->proportion = proportion; dec->earliest_time = timestamp + diff; GST_OBJECT_UNLOCK (dec); GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp), diff); res = gst_pad_push_event (dec->sinkpad, event); break; } default: res = gst_pad_push_event (dec->sinkpad, event); break; } done: gst_object_unref (dec); return res; /* ERRORS */ convert_error: { GST_DEBUG_OBJECT (dec, "could not convert format"); goto done; } }
static gboolean theora_dec_setcaps (GstPad * pad, GstCaps * caps) { GstTheoraDec *dec; GstStructure *s; const GValue *codec_data; dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); s = gst_caps_get_structure (caps, 0); /* parse the par, this overrides the encoded par */ dec->have_par = gst_structure_get_fraction (s, "pixel-aspect-ratio", &dec->par_num, &dec->par_den); if ((codec_data = gst_structure_get_value (s, "codec_data"))) { if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) { GstBuffer *buffer; guint8 *data; guint size; guint offset; buffer = gst_value_get_buffer (codec_data); offset = 0; size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); 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_create_sub (buffer, 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_chain (pad, buf); /* skip the data */ size -= psize; data += psize; offset += psize; } } } gst_object_unref (dec); return TRUE; }
static gboolean theora_dec_sink_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value) { gboolean res = TRUE; GstTheoraDec *dec; if (src_format == *dest_format) { *dest_value = src_value; return TRUE; } dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); /* we need the info part before we can done something */ if (!dec->have_header) goto no_header; switch (src_format) { case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_TIME: *dest_value = _theora_granule_start_time (dec, src_value); break; default: res = FALSE; } break; case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_DEFAULT: { guint rest; /* framecount */ *dest_value = gst_util_uint64_scale (src_value, dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator); /* funny way of calculating granulepos in theora */ rest = *dest_value / dec->info.keyframe_frequency_force; *dest_value -= rest; *dest_value <<= dec->granule_shift; *dest_value += rest; break; } default: res = FALSE; break; } break; default: res = FALSE; } done: gst_object_unref (dec); return res; /* ERRORS */ no_header: { GST_DEBUG_OBJECT (dec, "no header yet, cannot convert"); res = FALSE; goto done; } }
static gboolean theora_dec_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value) { gboolean res = TRUE; GstTheoraDec *dec; guint64 scale = 1; if (src_format == *dest_format) { *dest_value = src_value; return TRUE; } dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); /* we need the info part before we can done something */ if (!dec->have_header) goto no_header; switch (src_format) { case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_value = gst_util_uint64_scale_int (src_value, 2, dec->info.height * dec->info.width * 3); break; case GST_FORMAT_TIME: /* seems like a rather silly conversion, implement me if you like */ default: res = FALSE; } break; case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: scale = 3 * (dec->info.width * dec->info.height) / 2; case GST_FORMAT_DEFAULT: *dest_value = scale * gst_util_uint64_scale (src_value, dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND); break; default: res = FALSE; } break; case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_TIME: *dest_value = gst_util_uint64_scale (src_value, GST_SECOND * dec->info.fps_denominator, dec->info.fps_numerator); break; case GST_FORMAT_BYTES: *dest_value = gst_util_uint64_scale_int (src_value, 3 * dec->info.width * dec->info.height, 2); break; default: res = FALSE; } break; default: res = FALSE; } done: gst_object_unref (dec); return res; /* ERRORS */ no_header: { GST_DEBUG_OBJECT (dec, "no header yet, cannot convert"); res = FALSE; goto done; } }