diff options
author | Dave Robillard <dave@drobilla.net> | 2009-07-10 20:44:23 -0400 |
---|---|---|
committer | Dave Robillard <dave@drobilla.net> | 2009-07-10 20:44:23 -0400 |
commit | e3f368047b84fa35a3df286ef9775fed09e9eef9 (patch) | |
tree | 3893f3a6297d382c30e7a0744a1af4efec71c5ee /ext | |
parent | e289c875a5e0ee56dd8289e78b63f36f709b6534 (diff) | |
parent | ba89c9e518ded3d919461903a444799a5115c34f (diff) | |
download | gst-plugins-bad-e3f368047b84fa35a3df286ef9775fed09e9eef9.tar.gz gst-plugins-bad-e3f368047b84fa35a3df286ef9775fed09e9eef9.tar.bz2 gst-plugins-bad-e3f368047b84fa35a3df286ef9775fed09e9eef9.zip |
Merge branch 'fdo' into lv2
Diffstat (limited to 'ext')
-rw-r--r-- | ext/ivorbis/Makefile.am | 4 | ||||
-rw-r--r-- | ext/ivorbis/vorbisdec.c | 19 | ||||
-rw-r--r-- | ext/mimic/gstmimdec.c | 8 | ||||
-rw-r--r-- | ext/mimic/gstmimenc.c | 331 | ||||
-rw-r--r-- | ext/mimic/gstmimenc.h | 7 |
5 files changed, 339 insertions, 30 deletions
diff --git a/ext/ivorbis/Makefile.am b/ext/ivorbis/Makefile.am index 9fbea14c..83db5eab 100644 --- a/ext/ivorbis/Makefile.am +++ b/ext/ivorbis/Makefile.am @@ -3,10 +3,10 @@ plugin_LTLIBRARIES = libgstivorbis.la libgstivorbis_la_SOURCES = vorbis.c vorbisfile.c vorbisdec.c libgstivorbis_la_CFLAGS = $(GST_CFLAGS) $(IVORBIS_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) libgstivorbis_la_LIBADD = $(IVORBIS_LIBS) \ - $(IVORBISFILE_LIBS) \ $(GST_BASE_LIBS) \ $(GST_PLUGINS_BASE_LIBS) \ - -lgstaudio-@GST_MAJORMINOR@ + -lgstaudio-@GST_MAJORMINOR@ \ + -lgsttag-@GST_MAJORMINOR@ libgstivorbis_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstivorbis_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/ext/ivorbis/vorbisdec.c b/ext/ivorbis/vorbisdec.c index 325c46a3..4502a773 100644 --- a/ext/ivorbis/vorbisdec.c +++ b/ext/ivorbis/vorbisdec.c @@ -651,23 +651,26 @@ channel_count_error: static GstFlowReturn vorbis_handle_comment_packet (GstIVorbisDec * vd, ogg_packet * packet) { -#if 0 guint bitrate = 0; gchar *encoder = NULL; - GstTagList *list; + GstTagList *list, *old_list; GstBuffer *buf; GST_DEBUG_OBJECT (vd, "parsing comment packet"); - buf = gst_buffer_new_and_alloc (packet->bytes); - GST_BUFFER_DATA (buf) = packet->packet; + buf = gst_buffer_new (); + GST_BUFFER_DATA (buf) = packet->packet->buffer->data; + GST_BUFFER_SIZE (buf) = packet->packet->buffer->size; list = gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\003vorbis", 7, &encoder); + old_list = vd->taglist; vd->taglist = gst_tag_list_merge (vd->taglist, list, GST_TAG_MERGE_REPLACE); + if (old_list) + gst_tag_list_free (old_list); gst_tag_list_free (list); gst_buffer_unref (buf); @@ -683,18 +686,18 @@ vorbis_handle_comment_packet (GstIVorbisDec * vd, ogg_packet * packet) gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER_VERSION, vd->vi.version, GST_TAG_AUDIO_CODEC, "Vorbis", NULL); - if (vd->vi.bitrate_nominal > 0) { + if (vd->vi.bitrate_nominal > 0 && vd->vi.bitrate_nominal <= 0x7FFFFFFF) { gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_NOMINAL_BITRATE, (guint) vd->vi.bitrate_nominal, NULL); bitrate = vd->vi.bitrate_nominal; } - if (vd->vi.bitrate_upper > 0) { + if (vd->vi.bitrate_upper > 0 && vd->vi.bitrate_upper <= 0x7FFFFFFF) { gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE, (guint) vd->vi.bitrate_upper, NULL); if (!bitrate) bitrate = vd->vi.bitrate_upper; } - if (vd->vi.bitrate_lower > 0) { + if (vd->vi.bitrate_lower > 0 && vd->vi.bitrate_lower <= 0x7FFFFFFF) { gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_MINIMUM_BITRATE, (guint) vd->vi.bitrate_lower, NULL); if (!bitrate) @@ -715,7 +718,7 @@ vorbis_handle_comment_packet (GstIVorbisDec * vd, ogg_packet * packet) gst_element_post_message (GST_ELEMENT_CAST (vd), gst_message_new_tag (GST_OBJECT (vd), gst_tag_list_copy (vd->taglist))); } -#endif + return GST_FLOW_OK; } diff --git a/ext/mimic/gstmimdec.c b/ext/mimic/gstmimdec.c index c7f1ec87..1579bd41 100644 --- a/ext/mimic/gstmimdec.c +++ b/ext/mimic/gstmimdec.c @@ -172,7 +172,7 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) (mimdec->have_header ? mimdec->payload_size : 24)) { if (!mimdec->have_header) { header = (guchar *) gst_adapter_peek (mimdec->adapter, 24); - header_size = GUINT16_FROM_LE (*(guint16 *) (header + 0)); + header_size = header[0]; if (header_size != 24) { GST_WARNING_OBJECT (mimdec, "invalid frame: header size %d incorrect", header_size); @@ -201,6 +201,12 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) mimdec->have_header = TRUE; } + /* Check if its paused frame, drop it */ + if (mimdec->payload_size == 0) { + mimdec->have_header = FALSE; + continue; + } + if (gst_adapter_available (mimdec->adapter) < mimdec->payload_size) { goto out; } diff --git a/ext/mimic/gstmimenc.c b/ext/mimic/gstmimenc.c index a69ab2cc..84f34012 100644 --- a/ext/mimic/gstmimenc.c +++ b/ext/mimic/gstmimenc.c @@ -42,6 +42,15 @@ GST_DEBUG_CATEGORY (mimenc_debug); #define MAX_INTERFRAMES 15 + +enum +{ + PROP_0, + PROP_PAUSED_MODE, + PROP_LAST +}; + + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -75,11 +84,17 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", static gboolean gst_mimenc_setcaps (GstPad * pad, GstCaps * caps); static GstFlowReturn gst_mimenc_chain (GstPad * pad, GstBuffer * in); static GstBuffer *gst_mimenc_create_tcp_header (GstMimEnc * mimenc, - guint32 payload_size, guint32 timestamp); + guint32 payload_size, GstClockTime timestamp, gboolean keyframe, + gboolean paused); +static gboolean gst_mimenc_event (GstPad * pad, GstEvent * event); static GstStateChangeReturn gst_mimenc_change_state (GstElement * element, GstStateChange transition); +static void gst_mimenc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_mimenc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); GST_BOILERPLATE (GstMimEnc, gst_mimenc, GstElement, GST_TYPE_ELEMENT); @@ -93,6 +108,16 @@ gst_mimenc_base_init (gpointer klass) "Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>" }; GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gst_mimenc_set_property; + gobject_class->get_property = gst_mimenc_get_property; + + g_object_class_install_property (gobject_class, PROP_PAUSED_MODE, + g_param_spec_boolean ("paused-mode", "Paused mode", + "If enabled, empty frames will be generated every 4 seconds" + " when no data is received", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_factory)); @@ -123,6 +148,7 @@ gst_mimenc_init (GstMimEnc * mimenc, GstMimEncClass * klass) gst_element_add_pad (GST_ELEMENT (mimenc), mimenc->sinkpad); gst_pad_set_setcaps_function (mimenc->sinkpad, gst_mimenc_setcaps); gst_pad_set_chain_function (mimenc->sinkpad, gst_mimenc_chain); + gst_pad_set_event_function (mimenc->sinkpad, gst_mimenc_event); mimenc->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&src_factory), @@ -131,6 +157,8 @@ gst_mimenc_init (GstMimEnc * mimenc, GstMimEncClass * klass) mimenc->enc = NULL; + gst_segment_init (&mimenc->segment, GST_FORMAT_UNDEFINED); + // TODO property to set resolution mimenc->res = MIMIC_RES_HIGH; mimenc->buffer_size = -1; @@ -139,6 +167,42 @@ gst_mimenc_init (GstMimEnc * mimenc, GstMimEncClass * klass) mimenc->frames = 0; } +static void +gst_mimenc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstMimEnc *mimenc = GST_MIMENC (object); + + switch (prop_id) { + case PROP_PAUSED_MODE: + GST_OBJECT_LOCK (mimenc); + mimenc->paused_mode = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (mimenc); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mimenc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstMimEnc *mimenc = GST_MIMENC (object); + + switch (prop_id) { + case PROP_PAUSED_MODE: + GST_OBJECT_LOCK (mimenc); + g_value_set_boolean (value, mimenc->paused_mode); + GST_OBJECT_UNLOCK (mimenc); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static gboolean gst_mimenc_setcaps (GstPad * pad, GstCaps * caps) { @@ -196,6 +260,8 @@ gst_mimenc_chain (GstPad * pad, GstBuffer * in) gint buffer_size; GstBuffer *header = NULL; GstFlowReturn res = GST_FLOW_OK; + GstEvent *event = NULL; + gboolean keyframe; g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR); mimenc = GST_MIMENC (gst_pad_get_parent (pad)); @@ -204,6 +270,14 @@ gst_mimenc_chain (GstPad * pad, GstBuffer * in) GST_OBJECT_LOCK (mimenc); + if (mimenc->segment.format == GST_FORMAT_UNDEFINED) { + GST_WARNING_OBJECT (mimenc, "No new-segment received," + " initializing segment with time 0..-1"); + gst_segment_init (&mimenc->segment, GST_FORMAT_TIME); + gst_segment_set_newsegment (&mimenc->segment, + FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0); + } + if (mimenc->enc == NULL) { mimenc->enc = mimic_open (); if (mimenc->enc == NULL) { @@ -233,11 +307,14 @@ gst_mimenc_chain (GstPad * pad, GstBuffer * in) data = GST_BUFFER_DATA (buf); out_buf = gst_buffer_new_and_alloc (mimenc->buffer_size); - GST_BUFFER_TIMESTAMP (out_buf) = GST_BUFFER_TIMESTAMP (buf); + GST_BUFFER_TIMESTAMP (out_buf) = + gst_segment_to_running_time (&mimenc->segment, GST_FORMAT_TIME, + GST_BUFFER_TIMESTAMP (buf)); + mimenc->last_buffer = GST_BUFFER_TIMESTAMP (out_buf); buffer_size = mimenc->buffer_size; + keyframe = (mimenc->frames % MAX_INTERFRAMES) == 0 ? TRUE : FALSE; if (!mimic_encode_frame (mimenc->enc, data, GST_BUFFER_DATA (out_buf), - &buffer_size, - ((mimenc->frames % MAX_INTERFRAMES) == 0 ? TRUE : FALSE))) { + &buffer_size, keyframe)) { GST_WARNING_OBJECT (mimenc, "mimic_encode_frame error\n"); gst_buffer_unref (out_buf); gst_buffer_unref (buf); @@ -252,23 +329,40 @@ gst_mimenc_chain (GstPad * pad, GstBuffer * in) // now let's create that tcp header header = gst_mimenc_create_tcp_header (mimenc, buffer_size, - GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND); + GST_BUFFER_TIMESTAMP (out_buf), keyframe, FALSE); + + if (!header) { + gst_buffer_unref (out_buf); + GST_DEBUG_OBJECT (mimenc, "header not created succesfully"); + res = GST_FLOW_ERROR; + goto out_unlock; + } + + if (mimenc->need_newsegment) { + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0); + mimenc->need_newsegment = FALSE; + } GST_OBJECT_UNLOCK (mimenc); - if (header) { - res = gst_pad_push (mimenc->srcpad, header); - if (res != GST_FLOW_OK) { + if (event) { + if (!gst_pad_push_event (mimenc->srcpad, event)) { + res = GST_FLOW_ERROR; + GST_ERROR_OBJECT (mimenc, "Failed to push NEWSEGMENT event"); + gst_buffer_unref (header); gst_buffer_unref (out_buf); goto out; } + } - res = gst_pad_push (mimenc->srcpad, out_buf); - } else { - GST_DEBUG_OBJECT (mimenc, "header not created succesfully"); - res = GST_FLOW_ERROR; + res = gst_pad_push (mimenc->srcpad, header); + if (res != GST_FLOW_OK) { + gst_buffer_unref (out_buf); + goto out; } + res = gst_pad_push (mimenc->srcpad, out_buf); + out: if (buf) gst_buffer_unref (buf); @@ -284,34 +378,191 @@ out_unlock: static GstBuffer * gst_mimenc_create_tcp_header (GstMimEnc * mimenc, guint32 payload_size, - guint32 timestamp) + GstClockTime timestamp, gboolean keyframe, gboolean paused) { // 24 bytes GstBuffer *buf_header = gst_buffer_new_and_alloc (24); guchar *p = (guchar *) GST_BUFFER_DATA (buf_header); + GST_BUFFER_TIMESTAMP (buf_header) = timestamp; + p[0] = 24; - *((guchar *) (p + 1)) = 0; + *((guchar *) (p + 1)) = paused ? 1 : 0; *((guint16 *) (p + 2)) = GUINT16_TO_LE (mimenc->width); *((guint16 *) (p + 4)) = GUINT16_TO_LE (mimenc->height); - *((guint16 *) (p + 6)) = 0; + *((guint16 *) (p + 6)) = keyframe ? 1 : 0; *((guint32 *) (p + 8)) = GUINT32_TO_LE (payload_size); *((guint32 *) (p + 12)) = GUINT32_TO_LE (GST_MAKE_FOURCC ('M', 'L', '2', '0')); *((guint32 *) (p + 16)) = 0; - *((guint32 *) (p + 20)) = timestamp; + *((guint32 *) (p + 20)) = timestamp / GST_MSECOND; return buf_header; } +static gboolean +gst_mimenc_event (GstPad * pad, GstEvent * event) +{ + GstMimEnc *mimenc = GST_MIMENC (gst_pad_get_parent (pad)); + gboolean ret = TRUE; + gboolean forward = TRUE; + + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + gboolean update; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, + &format, &start, &stop, &time); + + /* we need time for now */ + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + GST_DEBUG_OBJECT (mimenc, + "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT + ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, + update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), + GST_TIME_ARGS (time)); + + /* now configure the values, we need these to time the release of the + * buffers on the srcpad. */ + GST_OBJECT_LOCK (mimenc); + gst_segment_set_newsegment_full (&mimenc->segment, update, + rate, arate, format, start, stop, time); + GST_OBJECT_UNLOCK (mimenc); + forward = FALSE; + break; + + } + break; + case GST_EVENT_FLUSH_STOP: + GST_OBJECT_LOCK (mimenc); + gst_segment_init (&mimenc->segment, GST_FORMAT_UNDEFINED); + mimenc->need_newsegment = TRUE; + GST_OBJECT_UNLOCK (mimenc); + break; + default: + break; + } + + if (forward) + ret = gst_pad_push_event (mimenc->srcpad, event); + else + gst_event_unref (event); + +done: + gst_object_unref (mimenc); + + return ret; + +newseg_wrong_format: + { + GST_DEBUG_OBJECT (mimenc, "received non TIME newsegment"); + gst_event_unref (event); + ret = FALSE; + goto done; + } +} + +static void +paused_mode_task (gpointer data) +{ + GstMimEnc *mimenc = GST_MIMENC (data); + GstClockTime now; + GstClockTimeDiff diff; + GstFlowReturn ret; + + if (!GST_ELEMENT_CLOCK (mimenc)) { + GST_ERROR_OBJECT (mimenc, "Element has no clock"); + gst_pad_pause_task (mimenc->srcpad); + return; + } + + GST_OBJECT_LOCK (mimenc); + + if (mimenc->stop_paused_mode) { + GST_OBJECT_UNLOCK (mimenc); + goto stop_task; + } + + now = gst_clock_get_time (GST_ELEMENT_CLOCK (mimenc)); + + diff = now - GST_ELEMENT_CAST (mimenc)->base_time - mimenc->last_buffer; + if (diff < 0) + diff = 0; + + if (diff > 3.95 * GST_SECOND) { + GstBuffer *buffer = gst_mimenc_create_tcp_header (mimenc, 0, + mimenc->last_buffer + 4 * GST_SECOND, FALSE, TRUE); + GstEvent *event = NULL; + + mimenc->last_buffer += 4 * GST_SECOND; + + if (mimenc->need_newsegment) { + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0); + mimenc->need_newsegment = FALSE; + } + + GST_OBJECT_UNLOCK (mimenc); + GST_LOG_OBJECT (mimenc, "Haven't had an incoming buffer in 4 seconds," + " sending out a pause frame"); + + if (event) { + if (!gst_pad_push_event (mimenc->srcpad, event)) { + GST_ERROR_OBJECT (mimenc, "Failed to push NEWSEGMENT event"); + gst_buffer_unref (buffer); + goto stop_task; + } + } + ret = gst_pad_push (mimenc->srcpad, buffer); + if (ret < 0) { + GST_WARNING_OBJECT (mimenc, "Error pushing paused header: %s", + gst_flow_get_name (ret)); + goto stop_task; + } + } else { + GstClockTime next_stop; + GstClockID id; + + next_stop = now + (4 * GST_SECOND - MIN (diff, 4 * GST_SECOND)); + + id = gst_clock_new_single_shot_id (GST_ELEMENT_CLOCK (mimenc), next_stop); + + if (mimenc->stop_paused_mode) { + GST_OBJECT_UNLOCK (mimenc); + goto stop_task; + } + + mimenc->clock_id = id; + GST_OBJECT_UNLOCK (mimenc); + + gst_clock_id_wait (id, NULL); + + GST_OBJECT_LOCK (mimenc); + mimenc->clock_id = NULL; + GST_OBJECT_UNLOCK (mimenc); + } + return; + +stop_task: + + gst_pad_pause_task (mimenc->srcpad); +} + static GstStateChangeReturn gst_mimenc_change_state (GstElement * element, GstStateChange transition) { - GstMimEnc *mimenc; + GstMimEnc *mimenc = GST_MIMENC (element); + GstStateChangeReturn ret; + gboolean paused_mode; switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: - mimenc = GST_MIMENC (element); GST_OBJECT_LOCK (element); if (mimenc->enc != NULL) { mimic_close (mimenc->enc); @@ -322,9 +573,51 @@ gst_mimenc_change_state (GstElement * element, GstStateChange transition) GST_OBJECT_UNLOCK (element); break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_OBJECT_LOCK (mimenc); + gst_segment_init (&mimenc->segment, GST_FORMAT_UNDEFINED); + mimenc->last_buffer = GST_CLOCK_TIME_NONE; + mimenc->need_newsegment = TRUE; + GST_OBJECT_UNLOCK (mimenc); + break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + GST_OBJECT_LOCK (mimenc); + if (mimenc->clock_id) + gst_clock_id_unschedule (mimenc->clock_id); + mimenc->stop_paused_mode = TRUE; + GST_OBJECT_UNLOCK (mimenc); + + gst_pad_pause_task (mimenc->srcpad); + + break; default: break; } - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + GST_OBJECT_LOCK (mimenc); + mimenc->stop_paused_mode = FALSE; + if (mimenc->last_buffer == GST_CLOCK_TIME_NONE) + mimenc->last_buffer = gst_clock_get_time (GST_ELEMENT_CLOCK (mimenc)) + - GST_ELEMENT_CAST (mimenc)->base_time; + paused_mode = mimenc->paused_mode; + GST_OBJECT_UNLOCK (mimenc); + if (paused_mode) { + if (!gst_pad_start_task (mimenc->srcpad, paused_mode_task, mimenc)) { + ret = GST_STATE_CHANGE_FAILURE; + GST_ERROR_OBJECT (mimenc, "Can not start task"); + } + } + break; + default: + break; + } + + return ret; } diff --git a/ext/mimic/gstmimenc.h b/ext/mimic/gstmimenc.h index c21d714f..89cff46e 100644 --- a/ext/mimic/gstmimenc.h +++ b/ext/mimic/gstmimenc.h @@ -54,6 +54,13 @@ struct _GstMimEnc gint buffer_size; guint32 frames; guint16 height, width; + + gboolean paused_mode; + GstSegment segment; + gboolean need_newsegment; + GstClockTime last_buffer; + GstClockID clock_id; + gboolean stop_paused_mode; }; struct _GstMimEncClass |