From 4f975243760c9c69bf3a30993f9234c12c81e151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 11 Jul 2011 21:30:28 -0400 Subject: [PATCH 01/85] rtpdtmfsrc: Just error out if there is no clock --- gst/dtmf/gstrtpdtmfsrc.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index c7e1c1fb2a..6b2f35031a 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -502,29 +502,27 @@ gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, } } -static void +static gboolean gst_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc * dtmfsrc) { GstClock *clock; clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); - if (clock != NULL) { - dtmfsrc->timestamp = gst_clock_get_time (clock) - + (MIN_INTER_DIGIT_INTERVAL * GST_MSECOND) - - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); - dtmfsrc->start_timestamp = dtmfsrc->timestamp; - gst_object_unref (clock); - } else { - gchar *dtmf_name = gst_element_get_name (dtmfsrc); - GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name); - dtmfsrc->timestamp = GST_CLOCK_TIME_NONE; - g_free (dtmf_name); - } + if (clock == NULL) + return FALSE; + + dtmfsrc->timestamp = gst_clock_get_time (clock) + + (MIN_INTER_DIGIT_INTERVAL * GST_MSECOND) + - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); + dtmfsrc->start_timestamp = dtmfsrc->timestamp; + gst_object_unref (clock); dtmfsrc->rtp_timestamp = dtmfsrc->ts_base + gst_util_uint64_scale_int (gst_segment_to_running_time (&GST_BASE_SRC (dtmfsrc)->segment, GST_FORMAT_TIME, dtmfsrc->timestamp), dtmfsrc->clock_rate, GST_SECOND); + + return TRUE; } @@ -660,7 +658,8 @@ gst_rtp_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset, dtmfsrc->last_packet = FALSE; /* Set the redundancy on the first packet */ dtmfsrc->redundancy_count = dtmfsrc->packet_redundancy; - gst_rtp_dtmf_prepare_timestamps (dtmfsrc); + if (!gst_rtp_dtmf_prepare_timestamps (dtmfsrc)) + goto no_clock; dtmfsrc->payload = event->payload; event->payload = NULL; @@ -727,6 +726,8 @@ gst_rtp_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset, GST_DEBUG_OBJECT (dtmfsrc, "Processed events, now lets wait on the clock"); clock = gst_element_get_clock (GST_ELEMENT (basesrc)); + if (!clock) + goto no_clock; clockid = gst_clock_new_single_shot_id (clock, dtmfsrc->timestamp + gst_element_get_base_time (GST_ELEMENT (dtmfsrc))); gst_object_unref (clock); @@ -793,6 +794,12 @@ paused: } else { return GST_FLOW_WRONG_STATE; } + +no_clock: + GST_ELEMENT_ERROR (dtmfsrc, STREAM, MUX, ("No available clock"), + ("No available clock")); + gst_pad_pause_task (GST_BASE_SRC_PAD (dtmfsrc)); + return GST_FLOW_ERROR; } From 38aaf7cbabd3358b32db191993b3c30cc4ee8620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 14 Apr 2011 16:49:39 -0400 Subject: [PATCH 02/85] rtpdtmfsrc: Respect ptime from the caps Respect the ptime from the caps for the DTMF packets --- gst/dtmf/gstrtpdtmfsrc.c | 48 ++++++++++++++++++---------------------- gst/dtmf/gstrtpdtmfsrc.h | 2 +- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index 6b2f35031a..a063e775a8 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -122,9 +122,7 @@ #include "gstrtpdtmfsrc.h" #define GST_RTP_DTMF_TYPE_EVENT 1 -#define DEFAULT_PACKET_INTERVAL 50 /* ms */ -#define MIN_PACKET_INTERVAL 10 /* ms */ -#define MAX_PACKET_INTERVAL 50 /* ms */ +#define DEFAULT_PTIME 40 /* ms */ #define DEFAULT_SSRC -1 #define DEFAULT_PT 96 #define DEFAULT_TIMESTAMP_OFFSET -1 @@ -155,7 +153,6 @@ enum PROP_CLOCK_RATE, PROP_TIMESTAMP, PROP_SEQNUM, - PROP_INTERVAL, PROP_REDUNDANCY }; @@ -264,11 +261,6 @@ gst_rtp_dtmf_src_class_init (GstRTPDTMFSrcClass * klass) g_param_spec_uint ("pt", "payload type", "The payload type of the packets", 0, 0x80, DEFAULT_PT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INTERVAL, - g_param_spec_uint ("interval", "Interval between rtp packets", - "Interval in ms between two rtp packets", MIN_PACKET_INTERVAL, - MAX_PACKET_INTERVAL, DEFAULT_PACKET_INTERVAL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_REDUNDANCY, g_param_spec_uint ("packet-redundancy", "Packet Redundancy", "Number of packets to send to indicate start and stop dtmf events", @@ -309,7 +301,7 @@ gst_rtp_dtmf_src_init (GstRTPDTMFSrc * object, GstRTPDTMFSrcClass * g_class) object->ts_offset = DEFAULT_TIMESTAMP_OFFSET; object->pt = DEFAULT_PT; object->clock_rate = DEFAULT_CLOCK_RATE; - object->interval = DEFAULT_PACKET_INTERVAL; + object->ptime = DEFAULT_PTIME; object->packet_redundancy = DEFAULT_PACKET_REDUNDANCY; object->event_queue = @@ -448,9 +440,6 @@ gst_rtp_dtmf_src_set_property (GObject * object, guint prop_id, dtmfsrc->pt = g_value_get_uint (value); dtmfsrc->dirty = TRUE; break; - case PROP_INTERVAL: - dtmfsrc->interval = g_value_get_uint (value); - break; case PROP_REDUNDANCY: dtmfsrc->packet_redundancy = g_value_get_uint (value); break; @@ -490,9 +479,6 @@ gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, case PROP_SEQNUM: g_value_set_uint (value, dtmfsrc->seqnum); break; - case PROP_INTERVAL: - g_value_set_uint (value, dtmfsrc->interval); - break; case PROP_REDUNDANCY: g_value_set_uint (value, dtmfsrc->packet_redundancy); break; @@ -537,7 +523,7 @@ gst_rtp_dtmf_src_add_start_event (GstRTPDTMFSrc * dtmfsrc, gint event_number, event->payload = g_slice_new0 (GstRTPDTMFPayload); event->payload->event = CLAMP (event_number, MIN_EVENT, MAX_EVENT); event->payload->volume = CLAMP (event_volume, MIN_VOLUME, MAX_VOLUME); - event->payload->duration = dtmfsrc->interval * dtmfsrc->clock_rate / 1000; + event->payload->duration = dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; g_async_queue_push (dtmfsrc->event_queue, event); } @@ -584,7 +570,7 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) if (dtmfsrc->redundancy_count > 1) GST_BUFFER_DURATION (buf) = 0; else - GST_BUFFER_DURATION (buf) = dtmfsrc->interval * GST_MSECOND; + GST_BUFFER_DURATION (buf) = dtmfsrc->ptime * GST_MSECOND; GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp; dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); @@ -593,12 +579,6 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) /* copy payload and convert to network-byte order */ g_memmove (payload, dtmfsrc->payload, sizeof (GstRTPDTMFPayload)); - /* Force the packet duration to a certain minumum - * if its the end of the event - */ - if (payload->e && - payload->duration < MIN_PULSE_DURATION * dtmfsrc->clock_rate / 1000) - payload->duration = MIN_PULSE_DURATION * dtmfsrc->clock_rate / 1000; payload->duration = g_htons (payload->duration); @@ -606,8 +586,7 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) /* duration of DTMF payloadfor the NEXT packet */ /* not updated for redundant packets */ if (dtmfsrc->redundancy_count == 0) - dtmfsrc->payload->duration += - dtmfsrc->interval * dtmfsrc->clock_rate / 1000; + dtmfsrc->payload->duration += dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; } @@ -920,6 +899,23 @@ gst_rtp_dtmf_src_negotiate (GstBaseSrc * basesrc) GST_LOG_OBJECT (dtmfsrc, "using internal seqnum-base %u", dtmfsrc->seqnum_base); } + + if (gst_structure_has_field_typed (s, "ptime", G_TYPE_UINT)) { + value = gst_structure_get_value (s, "ptime"); + dtmfsrc->ptime = g_value_get_uint (value); + GST_LOG_OBJECT (dtmfsrc, "using peer ptime %u", dtmfsrc->ptime); + } else if (gst_structure_has_field_typed (s, "maxptime", G_TYPE_UINT)) { + value = gst_structure_get_value (s, "maxptime"); + dtmfsrc->ptime = g_value_get_uint (value); + GST_LOG_OBJECT (dtmfsrc, "using peer maxptime as ptime %u", + dtmfsrc->ptime); + } else { + /* FIXME, fixate_nearest_uint would be even better */ + gst_structure_set (s, "ptime", G_TYPE_UINT, dtmfsrc->ptime, NULL); + GST_LOG_OBJECT (dtmfsrc, "using internal ptime %u", dtmfsrc->ptime); + } + + GST_DEBUG_OBJECT (dtmfsrc, "with peer caps: %" GST_PTR_FORMAT, srccaps); } diff --git a/gst/dtmf/gstrtpdtmfsrc.h b/gst/dtmf/gstrtpdtmfsrc.h index d04c6ecb7c..61cda5c119 100644 --- a/gst/dtmf/gstrtpdtmfsrc.h +++ b/gst/dtmf/gstrtpdtmfsrc.h @@ -90,7 +90,7 @@ struct _GstRTPDTMFSrc guint pt; guint ssrc; guint current_ssrc; - guint16 interval; + guint16 ptime; guint16 packet_redundancy; guint32 clock_rate; From 859b8ebfc95d54a0011ab44af4d5da283a7d3651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 14 Apr 2011 14:34:26 -0400 Subject: [PATCH 03/85] rtpdtmfmux: Add last-stop to dtmf-event upstream events Add the running time of the last outputted buffer to the upstream "dtmf-event" events so that the dtmf source does not leave a gap. --- gst/rtpmux/gstrtpdtmfmux.c | 25 +++++++++++++ gst/rtpmux/gstrtpmux.c | 72 +++++++++++++++++++++++++++++++------- gst/rtpmux/gstrtpmux.h | 6 +++- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/gst/rtpmux/gstrtpdtmfmux.c b/gst/rtpmux/gstrtpdtmfmux.c index f62c6263db..97ffacd2f0 100644 --- a/gst/rtpmux/gstrtpdtmfmux.c +++ b/gst/rtpmux/gstrtpdtmfmux.c @@ -64,6 +64,8 @@ static GstStateChangeReturn gst_rtp_dtmf_mux_change_state (GstElement * element, static gboolean gst_rtp_dtmf_mux_accept_buffer_locked (GstRTPMux * rtp_mux, GstRTPMuxPadPrivate * padpriv, GstBuffer * buffer); +static gboolean gst_rtp_dtmf_mux_src_event (GstRTPMux * rtp_mux, + GstEvent * event); GST_BOILERPLATE (GstRTPDTMFMux, gst_rtp_dtmf_mux, GstRTPMux, GST_TYPE_RTP_MUX); @@ -100,6 +102,7 @@ gst_rtp_dtmf_mux_class_init (GstRTPDTMFMuxClass * klass) gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_change_state); gstrtpmux_class->accept_buffer_locked = gst_rtp_dtmf_mux_accept_buffer_locked; + gstrtpmux_class->src_event = gst_rtp_dtmf_mux_src_event; } static gboolean @@ -173,6 +176,28 @@ gst_rtp_dtmf_mux_request_new_pad (GstElement * element, GstPadTemplate * templ, return pad; } +static gboolean +gst_rtp_dtmf_mux_src_event (GstRTPMux * rtp_mux, GstEvent * event) +{ + if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) { + const GstStructure *s = gst_event_get_structure (event); + + if (s && gst_structure_has_name (s, "dtmf-event")) { + GST_OBJECT_LOCK (rtp_mux); + if (GST_CLOCK_TIME_IS_VALID (rtp_mux->last_stop)) { + event = (GstEvent *) + gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (event)); + s = gst_event_get_structure (event); + gst_structure_set ((GstStructure *) s, + "last-stop", G_TYPE_UINT64, rtp_mux->last_stop, NULL); + } + GST_OBJECT_UNLOCK (rtp_mux); + } + } + + return GST_RTP_MUX_CLASS (parent_class)->src_event (rtp_mux, event); +} + static GstStateChangeReturn gst_rtp_dtmf_mux_change_state (GstElement * element, GstStateChange transition) diff --git a/gst/rtpmux/gstrtpmux.c b/gst/rtpmux/gstrtpmux.c index 57929ec608..f86fd5d6bc 100644 --- a/gst/rtpmux/gstrtpmux.c +++ b/gst/rtpmux/gstrtpmux.c @@ -107,6 +107,9 @@ static void gst_rtp_mux_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_rtp_mux_dispose (GObject * object); +static gboolean gst_rtp_mux_src_event_real (GstRTPMux *rtp_mux, + GstEvent * event); + GST_BOILERPLATE (GstRTPMux, gst_rtp_mux, GstElement, GST_TYPE_ELEMENT); static void @@ -137,6 +140,8 @@ gst_rtp_mux_class_init (GstRTPMuxClass * klass) gobject_class->set_property = gst_rtp_mux_set_property; gobject_class->dispose = gst_rtp_mux_dispose; + klass->src_event = gst_rtp_mux_src_event_real; + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMESTAMP_OFFSET, g_param_spec_int ("timestamp-offset", "Timestamp Offset", @@ -183,16 +188,30 @@ restart: static gboolean gst_rtp_mux_src_event (GstPad * pad, GstEvent * event) { - GstElement *rtp_mux; + GstRTPMux *rtp_mux; + GstRTPMuxClass *klass; + gboolean ret = FALSE; + + rtp_mux = (GstRTPMux *) gst_pad_get_parent_element (pad); + g_return_val_if_fail (rtp_mux != NULL, FALSE); + klass = GST_RTP_MUX_GET_CLASS (rtp_mux); + + ret = klass->src_event (rtp_mux, event); + + gst_object_unref (rtp_mux); + + return ret; +} + +static gboolean +gst_rtp_mux_src_event_real (GstRTPMux *rtp_mux, GstEvent * event) +{ GstIterator *iter; GstPad *sinkpad; gboolean result = FALSE; gboolean done = FALSE; - rtp_mux = gst_pad_get_parent_element (pad); - g_return_val_if_fail (rtp_mux != NULL, FALSE); - - iter = gst_element_iterate_sink_pads (rtp_mux); + iter = gst_element_iterate_sink_pads (GST_ELEMENT (rtp_mux)); while (!done) { switch (gst_iterator_next (iter, (gpointer) & sinkpad)) { @@ -213,7 +232,6 @@ gst_rtp_mux_src_event (GstPad * pad, GstEvent * event) } } gst_iterator_free (iter); - gst_object_unref (rtp_mux); gst_event_unref (event); return result; @@ -236,6 +254,7 @@ gst_rtp_mux_init (GstRTPMux * object, GstRTPMuxClass * g_class) object->seqnum_offset = DEFAULT_SEQNUM_OFFSET; object->segment_pending = TRUE; + object->last_stop = GST_CLOCK_TIME_NONE; } static void @@ -394,6 +413,20 @@ gst_rtp_mux_chain_list (GstPad * pad, GstBufferList * bufferlist) break; gst_buffer_list_iterator_take (it, rtpbuf); + + do { + if (GST_BUFFER_DURATION_IS_VALID (rtpbuf) && + GST_BUFFER_TIMESTAMP_IS_VALID (rtpbuf)) + rtp_mux->last_stop = GST_BUFFER_TIMESTAMP (rtpbuf) + + GST_BUFFER_DURATION (rtpbuf); + else + rtp_mux->last_stop = GST_CLOCK_TIME_NONE; + + gst_buffer_list_iterator_take (it, rtpbuf); + + } while ((rtpbuf = gst_buffer_list_iterator_next (it)) != NULL); + + } gst_buffer_list_iterator_free (it); @@ -456,15 +489,25 @@ gst_rtp_mux_chain (GstPad * pad, GstBuffer * buffer) drop = !process_buffer_locked (rtp_mux, padpriv, buffer); - if (!drop && rtp_mux->segment_pending) { - /* - * We set the start at 0, because we re-timestamps to the running time - */ - newseg_event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, - GST_FORMAT_TIME, 0, -1, 0); + if (!drop) { + if (rtp_mux->segment_pending) { + /* + * We set the start at 0, because we re-timestamps to the running time + */ + newseg_event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, + GST_FORMAT_TIME, 0, -1, 0); - rtp_mux->segment_pending = FALSE; + rtp_mux->segment_pending = FALSE; + } + + if (GST_BUFFER_DURATION_IS_VALID (buffer) && + GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + rtp_mux->last_stop = GST_BUFFER_TIMESTAMP (buffer) + + GST_BUFFER_DURATION (buffer); + else + rtp_mux->last_stop = GST_CLOCK_TIME_NONE; } + GST_OBJECT_UNLOCK (rtp_mux); if (newseg_event) @@ -709,6 +752,7 @@ gst_rtp_mux_sink_event (GstPad * pad, GstEvent * event) GstRTPMuxPadPrivate *padpriv; GST_OBJECT_LOCK (mux); + mux->last_stop = GST_CLOCK_TIME_NONE; mux->segment_pending = TRUE; padpriv = gst_pad_get_element_private (pad); if (padpriv) @@ -801,6 +845,8 @@ gst_rtp_mux_ready_to_paused (GstRTPMux * rtp_mux) else rtp_mux->ts_base = rtp_mux->ts_offset; + rtp_mux->last_stop = GST_CLOCK_TIME_NONE; + GST_DEBUG_OBJECT (rtp_mux, "set clock-base to %u", rtp_mux->ts_base); GST_OBJECT_UNLOCK (rtp_mux); diff --git a/gst/rtpmux/gstrtpmux.h b/gst/rtpmux/gstrtpmux.h index 96513836d5..7bfea60c0b 100644 --- a/gst/rtpmux/gstrtpmux.h +++ b/gst/rtpmux/gstrtpmux.h @@ -31,7 +31,7 @@ G_BEGIN_DECLS #define GST_TYPE_RTP_MUX (gst_rtp_mux_get_type()) #define GST_RTP_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MUX, GstRTPMux)) -#define GST_RTP_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MUX, GstRTPMux)) +#define GST_RTP_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MUX, GstRTPMuxClass)) #define GST_RTP_MUX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTP_MUX, GstRTPMuxClass)) #define GST_IS_RTP_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MUX)) #define GST_IS_RTP_MUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MUX)) @@ -74,6 +74,8 @@ struct _GstRTPMux guint current_ssrc; gboolean segment_pending; + + GstClockTime last_stop; }; struct _GstRTPMuxClass @@ -82,6 +84,8 @@ struct _GstRTPMuxClass gboolean (*accept_buffer_locked) (GstRTPMux *rtp_mux, GstRTPMuxPadPrivate * padpriv, GstBuffer * buffer); + + gboolean (*src_event) (GstRTPMux *rtp_mux, GstEvent *event); }; From 7837cc25fb537a225531a38d7ea484cdb58a12e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 14 Apr 2011 17:08:57 -0400 Subject: [PATCH 04/85] rtpdtmfsrc: Start at the last_stop from the start event if there was one The goal is to try to not have a GAP between the audio and the DTMF --- gst/dtmf/gstrtpdtmfsrc.c | 36 +++++++++++++++++++++++++++--------- gst/dtmf/gstrtpdtmfsrc.h | 2 ++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index a063e775a8..8d37f3195b 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -334,6 +334,7 @@ gst_rtp_dtmf_src_handle_dtmf_event (GstRTPDTMFSrc * dtmfsrc, gint event_type; gboolean start; gint method; + GstClockTime last_stop; if (!gst_structure_get_int (event_structure, "type", &event_type) || !gst_structure_get_boolean (event_structure, "start", &start) || @@ -346,6 +347,13 @@ gst_rtp_dtmf_src_handle_dtmf_event (GstRTPDTMFSrc * dtmfsrc, } } + GST_OBJECT_LOCK (dtmfsrc); + if (gst_structure_get_clock_time (event_structure, "last-stop", &last_stop)) + dtmfsrc->last_stop = last_stop; + else + dtmfsrc->last_stop = GST_CLOCK_TIME_NONE; + GST_OBJECT_UNLOCK (dtmfsrc); + if (start) { gint event_number; gint event_volume; @@ -491,17 +499,27 @@ gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, static gboolean gst_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc * dtmfsrc) { - GstClock *clock; + GstClockTime last_stop; - clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); - if (clock == NULL) - return FALSE; + GST_OBJECT_LOCK (dtmfsrc); + last_stop = dtmfsrc->last_stop; + GST_OBJECT_UNLOCK (dtmfsrc); - dtmfsrc->timestamp = gst_clock_get_time (clock) - + (MIN_INTER_DIGIT_INTERVAL * GST_MSECOND) - - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); - dtmfsrc->start_timestamp = dtmfsrc->timestamp; - gst_object_unref (clock); + if (GST_CLOCK_TIME_IS_VALID (last_stop)) { + dtmfsrc->timestamp = last_stop; + dtmfsrc->start_timestamp = last_stop; + } else { + GstClock *clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); + + if (clock == NULL) + return FALSE; + + dtmfsrc->timestamp = gst_clock_get_time (clock) + + (MIN_INTER_DIGIT_INTERVAL * GST_MSECOND) + - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); + dtmfsrc->start_timestamp = dtmfsrc->timestamp; + gst_object_unref (clock); + } dtmfsrc->rtp_timestamp = dtmfsrc->ts_base + gst_util_uint64_scale_int (gst_segment_to_running_time (&GST_BASE_SRC diff --git a/gst/dtmf/gstrtpdtmfsrc.h b/gst/dtmf/gstrtpdtmfsrc.h index 61cda5c119..9be9df69cb 100644 --- a/gst/dtmf/gstrtpdtmfsrc.h +++ b/gst/dtmf/gstrtpdtmfsrc.h @@ -94,6 +94,8 @@ struct _GstRTPDTMFSrc guint16 packet_redundancy; guint32 clock_rate; + GstClockTime last_stop; + gboolean dirty; guint16 redundancy_count; }; From e6c431a533877996d376ef5c49abb76e365fab2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 11 Jul 2011 20:46:20 -0400 Subject: [PATCH 05/85] rtpdtmfsrc: Put the inter digit interval at the end, not at the start The reason is to let rtpdtmfmux drop buffers during the inter digit interval, this way, there will be more silence around the DTMF tones so IVFs will have a better chance recognizing them. --- gst/dtmf/gstrtpdtmfsrc.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index 8d37f3195b..19acc6cae6 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -514,8 +514,7 @@ gst_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc * dtmfsrc) if (clock == NULL) return FALSE; - dtmfsrc->timestamp = gst_clock_get_time (clock) - + (MIN_INTER_DIGIT_INTERVAL * GST_MSECOND) + dtmfsrc->start_timestamp = gst_clock_get_time (clock) - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); dtmfsrc->start_timestamp = dtmfsrc->timestamp; gst_object_unref (clock); @@ -591,7 +590,6 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) GST_BUFFER_DURATION (buf) = dtmfsrc->ptime * GST_MSECOND; GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp; - dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); payload = (GstRTPDTMFPayload *) gst_rtp_buffer_get_payload (buf); @@ -606,6 +604,18 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) if (dtmfsrc->redundancy_count == 0) dtmfsrc->payload->duration += dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; + if (dtmfsrc->redundancy_count == 0 && dtmfsrc->last_packet) { + GstClockTime inter_digit_interval = MIN_INTER_DIGIT_INTERVAL; + + if (inter_digit_interval % dtmfsrc->ptime != 0) + inter_digit_interval += dtmfsrc->ptime - + (MIN_INTER_DIGIT_INTERVAL % dtmfsrc->ptime); + + GST_BUFFER_DURATION (buf) += inter_digit_interval * GST_MSECOND; + } + + if (GST_CLOCK_TIME_IS_VALID (dtmfsrc->timestamp)) + dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); } static GstBuffer * From 7c55ff3023c576c79e88dc2995fc011c927744d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 11 Jul 2011 20:47:23 -0400 Subject: [PATCH 06/85] rtpdtmfsrc: Make sure rtpdtmfsrc timestamps don't overlap --- gst/dtmf/gstrtpdtmfsrc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index 19acc6cae6..90e63fc6e1 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -506,7 +506,6 @@ gst_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc * dtmfsrc) GST_OBJECT_UNLOCK (dtmfsrc); if (GST_CLOCK_TIME_IS_VALID (last_stop)) { - dtmfsrc->timestamp = last_stop; dtmfsrc->start_timestamp = last_stop; } else { GstClock *clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); @@ -516,10 +515,15 @@ gst_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc * dtmfsrc) dtmfsrc->start_timestamp = gst_clock_get_time (clock) - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); - dtmfsrc->start_timestamp = dtmfsrc->timestamp; gst_object_unref (clock); } + /* If the last stop was in the past, then lets add the buffers together */ + if (dtmfsrc->start_timestamp < dtmfsrc->timestamp) + dtmfsrc->start_timestamp = dtmfsrc->timestamp; + + dtmfsrc->timestamp = dtmfsrc->start_timestamp; + dtmfsrc->rtp_timestamp = dtmfsrc->ts_base + gst_util_uint64_scale_int (gst_segment_to_running_time (&GST_BASE_SRC (dtmfsrc)->segment, GST_FORMAT_TIME, dtmfsrc->timestamp), @@ -976,6 +980,7 @@ gst_rtp_dtmf_src_ready_to_paused (GstRTPDTMFSrc * dtmfsrc) else dtmfsrc->ts_base = dtmfsrc->ts_offset; + dtmfsrc->timestamp = 0; } static GstStateChangeReturn From 2c7257481ad044c2bde230c31f91f92b24821826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 11 Jul 2011 21:31:07 -0400 Subject: [PATCH 07/85] rtpdtmfsrc: Correctly recognize the end of a buffer --- gst/dtmf/gstrtpdtmfsrc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index 90e63fc6e1..89e8c00270 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -605,10 +605,11 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) /* duration of DTMF payloadfor the NEXT packet */ /* not updated for redundant packets */ - if (dtmfsrc->redundancy_count == 0) + if (dtmfsrc->redundancy_count <= 1) dtmfsrc->payload->duration += dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; - if (dtmfsrc->redundancy_count == 0 && dtmfsrc->last_packet) { + + if (dtmfsrc->redundancy_count <= 1 && dtmfsrc->last_packet) { GstClockTime inter_digit_interval = MIN_INTER_DIGIT_INTERVAL; if (inter_digit_interval % dtmfsrc->ptime != 0) From e8268053c24164fd3512a19f0d40d98683f1ea6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 14 Apr 2011 15:46:08 -0400 Subject: [PATCH 08/85] dtmfsrc: Align DTMF sound buffers with last-stop from event Also make sure the timestamps never go backwards --- gst/dtmf/gstdtmfsrc.c | 48 ++++++++++++++++++++++++++++++++++--------- gst/dtmf/gstdtmfsrc.h | 2 ++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/gst/dtmf/gstdtmfsrc.c b/gst/dtmf/gstdtmfsrc.c index 79525b5729..eb55268d70 100644 --- a/gst/dtmf/gstdtmfsrc.c +++ b/gst/dtmf/gstdtmfsrc.c @@ -323,6 +323,7 @@ gst_dtmf_src_handle_dtmf_event (GstDTMFSrc * dtmfsrc, gint event_type; gboolean start; gint method; + GstClockTime last_stop; if (!gst_structure_get_int (event_structure, "type", &event_type) || !gst_structure_get_boolean (event_structure, "start", &start) || @@ -335,6 +336,14 @@ gst_dtmf_src_handle_dtmf_event (GstDTMFSrc * dtmfsrc, } } + + GST_OBJECT_LOCK (dtmfsrc); + if (gst_structure_get_clock_time (event_structure, "last-stop", &last_stop)) + dtmfsrc->last_stop = last_stop; + else + dtmfsrc->last_stop = GST_CLOCK_TIME_NONE; + GST_OBJECT_UNLOCK (dtmfsrc); + if (start) { gint event_number; gint event_volume; @@ -447,19 +456,37 @@ gst_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, static void gst_dtmf_prepare_timestamps (GstDTMFSrc * dtmfsrc) { - GstClock *clock; + GstClockTime last_stop; + GstClockTime timestamp; - clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); - if (clock != NULL) { - dtmfsrc->timestamp = gst_clock_get_time (clock) - - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); - gst_object_unref (clock); + GST_OBJECT_LOCK (dtmfsrc); + last_stop = dtmfsrc->last_stop; + GST_OBJECT_UNLOCK (dtmfsrc); + + if (GST_CLOCK_TIME_IS_VALID (last_stop)) { + timestamp = last_stop; } else { - gchar *dtmf_name = gst_element_get_name (dtmfsrc); - GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name); - dtmfsrc->timestamp = GST_CLOCK_TIME_NONE; - g_free (dtmf_name); + GstClock *clock; + + /* If there is no valid start time, lets use now as the start time */ + + clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); + if (clock != NULL) { + timestamp = gst_clock_get_time (clock) + - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); + gst_object_unref (clock); + } else { + gchar *dtmf_name = gst_element_get_name (dtmfsrc); + GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name); + dtmfsrc->timestamp = GST_CLOCK_TIME_NONE; + g_free (dtmf_name); + return; + } } + + /* Make sure the timestamp always goes forward */ + if (timestamp > dtmfsrc->timestamp) + dtmfsrc->timestamp = timestamp; } static void @@ -823,6 +850,7 @@ gst_dtmf_src_change_state (GstElement * element, GstStateChange transition) g_slice_free (GstDTMFSrcEvent, event); event = g_async_queue_try_pop (dtmfsrc->event_queue); } + dtmfsrc->timestamp = 0; no_preroll = TRUE; break; default: diff --git a/gst/dtmf/gstdtmfsrc.h b/gst/dtmf/gstdtmfsrc.h index aa5d35a611..cda5840a5f 100644 --- a/gst/dtmf/gstdtmfsrc.h +++ b/gst/dtmf/gstdtmfsrc.h @@ -81,6 +81,8 @@ struct _GstDTMFSrc gboolean paused; GstClockID clockid; + GstClockTime last_stop; + gint sample_rate; }; From 4167697c0e58432447c73288b603f9a856700885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 12 Jul 2011 19:09:02 -0400 Subject: [PATCH 09/85] dtmf: Max event type is 15 --- gst/dtmf/gstdtmfcommon.h | 4 ++-- gst/dtmf/gstdtmfsrc.c | 2 +- gst/dtmf/gstrtpdtmfsrc.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gst/dtmf/gstdtmfcommon.h b/gst/dtmf/gstdtmfcommon.h index aff881b987..82617d72fd 100644 --- a/gst/dtmf/gstdtmfcommon.h +++ b/gst/dtmf/gstdtmfcommon.h @@ -9,9 +9,9 @@ #define MAX_VOLUME 36 #define MIN_EVENT 0 -#define MAX_EVENT 16 +#define MAX_EVENT 15 #define MIN_EVENT_STRING "0" -#define MAX_EVENT_STRING "16" +#define MAX_EVENT_STRING "15" #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ diff --git a/gst/dtmf/gstdtmfsrc.c b/gst/dtmf/gstdtmfsrc.c index eb55268d70..a812e98ef0 100644 --- a/gst/dtmf/gstdtmfsrc.c +++ b/gst/dtmf/gstdtmfsrc.c @@ -65,7 +65,7 @@ * * number * G_TYPE_INT - * 0-16 + * 0-15 * The event number. * * diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index 89e8c00270..875a020661 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -63,7 +63,7 @@ * * number * G_TYPE_INT - * 0-16 + * 0-15 * The event number. * * From 1fdae5275330d7322f00e9d5c7dace30a41db477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 12 Jul 2011 21:48:37 -0400 Subject: [PATCH 10/85] dtmf: Add more debug --- gst/dtmf/gstdtmfsrc.c | 6 ++++++ gst/dtmf/gstrtpdtmfsrc.c | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/gst/dtmf/gstdtmfsrc.c b/gst/dtmf/gstdtmfsrc.c index a812e98ef0..7492b151e7 100644 --- a/gst/dtmf/gstdtmfsrc.c +++ b/gst/dtmf/gstdtmfsrc.c @@ -611,6 +611,12 @@ gst_dtmf_src_create_next_tone_packet (GstDTMFSrc * dtmfsrc, /* timestamp and duration of GstBuffer */ GST_BUFFER_DURATION (buf) = dtmfsrc->interval * GST_MSECOND; GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp; + + GST_LOG_OBJECT (dtmfsrc, "Creating new buffer with event %u duration " + " gst: %" GST_TIME_FORMAT " at %" GST_TIME_FORMAT, + event->event_number, GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); /* Set caps on the buffer before pushing it */ diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index 875a020661..8f07cafc4a 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -602,13 +602,6 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) payload->duration = g_htons (payload->duration); - - /* duration of DTMF payloadfor the NEXT packet */ - /* not updated for redundant packets */ - if (dtmfsrc->redundancy_count <= 1) - dtmfsrc->payload->duration += dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; - - if (dtmfsrc->redundancy_count <= 1 && dtmfsrc->last_packet) { GstClockTime inter_digit_interval = MIN_INTER_DIGIT_INTERVAL; @@ -619,8 +612,20 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) GST_BUFFER_DURATION (buf) += inter_digit_interval * GST_MSECOND; } + GST_LOG_OBJECT (dtmfsrc, "Creating new buffer with event %u duration " + " gst: %" GST_TIME_FORMAT " at %" GST_TIME_FORMAT "(rtp ts:%u dur:%u)", + dtmfsrc->payload->event, GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), dtmfsrc->rtp_timestamp, + dtmfsrc->payload->duration); + + /* duration of DTMF payloadfor the NEXT packet */ + /* not updated for redundant packets */ + if (dtmfsrc->redundancy_count <= 1) + dtmfsrc->payload->duration += dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; + if (GST_CLOCK_TIME_IS_VALID (dtmfsrc->timestamp)) dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); + } static GstBuffer * From 60442079f34591cb5a129f7a773ce74afcc1d886 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 25 Aug 2011 19:24:56 +0100 Subject: [PATCH 11/85] tests: make camerabin2 test build https://bugzilla.gnome.org/show_bug.cgi?id=657363 --- tests/check/Makefile.am | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index dca2775b1b..174c73036d 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -242,7 +242,6 @@ elements_camerabin_LDADD = \ elements_camerabin_SOURCES = elements/camerabin.c -if BUILD_EXPERIMENTAL elements_camerabin2_CFLAGS = \ $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) -DGST_USE_UNSTABLE_API @@ -253,8 +252,6 @@ elements_camerabin2_LDADD = \ $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) elements_camerabin2_SOURCES = elements/camerabin2.c -endif - elements_jifmux_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(EXIF_CFLAGS) $(AM_CFLAGS) elements_jifmux_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) $(GST_CHECK_LIBS) $(EXIF_LIBS) $(LDADD) elements_jifmux_SOURCES = elements/jifmux.c From adcfcabb14cec04e3e70529c3bc57ff4aae14c39 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Tue, 23 Aug 2011 20:49:33 +0000 Subject: [PATCH 12/85] hlsdemux: Stop the streaming task on PAUSED_TO_READY state change --- gst/hls/gsthlsdemux.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 1e984b9dc9..cd61ed95b4 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -302,7 +302,7 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: demux->cancelled = TRUE; - g_cond_signal (demux->fetcher_cond); + gst_hls_demux_stop (demux); break; default: break; @@ -374,6 +374,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ()); } + demux->cancelled = TRUE; gst_hls_demux_stop_fetcher (demux, TRUE); gst_task_pause (demux->task); g_cond_signal (demux->thread_cond); @@ -397,6 +398,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop ()); } + demux->cancelled = FALSE; gst_task_start (demux->task); g_static_rec_mutex_unlock (&demux->task_lock); From e1a0bdcdf589db8e29c0779cd5518634db7c903f Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 25 Aug 2011 23:33:10 +0000 Subject: [PATCH 13/85] hlsdemux: do not allow two fetchers to run simultanously --- gst/hls/gsthlsdemux.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index cd61ed95b4..10d2b91503 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -555,7 +555,7 @@ gst_hls_demux_fetcher_sink_event (GstPad * pad, GstEvent * event) GST_DEBUG_OBJECT (demux, "Got EOS on the fetcher pad"); /* signal we have fetched the URI */ if (!demux->cancelled) - g_cond_signal (demux->fetcher_cond); + g_cond_broadcast (demux->fetcher_cond); } default: break; @@ -771,7 +771,7 @@ gst_hls_demux_fetcher_bus_handler (GstBus * bus, if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { demux->fetcher_error = TRUE; - g_cond_signal (demux->fetcher_cond); + g_cond_broadcast (demux->fetcher_cond); } gst_message_unref (message); @@ -994,6 +994,12 @@ gst_hls_demux_fetch_location (GstHLSDemux * demux, const gchar * uri) g_mutex_lock (demux->fetcher_lock); + while (demux->fetcher) + g_cond_wait (demux->fetcher_cond, demux->fetcher_lock); + + if (demux->cancelled) + goto quit; + if (!gst_hls_demux_make_fetcher (demux, uri)) { goto uri_error; } @@ -1034,6 +1040,8 @@ state_change_error: quit: { g_mutex_unlock (demux->fetcher_lock); + /* Unlock any other fetcher that might be waiting */ + g_cond_broadcast (demux->fetcher_cond); return bret; } } From 39657fa52fc93bf7ebb60477bc40245b23867b5c Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 25 Aug 2011 23:35:55 +0000 Subject: [PATCH 14/85] hlsdemux: lock the fetcher mutex before stopping it --- gst/hls/gsthlsdemux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 10d2b91503..06f26a5578 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -375,7 +375,9 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) } demux->cancelled = TRUE; + g_mutex_lock (demux->fetcher_lock); gst_hls_demux_stop_fetcher (demux, TRUE); + g_mutex_unlock (demux->fetcher_lock); gst_task_pause (demux->task); g_cond_signal (demux->thread_cond); @@ -642,7 +644,9 @@ gst_hls_demux_stop_fetcher (GstHLSDemux * demux, gboolean cancelled) static void gst_hls_demux_stop (GstHLSDemux * demux) { + g_mutex_lock (demux->fetcher_lock); gst_hls_demux_stop_fetcher (demux, TRUE); + g_mutex_unlock (demux->fetcher_lock); if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) gst_task_stop (demux->task); g_cond_signal (demux->thread_cond); From 42401da6867487920fb6b91fcb659b871bea9d99 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 25 Aug 2011 23:36:27 +0000 Subject: [PATCH 15/85] hlsdemux: lock the update_thread mutex before stopping it --- gst/hls/gsthlsdemux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 06f26a5578..4c932ff605 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -379,7 +379,9 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) gst_hls_demux_stop_fetcher (demux, TRUE); g_mutex_unlock (demux->fetcher_lock); gst_task_pause (demux->task); + g_mutex_lock (demux->thread_lock); g_cond_signal (demux->thread_cond); + g_mutex_unlock (demux->thread_lock); /* wait for streaming to finish */ g_static_rec_mutex_lock (&demux->task_lock); @@ -649,7 +651,9 @@ gst_hls_demux_stop (GstHLSDemux * demux) g_mutex_unlock (demux->fetcher_lock); if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) gst_task_stop (demux->task); + g_mutex_lock (demux->thread_lock); g_cond_signal (demux->thread_cond); + g_mutex_unlock (demux->thread_lock); } static void From d69297a23c955d01516cb9ed7e707697b7a98083 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 25 Aug 2011 23:37:00 +0000 Subject: [PATCH 16/85] hlsdemux: if we're canceling a fetcher, we must stop its thread --- gst/hls/gsthlsdemux.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 4c932ff605..d588ed970b 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -638,9 +638,10 @@ gst_hls_demux_stop_fetcher (GstHLSDemux * demux, gboolean cancelled) /* if we stopped it to cancell a download, free the cached buffer */ if (cancelled && !gst_adapter_available (demux->download)) { gst_adapter_clear (demux->download); - /* signal the fetcher thread that the download has finished/cancelled */ - g_cond_signal (demux->fetcher_cond); } + /* signal the fetcher thread that the download has finished/cancelled */ + if (cancelled) + g_cond_broadcast (demux->fetcher_cond); } static void From 015d1e11e211c18825856285d22d9b8e5bb5390d Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 25 Aug 2011 23:37:25 +0000 Subject: [PATCH 17/85] hlsdemux: do not post an error if we seek/cancel during caching of fragments --- gst/hls/gsthlsdemux.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index d588ed970b..582507ab5f 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -751,9 +751,11 @@ end_of_playlist: cache_error: { - GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, - ("Could not cache the first fragments"), (NULL)); - gst_hls_demux_stop (demux); + if (!demux->cancelled) { + GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, + ("Could not cache the first fragments"), (NULL)); + gst_hls_demux_stop (demux); + } return; } From 46ac6a2589f4b40dadd9aa30b9a8dc5da47d484d Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 25 Aug 2011 23:37:47 +0000 Subject: [PATCH 18/85] hlsdemux: stop the threads in the proper order first pause the task, then stop all fetchers, then stop the update thread then pause the task again, since it might have been restarted by another thread in the meantime --- gst/hls/gsthlsdemux.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 582507ab5f..14695d5663 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -375,13 +375,14 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) } demux->cancelled = TRUE; + gst_task_pause (demux->task); g_mutex_lock (demux->fetcher_lock); gst_hls_demux_stop_fetcher (demux, TRUE); g_mutex_unlock (demux->fetcher_lock); - gst_task_pause (demux->task); g_mutex_lock (demux->thread_lock); g_cond_signal (demux->thread_cond); g_mutex_unlock (demux->thread_lock); + gst_task_pause (demux->task); /* wait for streaming to finish */ g_static_rec_mutex_lock (&demux->task_lock); From 8c04ea47d2c6fd7db32ac71e025739ec15ce9ede Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 00:26:29 +0000 Subject: [PATCH 19/85] hlsdemux: Ensure the task is paused in case of cache error --- gst/hls/gsthlsdemux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 14695d5663..2c0ec559ee 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -752,6 +752,7 @@ end_of_playlist: cache_error: { + gst_task_pause (demux->task); if (!demux->cancelled) { GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not cache the first fragments"), (NULL)); From 108493ee5151832adb7bd66ed5f074ef0921cebf Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 00:47:53 +0000 Subject: [PATCH 20/85] hlsdemux: do not make the update_thread joinable --- gst/hls/gsthlsdemux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 2c0ec559ee..18c0bc03ef 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -930,7 +930,7 @@ gst_hls_demux_start_update (GstHLSDemux * demux) /* creates a new thread for the updates */ demux->updates_thread = g_thread_create ( - (GThreadFunc) gst_hls_demux_update_thread, demux, TRUE, &error); + (GThreadFunc) gst_hls_demux_update_thread, demux, FALSE, &error); return (error != NULL); } From e380fbb0198244e02ef1795d67878c584a04fa96 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 00:52:46 +0000 Subject: [PATCH 21/85] tsdemux: In push mode, do not start pull loop if upstream seek fails --- gst/mpegtsdemux/mpegtsbase.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/mpegtsdemux/mpegtsbase.c b/gst/mpegtsdemux/mpegtsbase.c index 1749ba2ccc..e76677855b 100644 --- a/gst/mpegtsdemux/mpegtsbase.c +++ b/gst/mpegtsdemux/mpegtsbase.c @@ -1442,7 +1442,7 @@ mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad, if (base->mode == BASE_MODE_PUSHING) { GST_ERROR ("seeking in push mode not supported"); - goto done; + goto push_mode; } /* stop streaming, either by flushing or by pausing the task */ @@ -1493,7 +1493,7 @@ mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad, //else done: gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base); - +push_mode: GST_PAD_STREAM_UNLOCK (base->sinkpad); return ret == GST_FLOW_OK; } From f3cb4439cf54dfc42f5c7270ff2454778d9cc213 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 25 Aug 2011 19:56:58 +0100 Subject: [PATCH 22/85] basevideoencoder: fix element leak and this concludes an hour of yelling at the bloody test failing, only to track down the problem not being in the test. https://bugzilla.gnome.org/show_bug.cgi?id=657368 --- gst-libs/gst/video/gstbasevideoencoder.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst-libs/gst/video/gstbasevideoencoder.c b/gst-libs/gst/video/gstbasevideoencoder.c index e1c2c77a62..5baa4b0d22 100644 --- a/gst-libs/gst/video/gstbasevideoencoder.c +++ b/gst-libs/gst/video/gstbasevideoencoder.c @@ -432,6 +432,7 @@ done: GST_LOG_OBJECT (base_video_encoder, "Returning caps %" GST_PTR_FORMAT, fcaps); + g_object_unref (base_video_encoder); return fcaps; } From 5ae630489cbc6982c2f3c3c47ba3aa9664932c31 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Fri, 26 Aug 2011 09:43:14 -0300 Subject: [PATCH 23/85] camerabin2: Make viewfinder's queue a thread limit only Reduce the viewfinder queue limits to only allow it to store one buffer, preventing the queue from holding old buffers for too long. This also avoids showing slightly outdated frames on the viewfinder when the source has already produced new ones and improves the buffer recycling rate, important for sources that use bufferpools. --- gst/camerabin2/gstcamerabin2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/camerabin2/gstcamerabin2.c b/gst/camerabin2/gstcamerabin2.c index f40ef051be..8343d88505 100644 --- a/gst/camerabin2/gstcamerabin2.c +++ b/gst/camerabin2/gstcamerabin2.c @@ -1368,7 +1368,8 @@ gst_camera_bin_create_elements (GstCameraBin2 * camera) } g_object_set (camera->viewfinderbin_queue, "leaky", 2, "silent", TRUE, - NULL); + "max-size-time", (guint64) 0, "max-size-bytes", (guint) 0, + "max-size-buffers", (guint) 1, NULL); gst_bin_add_many (GST_BIN_CAST (camera), gst_object_ref (camera->video_encodebin), From 71a2a1be8e3931c79272dc245e4c0b90eb2a56a2 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Wed, 17 Aug 2011 09:42:07 +0200 Subject: [PATCH 24/85] Move m_pprevFrame free just before the clone of the latest frame for readability --- ext/opencv/MotionCells.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opencv/MotionCells.cpp b/ext/opencv/MotionCells.cpp index 2b81b305dd..7db9896e51 100644 --- a/ext/opencv/MotionCells.cpp +++ b/ext/opencv/MotionCells.cpp @@ -168,8 +168,6 @@ MotionCells::performDetectionMotionCells (IplImage * p_frame, m_pbwImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1); cvPyrDown (m_pprevFrame, m_pprevDown); cvCvtColor (m_pprevDown, m_pprevgreyImage, CV_RGB2GRAY); - if (m_pprevFrame) - cvReleaseImage (&m_pprevFrame); cvPyrDown (m_pcurFrame, m_pcurDown); cvCvtColor (m_pcurDown, m_pcurgreyImage, CV_RGB2GRAY); m_pdifferenceImage = cvCloneImage (m_pcurgreyImage); @@ -268,6 +266,8 @@ MotionCells::performDetectionMotionCells (IplImage * p_frame, cvReleaseImage (&transparencyimg); } + if (m_pprevFrame) + cvReleaseImage (&m_pprevFrame); m_pprevFrame = cvCloneImage (m_pcurFrame); m_framecnt = 0; if (m_pcurFrame) From 436620fff92b14a63f77fade5325f9e286f7d190 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Mon, 15 Aug 2011 17:57:53 +0200 Subject: [PATCH 25/85] fix valgrind warning about Conditional jump or move depends on uninitialised value(s) --- ext/opencv/MotionCells.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opencv/MotionCells.cpp b/ext/opencv/MotionCells.cpp index 7db9896e51..5223bc7f73 100644 --- a/ext/opencv/MotionCells.cpp +++ b/ext/opencv/MotionCells.cpp @@ -95,6 +95,8 @@ MotionCells::MotionCells () m_saveerrorcode = 0; m_alpha = 0.5; m_beta = 0.5; + m_useAlpha = false; + m_isVisible = false; } From 8fbf3a3129fead054a57c37ee8332e685d9356bc Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 30 Aug 2011 17:43:22 +1000 Subject: [PATCH 26/85] soundtouch: Handling incoming rate when requested. Absorb inbound segments with rate != 1.0 and output a segment with rate taken from the new output-rate property, adjusting the segment applied_rate, stream duration and timestamps as necessary to compensate --- ext/soundtouch/gstpitch.cc | 51 +++++++++++++++++++++++++++++++------- ext/soundtouch/gstpitch.hh | 7 ++++++ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/ext/soundtouch/gstpitch.cc b/ext/soundtouch/gstpitch.cc index 46f73194d5..6b5b95f151 100644 --- a/ext/soundtouch/gstpitch.cc +++ b/ext/soundtouch/gstpitch.cc @@ -55,6 +55,7 @@ struct _GstPitchPrivate enum { ARG_0, + ARG_OUT_RATE, ARG_RATE, ARG_TEMPO, ARG_PITCH @@ -147,6 +148,11 @@ gst_pitch_class_init (GstPitchClass * klass) "Audio stream rate", 0.1, 10.0, 1.0, (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, ARG_OUT_RATE, + g_param_spec_float ("output-rate", "Output Rate", + "Output rate on downstream segment events", 0.1, 10.0, 1.0, + (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS))); + g_type_class_add_private (gobject_class, sizeof (GstPitchPrivate)); } @@ -186,12 +192,14 @@ gst_pitch_init (GstPitch * pitch, GstPitchClass * pitch_class) pitch->tempo = 1.0; pitch->rate = 1.0; + pitch->out_seg_rate = 1.0; + pitch->seg_arate = 1.0; pitch->pitch = 1.0; pitch->next_buffer_time = 0; pitch->next_buffer_offset = 0; pitch->priv->st->setRate (pitch->rate); - pitch->priv->st->setTempo (pitch->tempo); + pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate); pitch->priv->st->setPitch (pitch->pitch); pitch->priv->stream_time_ratio = 1.0; @@ -232,18 +240,22 @@ gst_pitch_set_property (GObject * object, guint prop_id, switch (prop_id) { case ARG_TEMPO: pitch->tempo = g_value_get_float (value); - pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate; - pitch->priv->st->setTempo (pitch->tempo); + pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate; + pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate); GST_OBJECT_UNLOCK (pitch); gst_pitch_update_duration (pitch); break; case ARG_RATE: pitch->rate = g_value_get_float (value); - pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate; + pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate; pitch->priv->st->setRate (pitch->rate); GST_OBJECT_UNLOCK (pitch); gst_pitch_update_duration (pitch); break; + case ARG_OUT_RATE: + /* Has no effect until the next input segment */ + pitch->out_seg_rate = g_value_get_float (value); + GST_OBJECT_UNLOCK (pitch); case ARG_PITCH: pitch->pitch = g_value_get_float (value); pitch->priv->st->setPitch (pitch->pitch); @@ -270,6 +282,9 @@ gst_pitch_get_property (GObject * object, guint prop_id, case ARG_RATE: g_value_set_float (value, pitch->rate); break; + case ARG_OUT_RATE: + g_value_set_float (value, pitch->out_seg_rate); + break; case ARG_PITCH: g_value_set_float (value, pitch->pitch); break; @@ -695,16 +710,17 @@ gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event) gint64 start_value, stop_value, base; gint64 next_offset = 0, next_time = 0; gboolean update = FALSE; - gdouble rate; + gdouble rate, out_seg_rate, arate, our_arate; gfloat stream_time_ratio; g_return_val_if_fail (event, FALSE); GST_OBJECT_LOCK (pitch); stream_time_ratio = pitch->priv->stream_time_ratio; + out_seg_rate = pitch->out_seg_rate; GST_OBJECT_UNLOCK (pitch); - gst_event_parse_new_segment (*event, &update, &rate, &format, &start_value, + gst_event_parse_new_segment_full (*event, &update, &rate, &arate, &format, &start_value, &stop_value, &base); if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) { @@ -713,21 +729,36 @@ gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event) "open ended NEWSEGMENT in TIME format."); gst_event_unref (*event); *event = - gst_event_new_new_segment (update, rate, GST_FORMAT_TIME, 0, -1, 0); + gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, 0, -1, 0); start_value = 0; stop_value = -1; base = 0; } + /* Figure out how much of the incoming 'rate' we'll apply ourselves */ + our_arate = rate / out_seg_rate; + /* update the output rate variables */ + rate = out_seg_rate; + arate *= our_arate; + GST_LOG_OBJECT (pitch->sinkpad, "segment %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%d)", start_value, stop_value, format); + stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate; + if (stream_time_ratio == 0) { GST_LOG_OBJECT (pitch->sinkpad, "stream_time_ratio is zero"); return FALSE; } + /* Update the playback rate */ + GST_OBJECT_LOCK (pitch); + pitch->seg_arate = our_arate; + pitch->priv->stream_time_ratio = stream_time_ratio; + pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate); + GST_OBJECT_UNLOCK (pitch); + start_value = (gint64) (start_value / stream_time_ratio); if (stop_value != -1) stop_value = (gint64) (stop_value / stream_time_ratio); @@ -752,8 +783,8 @@ gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event) pitch->next_buffer_offset = next_offset; gst_event_unref (*event); - *event = gst_event_new_new_segment (update, rate, format, start_value, - stop_value, base); + *event = gst_event_new_new_segment_full (update, rate, arate, format, + start_value, stop_value, base); return TRUE; } @@ -772,6 +803,8 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event) case GST_EVENT_FLUSH_STOP: gst_pitch_flush_buffer (pitch, FALSE); pitch->priv->st->clear (); + pitch->next_buffer_offset = 0; + pitch->next_buffer_time = 0; pitch->min_latency = pitch->max_latency = 0; break; case GST_EVENT_EOS: diff --git a/ext/soundtouch/gstpitch.hh b/ext/soundtouch/gstpitch.hh index 32cb04dd13..ad1f7f6d1f 100644 --- a/ext/soundtouch/gstpitch.hh +++ b/ext/soundtouch/gstpitch.hh @@ -58,11 +58,18 @@ struct _GstPitch * > 1 makes the stream shorter */ + gfloat out_seg_rate; /* change output segment rate + * Affects playback when input + * segments have rate != out_rate + */ + gfloat pitch; /* change pitch * change the pitch without affecting the * duration, stream length doesn't change */ + gfloat seg_arate; /* Rate to apply from input segment */ + /* values extracted from caps */ gint samplerate; /* samplerate */ gint channels; /* number of audio channels */ From 3054355dc1bf9df817bd257d523408ece64472d8 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 17:54:51 +0000 Subject: [PATCH 27/85] hlsdemux: We do not need to set the current playlist to the main one When caching fragments, if we set the current playlist to main, then it will always think it's a live stream (no endlist in it) so it will force the redownload of the main playlist after every seek, which is unnecessary. Also, it causes a race condition where a seek migh happen during that redownload, and we'll think we're trying to seek a live pipeline. --- gst/hls/gsthlsdemux.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 18c0bc03ef..89e80557ad 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -939,17 +939,6 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) { gint i; - /* Start parsing the main playlist */ - gst_m3u8_client_set_current (demux->client, demux->client->main); - - if (gst_m3u8_client_is_live (demux->client)) { - if (!gst_hls_demux_update_playlist (demux, FALSE)) { - GST_ERROR_OBJECT (demux, "Could not fetch the main playlist %s", - demux->client->main->uri); - return FALSE; - } - } - /* If this playlist is a variant playlist, select the first one * and update it */ if (gst_m3u8_client_has_variant_playlist (demux->client)) { From 50d0468241c58134b26e1ba519cd6b8b1b2a24c4 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 19:09:54 +0000 Subject: [PATCH 28/85] hlsdemux: We must signal the fetcher_cond while holding its lock --- gst/hls/gsthlsdemux.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 89e80557ad..9eca3c8cb6 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -559,8 +559,11 @@ gst_hls_demux_fetcher_sink_event (GstPad * pad, GstEvent * event) case GST_EVENT_EOS:{ GST_DEBUG_OBJECT (demux, "Got EOS on the fetcher pad"); /* signal we have fetched the URI */ - if (!demux->cancelled) + if (!demux->cancelled) { + g_mutex_lock (demux->fetcher_lock); g_cond_broadcast (demux->fetcher_cond); + g_mutex_unlock (demux->fetcher_lock); + } } default: break; @@ -784,7 +787,9 @@ gst_hls_demux_fetcher_bus_handler (GstBus * bus, if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { demux->fetcher_error = TRUE; + g_mutex_lock (demux->fetcher_lock); g_cond_broadcast (demux->fetcher_cond); + g_mutex_unlock (demux->fetcher_lock); } gst_message_unref (message); @@ -1041,9 +1046,9 @@ state_change_error: quit: { - g_mutex_unlock (demux->fetcher_lock); /* Unlock any other fetcher that might be waiting */ g_cond_broadcast (demux->fetcher_cond); + g_mutex_unlock (demux->fetcher_lock); return bret; } } From e22c300be28893780d329969a96f688582571479 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 19:10:39 +0000 Subject: [PATCH 29/85] hlsdemux: Rename functions that need to be called with a locked mutex --- gst/hls/gsthlsdemux.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 9eca3c8cb6..6a01cd72c0 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -100,7 +100,7 @@ static gboolean gst_hls_demux_fetcher_sink_event (GstPad * pad, GstEvent * event); static void gst_hls_demux_loop (GstHLSDemux * demux); static void gst_hls_demux_stop (GstHLSDemux * demux); -static void gst_hls_demux_stop_fetcher (GstHLSDemux * demux, +static void gst_hls_demux_stop_fetcher_locked (GstHLSDemux * demux, gboolean cancelled); static gboolean gst_hls_demux_start_update (GstHLSDemux * demux); static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux); @@ -377,7 +377,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) demux->cancelled = TRUE; gst_task_pause (demux->task); g_mutex_lock (demux->fetcher_lock); - gst_hls_demux_stop_fetcher (demux, TRUE); + gst_hls_demux_stop_fetcher_locked (demux, TRUE); g_mutex_unlock (demux->fetcher_lock); g_mutex_lock (demux->thread_lock); g_cond_signal (demux->thread_cond); @@ -614,7 +614,7 @@ done: } static void -gst_hls_demux_stop_fetcher (GstHLSDemux * demux, gboolean cancelled) +gst_hls_demux_stop_fetcher_locked (GstHLSDemux * demux, gboolean cancelled) { GstPad *pad; @@ -652,7 +652,7 @@ static void gst_hls_demux_stop (GstHLSDemux * demux) { g_mutex_lock (demux->fetcher_lock); - gst_hls_demux_stop_fetcher (demux, TRUE); + gst_hls_demux_stop_fetcher_locked (demux, TRUE); g_mutex_unlock (demux->fetcher_lock); if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) gst_task_stop (demux->task); @@ -797,7 +797,7 @@ gst_hls_demux_fetcher_bus_handler (GstBus * bus, } static gboolean -gst_hls_demux_make_fetcher (GstHLSDemux * demux, const gchar * uri) +gst_hls_demux_make_fetcher_locked (GstHLSDemux * demux, const gchar * uri) { GstPad *pad; @@ -1007,7 +1007,7 @@ gst_hls_demux_fetch_location (GstHLSDemux * demux, const gchar * uri) if (demux->cancelled) goto quit; - if (!gst_hls_demux_make_fetcher (demux, uri)) { + if (!gst_hls_demux_make_fetcher_locked (demux, uri)) { goto uri_error; } @@ -1019,7 +1019,7 @@ gst_hls_demux_fetch_location (GstHLSDemux * demux, const gchar * uri) GST_DEBUG_OBJECT (demux, "Waiting to fetch the URI"); g_cond_wait (demux->fetcher_cond, demux->fetcher_lock); - gst_hls_demux_stop_fetcher (demux, FALSE); + gst_hls_demux_stop_fetcher_locked (demux, FALSE); if (gst_adapter_available (demux->download)) { GST_INFO_OBJECT (demux, "URI fetched successfully"); From 6e9eeb767839260773e111b3775717ed7c77b33a Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 19:10:58 +0000 Subject: [PATCH 30/85] hlsdemux: Do not succeed a download if the fetcher had an error --- gst/hls/gsthlsdemux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 6a01cd72c0..d0331bf69a 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -1021,7 +1021,7 @@ gst_hls_demux_fetch_location (GstHLSDemux * demux, const gchar * uri) gst_hls_demux_stop_fetcher_locked (demux, FALSE); - if (gst_adapter_available (demux->download)) { + if (!demux->fetcher_error && gst_adapter_available (demux->download)) { GST_INFO_OBJECT (demux, "URI fetched successfully"); bret = TRUE; } From fc0fe37b10b34f4cd710d851ec215fd4165d65e9 Mon Sep 17 00:00:00 2001 From: Jonas Larsson Date: Fri, 26 Aug 2011 19:29:06 +0000 Subject: [PATCH 31/85] hlsdemux: Split playslist on \r\n not just \n --- gst/hls/m3u8.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gst/hls/m3u8.c b/gst/hls/m3u8.c index 26548101f3..f081764257 100644 --- a/gst/hls/m3u8.c +++ b/gst/hls/m3u8.c @@ -230,6 +230,8 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated) *end = '\0'; if (data[0] != '#') { + gchar *r; + if (duration < 0 && list == NULL) { GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data); goto next_line; @@ -250,8 +252,13 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated) *slash = '\0'; data = g_strdup_printf ("%s/%s", self->uri, data); *slash = '/'; - } else + } else { data = g_strdup (data); + } + + r = g_utf8_strchr (data, -1, '\r'); + if (r) + *r = '\0'; if (list != NULL) { if (g_list_find_custom (self->lists, data, From cb30cd242ca5084df9eaee2154bb80a740c4a7ed Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 21:46:06 +0000 Subject: [PATCH 32/85] hlsdemux: do not broadcast the fetcher_cond on bus error if we're cancelled If we cancel the fetch and call the stop_fetcher, which holds the lock, when it sets the fetcher's state to NULL, it might send an error on the bus. In that case, we must ignore it, otherwise it will try to take the lock and will block forever. --- gst/hls/gsthlsdemux.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index d0331bf69a..9b6f99a61d 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -787,9 +787,11 @@ gst_hls_demux_fetcher_bus_handler (GstBus * bus, if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { demux->fetcher_error = TRUE; - g_mutex_lock (demux->fetcher_lock); - g_cond_broadcast (demux->fetcher_cond); - g_mutex_unlock (demux->fetcher_lock); + if (!demux->cancelled) { + g_mutex_lock (demux->fetcher_lock); + g_cond_broadcast (demux->fetcher_cond); + g_mutex_unlock (demux->fetcher_lock); + } } gst_message_unref (message); From 59c61209cf87f547386f90422f31f66605ca67b1 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 22:44:08 +0000 Subject: [PATCH 33/85] tsdemux: Need to flush all streams when we receive a flush-start --- gst/mpegtsdemux/mpegtsbase.c | 13 +++++++++++++ gst/mpegtsdemux/mpegtsbase.h | 3 +++ gst/mpegtsdemux/tsdemux.c | 15 ++++++++++++--- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/gst/mpegtsdemux/mpegtsbase.c b/gst/mpegtsdemux/mpegtsbase.c index e76677855b..5542abf029 100644 --- a/gst/mpegtsdemux/mpegtsbase.c +++ b/gst/mpegtsdemux/mpegtsbase.c @@ -1154,6 +1154,18 @@ gst_mpegts_base_handle_eos (MpegTSBase * base) return TRUE; } +static inline void +mpegts_base_flush (MpegTSBase * base) +{ + MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base); + + /* Call implementation */ + if (G_UNLIKELY (klass->flush == NULL)) + GST_WARNING_OBJECT (base, "Class doesn't have a 'flush' implementation !"); + else + klass->flush (base); +} + static gboolean mpegts_base_sink_event (GstPad * pad, GstEvent * event) { @@ -1191,6 +1203,7 @@ mpegts_base_sink_event (GstPad * pad, GstEvent * event) break; case GST_EVENT_FLUSH_START: mpegts_packetizer_flush (base->packetizer); + mpegts_base_flush (base); res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event); gst_event_unref (event); break; diff --git a/gst/mpegtsdemux/mpegtsbase.h b/gst/mpegtsdemux/mpegtsbase.h index 1513898ec5..cce4e0fd72 100644 --- a/gst/mpegtsdemux/mpegtsbase.h +++ b/gst/mpegtsdemux/mpegtsbase.h @@ -162,6 +162,9 @@ struct _MpegTSBaseClass { /* seek is called to wait for seeking */ GstFlowReturn (*seek) (MpegTSBase * base, GstEvent * event, guint16 pid); + /* flush all streams */ + void (*flush) (MpegTSBase * base); + /* signals */ void (*pat_info) (GstStructure *pat); void (*pmt_info) (GstStructure *pmt); diff --git a/gst/mpegtsdemux/tsdemux.c b/gst/mpegtsdemux/tsdemux.c index f7dc5cc6b1..3a327a5cd9 100644 --- a/gst/mpegtsdemux/tsdemux.c +++ b/gst/mpegtsdemux/tsdemux.c @@ -188,6 +188,7 @@ static void gst_ts_demux_reset (MpegTSBase * base); static GstFlowReturn gst_ts_demux_push (MpegTSBase * base, MpegTSPacketizerPacket * packet, MpegTSPacketizerSection * section); +static void gst_ts_demux_flush (MpegTSBase * base); static void gst_ts_demux_stream_added (MpegTSBase * base, MpegTSBaseStream * stream, MpegTSBaseProgram * program); @@ -282,6 +283,7 @@ gst_ts_demux_class_init (GstTSDemuxClass * klass) ts_class->stream_removed = gst_ts_demux_stream_removed; ts_class->find_timestamps = GST_DEBUG_FUNCPTR (find_timestamps); ts_class->seek = GST_DEBUG_FUNCPTR (gst_ts_demux_do_seek); + ts_class->flush = GST_DEBUG_FUNCPTR (gst_ts_demux_flush); } static void @@ -882,9 +884,7 @@ gst_ts_demux_srcpad_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: res = mpegts_base_handle_seek_event ((MpegTSBase *) demux, pad, event); - if (res) - demux->need_newsegment = TRUE; - else + if (!res) GST_WARNING ("seeking failed"); gst_event_unref (event); break; @@ -2278,6 +2278,15 @@ gst_ts_demux_handle_packet (GstTSDemux * demux, TSDemuxStream * stream, return res; } +static void +gst_ts_demux_flush (MpegTSBase * base) +{ + GstTSDemux *demux = GST_TS_DEMUX_CAST (base); + + demux->need_newsegment = TRUE; + gst_ts_demux_flush_streams (demux); +} + static GstFlowReturn gst_ts_demux_push (MpegTSBase * base, MpegTSPacketizerPacket * packet, MpegTSPacketizerSection * section) From 4a6d888961fc38aa7b55d7adfb9458ad5ad5d87a Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 22:47:35 +0000 Subject: [PATCH 34/85] hlsdemux: oups, we need to empty the queue after freeing its content --- gst/hls/gsthlsdemux.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 9b6f99a61d..adeb965331 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -392,6 +392,8 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) GstBuffer *buf = g_queue_pop_head (demux->queue); gst_buffer_unref (buf); } + g_queue_clear (demux->queue); + gst_adapter_clear (demux->download); GST_DEBUG_OBJECT (demux, "seeking to sequence %d", current_sequence); demux->client->sequence = current_sequence; From ed5a5cc139ae9912386aef461a0bbe1555143ce3 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 26 Aug 2011 22:48:01 +0000 Subject: [PATCH 35/85] hlsdemux: seems there was a typo, free the adapter if it contains data, not if already empty --- gst/hls/gsthlsdemux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index adeb965331..8fdf317e67 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -642,7 +642,7 @@ gst_hls_demux_stop_fetcher_locked (GstHLSDemux * demux, gboolean cancelled) demux->fetcher = NULL; /* if we stopped it to cancell a download, free the cached buffer */ - if (cancelled && !gst_adapter_available (demux->download)) { + if (cancelled && gst_adapter_available (demux->download)) { gst_adapter_clear (demux->download); } /* signal the fetcher thread that the download has finished/cancelled */ From f93939b46cedcfe78bad3df4dd55ce410088032a Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Wed, 31 Aug 2011 01:08:39 +0000 Subject: [PATCH 36/85] hlsdemux: Send a flush-stop when switching pads. Fixes A->A/V sync issue. This is to ensure that we reset the accumulate segment on the sinks so if we start with audio only then switch to audio+video, then both sinks will have the same segments and will be synchronized. --- gst/hls/gsthlsdemux.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 8fdf317e67..4ea8bfef2d 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -670,6 +670,18 @@ switch_pads (GstHLSDemux * demux, GstCaps * newcaps) GST_DEBUG ("Switching pads (oldpad:%p)", oldpad); + /* FIXME: This is a workaround for a bug in playsink. + * If we're switching from an audio-only or video-only fragment + * to an audio-video segment, the new sink doesn't know about + * the current running time and audio/video will go out of sync. + * + * This should be fixed in playsink by distributing the + * current running time to newly created sinks and is + * fixed in 0.11 with the new segments. + */ + if (demux->srcpad) + gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop ()); + /* First create and activate new pad */ demux->srcpad = gst_pad_new_from_static_template (&srctemplate, NULL); gst_pad_set_event_function (demux->srcpad, From 4724b9a5b6e8f9435055613775328d7c33c74cd6 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Wed, 31 Aug 2011 02:05:08 +0000 Subject: [PATCH 37/85] hlsdemux: Post duration message when we parse the playlist --- gst/hls/gsthlsdemux.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 4ea8bfef2d..4a9b0aca25 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -980,6 +980,15 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) demux->client->sequence -= demux->fragments_cache; else demux->client->sequence = 0; + } else { + GstClockTime duration = gst_m3u8_client_get_duration (demux->client); + + GST_DEBUG_OBJECT (demux, "Sending duration message : %" GST_TIME_FORMAT, + GST_TIME_ARGS (duration)); + if (duration != GST_CLOCK_TIME_NONE) + gst_element_post_message (GST_ELEMENT (demux), + gst_message_new_duration (GST_OBJECT (demux), + GST_FORMAT_TIME, duration)); } /* Cache the first fragments */ From 917708df82b1bb50401c85bbe3d7573650ecd929 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Wed, 31 Aug 2011 03:01:58 +0000 Subject: [PATCH 38/85] hlsdemux: start/stop update thread and keep track of status --- gst/hls/gsthlsdemux.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 4a9b0aca25..41284e4832 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -102,6 +102,7 @@ static void gst_hls_demux_loop (GstHLSDemux * demux); static void gst_hls_demux_stop (GstHLSDemux * demux); static void gst_hls_demux_stop_fetcher_locked (GstHLSDemux * demux, gboolean cancelled); +static void gst_hls_demux_stop_update (GstHLSDemux * demux); static gboolean gst_hls_demux_start_update (GstHLSDemux * demux); static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux); static gboolean gst_hls_demux_schedule (GstHLSDemux * demux); @@ -379,9 +380,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) g_mutex_lock (demux->fetcher_lock); gst_hls_demux_stop_fetcher_locked (demux, TRUE); g_mutex_unlock (demux->fetcher_lock); - g_mutex_lock (demux->thread_lock); - g_cond_signal (demux->thread_cond); - g_mutex_unlock (demux->thread_lock); + gst_hls_demux_stop_update (demux); gst_task_pause (demux->task); /* wait for streaming to finish */ @@ -658,9 +657,7 @@ gst_hls_demux_stop (GstHLSDemux * demux) g_mutex_unlock (demux->fetcher_lock); if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) gst_task_stop (demux->task); - g_mutex_lock (demux->thread_lock); - g_cond_signal (demux->thread_cond); - g_mutex_unlock (demux->thread_lock); + gst_hls_demux_stop_update (demux); } static void @@ -897,6 +894,7 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) * switch to a different bitrate */ g_mutex_lock (demux->thread_lock); + GST_DEBUG_OBJECT (demux, "Started updates thread"); while (TRUE) { /* block until the next scheduled update or the signal to quit this thread */ if (g_cond_timed_wait (demux->thread_cond, demux->thread_lock, @@ -939,19 +937,40 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) quit: { + GST_DEBUG_OBJECT (demux, "Stopped updates thread"); + demux->updates_thread = NULL; g_mutex_unlock (demux->thread_lock); return TRUE; } } + +static void +gst_hls_demux_stop_update (GstHLSDemux * demux) +{ + GError *error; + + GST_DEBUG_OBJECT (demux, "Stopping updates thread"); + while (demux->updates_thread) { + g_mutex_lock (demux->thread_lock); + g_cond_signal (demux->thread_cond); + g_mutex_unlock (demux->thread_lock); + } +} + static gboolean gst_hls_demux_start_update (GstHLSDemux * demux) { GError *error; /* creates a new thread for the updates */ - demux->updates_thread = g_thread_create ( - (GThreadFunc) gst_hls_demux_update_thread, demux, FALSE, &error); + g_mutex_lock (demux->thread_lock); + if (demux->updates_thread == NULL) { + GST_DEBUG_OBJECT (demux, "Starting updates thread"); + demux->updates_thread = g_thread_create ( + (GThreadFunc) gst_hls_demux_update_thread, demux, FALSE, &error); + } + g_mutex_unlock (demux->thread_lock); return (error != NULL); } From 277a7d05b2674705a93047f34ddcac462821de2f Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Wed, 31 Aug 2011 03:07:48 +0000 Subject: [PATCH 39/85] hlsdemux: If paused, do not cache fragments until out of memory error! We should stop the update thread in PAUSED state and avoid fetching new fragments when the queue is not empty. The queue should always be empty since we push data into a queue. Also, in totem, if we seek and pause the stream while it's buffering, then the state will stay playing for some reason, so it's best not to continue fetching fragments forever. --- gst/hls/gsthlsdemux.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 41284e4832..74c6f32196 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -294,6 +294,14 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: gst_hls_demux_reset (demux, FALSE); break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + /* Start the streaming loop in paused only if we already received + the main playlist. It might have been stopped if we were in PAUSED + state and we filled our queue with enough cached fragments + */ + if (gst_m3u8_client_get_uri (demux->client)[0] != '\0') + gst_hls_demux_start_update (demux); + break; default: break; } @@ -301,6 +309,9 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + gst_hls_demux_stop_update (demux); + break; case GST_STATE_CHANGE_PAUSED_TO_READY: demux->cancelled = TRUE; gst_hls_demux_stop (demux); @@ -716,8 +727,9 @@ gst_hls_demux_loop (GstHLSDemux * demux) if (!gst_hls_demux_cache_fragments (demux)) goto cache_error; - /* we can start now the updates thread */ - gst_hls_demux_start_update (demux); + /* we can start now the updates thread (only if on playing) */ + if (GST_STATE (demux) == GST_STATE_PLAYING) + gst_hls_demux_start_update (demux); GST_INFO_OBJECT (demux, "First fragments cached successfully"); } @@ -725,7 +737,7 @@ gst_hls_demux_loop (GstHLSDemux * demux) if (demux->end_of_playlist) goto end_of_playlist; - goto empty_queue; + goto pause_task; } buf = g_queue_pop_head (demux->queue); @@ -783,7 +795,7 @@ error: return; } -empty_queue: +pause_task: { gst_task_pause (demux->task); return; @@ -925,14 +937,15 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) } /* fetch the next fragment */ - if (!gst_hls_demux_get_next_fragment (demux, TRUE)) { - if (!demux->end_of_playlist && !demux->cancelled) - GST_ERROR_OBJECT (demux, "Could not fetch the next fragment"); - goto quit; + if (g_queue_is_empty (demux->queue)) { + if (!gst_hls_demux_get_next_fragment (demux, TRUE)) { + if (!demux->end_of_playlist && !demux->cancelled) + GST_ERROR_OBJECT (demux, "Could not fetch the next fragment"); + goto quit; + } + /* try to switch to another bitrate if needed */ + gst_hls_demux_switch_playlist (demux); } - - /* try to switch to another bitrate if needed */ - gst_hls_demux_switch_playlist (demux); } quit: From 6436747eb0a453b9fedffe04af14c9c8957e61a8 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Wed, 31 Aug 2011 03:20:48 +0000 Subject: [PATCH 40/85] hlsdemux: remove spamming GST_LOG_OBJECT --- gst/hls/gsthlsdemux.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 74c6f32196..a7f3a4b563 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -615,8 +615,6 @@ gst_hls_demux_fetcher_chain (GstPad * pad, GstBuffer * buf) goto done; } - GST_LOG_OBJECT (demux, "The uri fetcher received a new buffer of size %u", - GST_BUFFER_SIZE (buf)); gst_adapter_push (demux->download, buf); done: From 0c97deb59dae4bf49c7dab15d89dc4af5b4b76b1 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Wed, 31 Aug 2011 03:47:52 +0000 Subject: [PATCH 41/85] hlsdemux: switch to higher/lower bitrate by more than one step at a time --- gst/hls/gsthlsdemux.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index a7f3a4b563..bfda3a5c8c 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -1229,14 +1229,20 @@ gst_hls_demux_switch_playlist (GstHLSDemux * demux) /* if we are on time switch to a higher bitrate */ if (diff > limit) { - gst_hls_demux_change_playlist (demux, TRUE); + while (diff > limit) { + gst_hls_demux_change_playlist (demux, TRUE); + diff -= limit; + } + demux->accumulated_delay = 0; } else if (diff < 0) { /* if the client is too slow wait until it has accumulated a certain delay to * switch to a lower bitrate */ demux->accumulated_delay -= diff; if (demux->accumulated_delay >= limit) { - gst_hls_demux_change_playlist (demux, FALSE); - } else if (demux->accumulated_delay < 0) { + while (demux->accumulated_delay >= limit) { + gst_hls_demux_change_playlist (demux, FALSE); + demux->accumulated_delay -= limit; + } demux->accumulated_delay = 0; } } From c8b7ae1ffa8f3813e00f17089987758f6f67a419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 31 Aug 2011 12:12:59 +0200 Subject: [PATCH 42/85] hlsdemux: Remove unused variable --- gst/hls/gsthlsdemux.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index bfda3a5c8c..9d8c3126d4 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -959,8 +959,6 @@ quit: static void gst_hls_demux_stop_update (GstHLSDemux * demux) { - GError *error; - GST_DEBUG_OBJECT (demux, "Stopping updates thread"); while (demux->updates_thread) { g_mutex_lock (demux->thread_lock); From 119771eaabb67e43c3a5929f7c9327d6b1f9bb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 31 Aug 2011 14:47:40 +0200 Subject: [PATCH 43/85] hlsdemux: Join the task instead of just stopping it and not waiting for it to finish Fixes interesting race conditions that cause crashes in decodebin2 because pads are added/removed from child elements although they should be in READY state already. --- gst/hls/gsthlsdemux.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 9d8c3126d4..522699bbd9 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -664,8 +664,7 @@ gst_hls_demux_stop (GstHLSDemux * demux) g_mutex_lock (demux->fetcher_lock); gst_hls_demux_stop_fetcher_locked (demux, TRUE); g_mutex_unlock (demux->fetcher_lock); - if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) - gst_task_stop (demux->task); + gst_task_join (demux->task); gst_hls_demux_stop_update (demux); } From cf8f00d57a7177b6ae719ae00c262eff37d5feea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 31 Aug 2011 14:48:47 +0200 Subject: [PATCH 44/85] hlsdemux: Just join the task gst_task_join() will already stop the task, no need to stop it additionally. --- gst/hls/gsthlsdemux.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 522699bbd9..a24441c37f 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -156,10 +156,7 @@ gst_hls_demux_dispose (GObject * obj) g_cond_free (demux->thread_cond); g_mutex_free (demux->thread_lock); - if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) { - gst_task_stop (demux->task); - gst_task_join (demux->task); - } + gst_task_join (demux->task); gst_object_unref (demux->task); g_static_rec_mutex_free (&demux->task_lock); From ac7fc0d357f45a3be22f3e7606f271f494bce7f9 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 31 Aug 2011 15:33:10 -0300 Subject: [PATCH 45/85] camerabin2: Use explicit boolean literals to make gobject happy gobject boolean properties are strict and only accept 1 or 0, otherwise they throw an assertion. --- gst/camerabin2/gstcamerabin2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/camerabin2/gstcamerabin2.c b/gst/camerabin2/gstcamerabin2.c index 8343d88505..e7bc09c851 100644 --- a/gst/camerabin2/gstcamerabin2.c +++ b/gst/camerabin2/gstcamerabin2.c @@ -1424,7 +1424,8 @@ gst_camera_bin_create_elements (GstCameraBin2 * camera) g_object_set (camera->video_encodebin, "flags", encbin_flags, NULL); g_object_set (camera->viewfinderbin, "disable-converters", - camera->flags & GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION, NULL); + camera->flags & GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION ? TRUE : FALSE, + NULL); if (camera->video_profile_switch) { GST_DEBUG_OBJECT (camera, "Switching encodebin's profile"); From e17d99c4c426d432858e532d9f74fe01751535db Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 31 Aug 2011 13:28:25 -0300 Subject: [PATCH 46/85] basecamerabinsrc: Optimize preview pipeline startup Use gst_element_link_pads_full to optimize preview pipeline startup, leading to a faster camerabin2 startup as well. --- .../basecamerabinsrc/gstcamerabinpreview.c | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c index 6b854197dd..6dd07b0609 100644 --- a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c +++ b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c @@ -128,6 +128,7 @@ gst_camerabin_create_preview_pipeline (GstElement * element, GstElement *csp2; GstElement *vscale; gboolean added = FALSE; + gboolean linkfail = FALSE; GstBus *bus; GstAppSinkCallbacks callbacks = { 0, }; @@ -154,13 +155,34 @@ gst_camerabin_create_preview_pipeline (GstElement * element, added = TRUE; if (filter) { - if (!gst_element_link_many (data->appsrc, filter, csp, vscale, csp2, - data->capsfilter, data->appsink, NULL)) - goto error; + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->appsrc, "src", + filter, NULL, GST_PAD_LINK_CHECK_NOTHING)); + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (filter, NULL, + csp, "sink", GST_PAD_LINK_CHECK_CAPS)); } else { - if (!gst_element_link_many (data->appsrc, csp, vscale, csp2, - data->capsfilter, data->appsink, NULL)) - goto error; + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->appsrc, "src", + csp, "sink", GST_PAD_LINK_CHECK_NOTHING)); + } + + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (csp, "src", vscale, + "sink", GST_PAD_LINK_CHECK_NOTHING)); + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (vscale, "src", csp2, + "sink", GST_PAD_LINK_CHECK_NOTHING)); + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (csp2, "src", + data->capsfilter, "sink", GST_PAD_LINK_CHECK_NOTHING)); + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->capsfilter, "src", + data->appsink, "sink", GST_PAD_LINK_CHECK_NOTHING)); + + if (linkfail) { + GST_WARNING ("Failed to link preview pipeline elements"); + goto error; } callbacks.new_buffer = gst_camerabin_preview_pipeline_new_buffer; From 2ed3d1b3092c12af2b176d7d267e923805d0c8c8 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 31 Aug 2011 13:29:17 -0300 Subject: [PATCH 47/85] camerabin2: Micro optimize encodebin pads linking Reduce one check in encodebin pads linking on camerabin2 by using the link_pads_full version --- gst/camerabin2/gstcamerabin2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/camerabin2/gstcamerabin2.c b/gst/camerabin2/gstcamerabin2.c index e7bc09c851..3b08927836 100644 --- a/gst/camerabin2/gstcamerabin2.c +++ b/gst/camerabin2/gstcamerabin2.c @@ -1132,7 +1132,7 @@ gst_camera_bin_link_encodebin (GstCameraBin2 * camera, GstElement * encodebin, return GST_PAD_LINK_REFUSED; } - ret = gst_pad_link (srcpad, sinkpad); + ret = gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_CAPS); gst_object_unref (sinkpad); gst_object_unref (srcpad); From d663259d4ba75820d1e85a35b9aea9dda823d73b Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Wed, 31 Aug 2011 23:06:53 +0200 Subject: [PATCH 48/85] mms: fix warnings related to the format on the debug lines. --- ext/libmms/gstmms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/libmms/gstmms.c b/ext/libmms/gstmms.c index 23a5226ea3..cf7a091454 100644 --- a/ext/libmms/gstmms.c +++ b/ext/libmms/gstmms.c @@ -273,7 +273,7 @@ gst_mms_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, static gboolean gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment) { - mms_off_t start; + gint64 start; GstMMS *mmssrc = GST_MMS (src); if (segment->format == GST_FORMAT_TIME) { From db0739e54f6d1917d5899cf8d2f5c817ab4fb6cf Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Thu, 1 Sep 2011 15:57:50 +0200 Subject: [PATCH 49/85] camerabin2: Add a flag to disable image conversion elements If we know that our camera source element produces buffers at the same resolution and appropriate colourspace for the output, we don't need any of the generic conversion elements in encodebin. This reduces caps negotiation overheads among other things. --- gst/camerabin2/gstcamerabin2.c | 7 +++++++ gst/camerabin2/gstcamerabin2.h | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/gst/camerabin2/gstcamerabin2.c b/gst/camerabin2/gstcamerabin2.c index 3b08927836..1caa164048 100644 --- a/gst/camerabin2/gstcamerabin2.c +++ b/gst/camerabin2/gstcamerabin2.c @@ -273,6 +273,8 @@ gst_cam_flags_get_type (void) {C_FLAGS (GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION), "Do not use viewfinder conversion " "elements", "no-viewfinder-conversion"}, + {C_FLAGS (GST_CAM_FLAG_NO_IMAGE_CONVERSION), "Do not use image conversion " + "elements", "no-image-conversion"}, {0, NULL, NULL} }; static volatile GType id = 0; @@ -1423,6 +1425,11 @@ gst_camera_bin_create_elements (GstCameraBin2 * camera) encbin_flags |= (1 << 1); g_object_set (camera->video_encodebin, "flags", encbin_flags, NULL); + /* image encodebin has only video branch so disable its conversion elements + * appropriately */ + if (camera->flags & GST_CAM_FLAG_NO_IMAGE_CONVERSION) + g_object_set (camera->image_encodebin, "flags", (1 << 1), NULL); + g_object_set (camera->viewfinderbin, "disable-converters", camera->flags & GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION ? TRUE : FALSE, NULL); diff --git a/gst/camerabin2/gstcamerabin2.h b/gst/camerabin2/gstcamerabin2.h index b2cf61f6ce..46113d0372 100644 --- a/gst/camerabin2/gstcamerabin2.h +++ b/gst/camerabin2/gstcamerabin2.h @@ -38,7 +38,10 @@ typedef enum /* matches GstEncFlags GST_ENC_FLAG_NO_VIDEO_CONVERSION in encodebin */ GST_CAM_FLAG_NO_VIDEO_CONVERSION = (1 << 1), /* maps to 'disable-converters' property in viewfinderbin */ - GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION = (1 << 2) + GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION = (1 << 2), + /* maps to GstEncFlags GST_ENC_FLAG_NO_VIDEO_CONVERSION in the image bin's + * encodebin */ + GST_CAM_FLAG_NO_IMAGE_CONVERSION = (1 << 3) } GstCamFlags; From cd35839e9868b86ba7988db1dd2aedc4c23af7ba Mon Sep 17 00:00:00 2001 From: David Schleef Date: Sun, 21 Aug 2011 20:15:25 -0700 Subject: [PATCH 50/85] vp8enc: fix drop-frame property Fixes #656929. --- ext/vp8/gstvp8enc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/vp8/gstvp8enc.c b/ext/vp8/gstvp8enc.c index f0d282f220..e832975644 100644 --- a/ext/vp8/gstvp8enc.c +++ b/ext/vp8/gstvp8enc.c @@ -563,7 +563,7 @@ gst_vp8_enc_set_property (GObject * object, guint prop_id, gst_vp8_enc->static_threshold = g_value_get_int (value); break; case PROP_DROP_FRAME: - gst_vp8_enc->drop_frame = g_value_get_boolean (value); + gst_vp8_enc->drop_frame = g_value_get_int (value); break; case PROP_RESIZE_ALLOWED: gst_vp8_enc->resize_allowed = g_value_get_boolean (value); @@ -652,7 +652,7 @@ gst_vp8_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_int (value, gst_vp8_enc->static_threshold); break; case PROP_DROP_FRAME: - g_value_set_boolean (value, gst_vp8_enc->drop_frame); + g_value_set_int (value, gst_vp8_enc->drop_frame); break; case PROP_RESIZE_ALLOWED: g_value_set_boolean (value, gst_vp8_enc->resize_allowed); From 8889c5927cbf02a6828f1b4978f9d5ae693c7844 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 1 Sep 2011 22:46:19 +0000 Subject: [PATCH 51/85] hlsdemux: remove unused argument --- gst/hls/gsthlsdemux.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index a24441c37f..e969c4fea7 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -107,10 +107,8 @@ static gboolean gst_hls_demux_start_update (GstHLSDemux * demux); static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux); static gboolean gst_hls_demux_schedule (GstHLSDemux * demux); static gboolean gst_hls_demux_switch_playlist (GstHLSDemux * demux); -static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux, - gboolean retry); -static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux, - gboolean retry); +static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux); +static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux); static void gst_hls_demux_reset (GstHLSDemux * demux, gboolean dispose); static gboolean gst_hls_demux_set_location (GstHLSDemux * demux, const gchar * uri); @@ -910,7 +908,7 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) /* update the playlist for live sources */ if (gst_m3u8_client_is_live (demux->client)) { - if (!gst_hls_demux_update_playlist (demux, TRUE)) { + if (!gst_hls_demux_update_playlist (demux)) { GST_ERROR_OBJECT (demux, "Could not update the playlist"); goto quit; } @@ -932,7 +930,7 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) /* fetch the next fragment */ if (g_queue_is_empty (demux->queue)) { - if (!gst_hls_demux_get_next_fragment (demux, TRUE)) { + if (!gst_hls_demux_get_next_fragment (demux)) { if (!demux->end_of_playlist && !demux->cancelled) GST_ERROR_OBJECT (demux, "Could not fetch the next fragment"); goto quit; @@ -989,7 +987,7 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) if (gst_m3u8_client_has_variant_playlist (demux->client)) { GstM3U8 *child = demux->client->main->current_variant->data; gst_m3u8_client_set_current (demux->client, child); - if (!gst_hls_demux_update_playlist (demux, FALSE)) { + if (!gst_hls_demux_update_playlist (demux)) { GST_ERROR_OBJECT (demux, "Could not fetch the child playlist %s", child->uri); return FALSE; @@ -1023,7 +1021,7 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) g_get_current_time (&demux->next_update); g_time_val_add (&demux->next_update, demux->client->current->targetduration * 1000000); - if (!gst_hls_demux_get_next_fragment (demux, FALSE)) { + if (!gst_hls_demux_get_next_fragment (demux)) { if (!demux->cancelled) GST_ERROR_OBJECT (demux, "Error caching the first fragments"); return FALSE; @@ -1117,7 +1115,7 @@ gst_hls_src_buf_to_utf8_playlist (gchar * data, guint size) } static gboolean -gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean retry) +gst_hls_demux_update_playlist (GstHLSDemux * demux) { const guint8 *data; gchar *playlist; @@ -1158,7 +1156,7 @@ gst_hls_demux_change_playlist (GstHLSDemux * demux, gboolean is_fast) demux->client->main->current_variant = list; gst_m3u8_client_set_current (demux->client, list->data); - gst_hls_demux_update_playlist (demux, TRUE); + gst_hls_demux_update_playlist (demux); GST_INFO_OBJECT (demux, "Client is %s, switching to bitrate %d", is_fast ? "fast" : "slow", demux->client->current->bandwidth); @@ -1244,7 +1242,7 @@ gst_hls_demux_switch_playlist (GstHLSDemux * demux) } static gboolean -gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean retry) +gst_hls_demux_get_next_fragment (GstHLSDemux * demux) { GstBuffer *buf; guint avail; From acacc251fa31ea5ea11bed84dbdabf3a5eb64f32 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Thu, 1 Sep 2011 23:18:51 +0000 Subject: [PATCH 52/85] hlsdemux: Allow up to 3 consecutive failed downloads before erroring In some networks, especiall in 3G, a fragment download or playlist update may fail. We allow for up to 3 consecutive failures, while using the rfc's specs for retry delays before considering that there was an error on the stream. --- gst/hls/gsthlsdemux.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index e969c4fea7..0a266e26a5 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -909,8 +909,16 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) /* update the playlist for live sources */ if (gst_m3u8_client_is_live (demux->client)) { if (!gst_hls_demux_update_playlist (demux)) { - GST_ERROR_OBJECT (demux, "Could not update the playlist"); - goto quit; + demux->client->update_failed_count++; + if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) { + GST_WARNING_OBJECT (demux, "Could not update the playlist"); + gst_hls_demux_schedule (demux); + continue; + } else { + GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, + ("Could not update the playlist"), (NULL)); + goto quit; + } } } @@ -931,10 +939,21 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) /* fetch the next fragment */ if (g_queue_is_empty (demux->queue)) { if (!gst_hls_demux_get_next_fragment (demux)) { - if (!demux->end_of_playlist && !demux->cancelled) - GST_ERROR_OBJECT (demux, "Could not fetch the next fragment"); - goto quit; + if (!demux->end_of_playlist && !demux->cancelled) { + demux->client->update_failed_count++; + if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) { + GST_WARNING_OBJECT (demux, "Could not fetch the next fragment"); + continue; + } else { + GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, + ("Could not fetch the next fragment"), (NULL)); + goto quit; + } + } + } else { + demux->client->update_failed_count = 0; } + /* try to switch to another bitrate if needed */ gst_hls_demux_switch_playlist (demux); } From 445f6260164809a05ae3af85249bfe8f5145e3a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 2 Sep 2011 14:54:56 +0200 Subject: [PATCH 53/85] hlsdemux: Set playlist parsing helper object to NULL after freeing --- gst/hls/gsthlsdemux.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 0a266e26a5..c05192f9b5 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -861,8 +861,10 @@ gst_hls_demux_reset (GstHLSDemux * demux, gboolean dispose) gst_adapter_clear (demux->download); - if (demux->client) + if (demux->client) { gst_m3u8_client_free (demux->client); + demux->client = NULL; + } if (!dispose) { demux->client = gst_m3u8_client_new (""); From 82bbc76f15ecbbf1f0bbde62570123748a5628f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 2 Sep 2011 14:55:45 +0200 Subject: [PATCH 54/85] hlsdemux: Reset the internal state going to/from READY instead of NULL This theoretically allows reuse of the element when setting it to READY instead of NULL. --- gst/hls/gsthlsdemux.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index c05192f9b5..4f30bc50ad 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -286,7 +286,8 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) GstHLSDemux *demux = GST_HLS_DEMUX (element); switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_READY: gst_hls_demux_reset (demux, FALSE); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: From 0561c3751c3fadaf13a12a197fcb23d7fb9a819b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 2 Sep 2011 15:00:58 +0200 Subject: [PATCH 55/85] hlsdemux: Remove obsolete FIXME comment --- gst/hls/m3u8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/hls/m3u8.c b/gst/hls/m3u8.c index f081764257..82e7df8d2a 100644 --- a/gst/hls/m3u8.c +++ b/gst/hls/m3u8.c @@ -225,7 +225,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated) title = NULL; data += 7; while (TRUE) { - end = g_utf8_strchr (data, -1, '\n'); /* FIXME: support \r\n */ + end = g_utf8_strchr (data, -1, '\n'); if (end) *end = '\0'; From 4ca79463fc9a40f9450d465980cc3d72937560a0 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 30 Aug 2011 14:50:41 -0300 Subject: [PATCH 56/85] docs: Build documentations for libraries --- configure.ac | 1 + docs/Makefile.am | 4 +- docs/libs/Makefile.am | 97 ++++++++++++++++++++ docs/libs/compiling.sgml | 48 ++++++++++ docs/libs/gst-plugins-bad-libs-docs.sgml | 41 +++++++++ docs/libs/gst-plugins-bad-libs-overrides.txt | 0 docs/libs/gst-plugins-bad-libs-sections.txt | 0 docs/libs/gst-plugins-bad-libs.types | 1 + 8 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 docs/libs/Makefile.am create mode 100644 docs/libs/compiling.sgml create mode 100644 docs/libs/gst-plugins-bad-libs-docs.sgml create mode 100644 docs/libs/gst-plugins-bad-libs-overrides.txt create mode 100644 docs/libs/gst-plugins-bad-libs-sections.txt create mode 100644 docs/libs/gst-plugins-bad-libs.types diff --git a/configure.ac b/configure.ac index 3450bd2824..5d90452546 100644 --- a/configure.ac +++ b/configure.ac @@ -2041,6 +2041,7 @@ ext/zbar/Makefile po/Makefile.in docs/Makefile docs/plugins/Makefile +docs/libs/Makefile docs/version.entities pkgconfig/Makefile pkgconfig/gstreamer-plugins-bad.pc diff --git a/docs/Makefile.am b/docs/Makefile.am index 7af47d43f1..a99c90f362 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -10,8 +10,8 @@ else GTK_DOC_DIRS = endif -SUBDIRS = $(GTK_DOC_DIRS) -DIST_SUBDIRS = plugins +SUBDIRS = libs $(GTK_DOC_DIRS) +DIST_SUBDIRS = libs plugins EXTRA_DIST = \ random/ChangeLog-0.8 \ diff --git a/docs/libs/Makefile.am b/docs/libs/Makefile.am new file mode 100644 index 0000000000..e8e43a8c20 --- /dev/null +++ b/docs/libs/Makefile.am @@ -0,0 +1,97 @@ +## Process this file with automake to produce Makefile.in + +# FIXME: fix the docs then remove this variable +DOCS_ARE_INCOMPLETE_PLEASE_FIXME=yespleasedo + +# The name of the module, e.g. 'glib'. +#DOC_MODULE=gst-plugins-libs-@GST_MAJORMINOR@ +DOC_MODULE=gst-plugins-bad-libs + +# for upload-doc.mak +DOC=gst-plugins-bad-libs +FORMATS=html +html: html-build.stamp +include $(top_srcdir)/common/upload-doc.mak + +# generated basefiles +#basefiles = \ +## $(DOC_MODULE).types \ +# $(DOC_MODULE)-sections.txt \ +# $(DOC_MODULE)-docs.sgml + +# ugly hack to make -unused.sgml work +#unused-build.stamp: +# BUILDDIR=`pwd` && \ +# cd $(srcdir)/tmpl && \ +# ln -sf gstreamer-libs-unused.sgml \ +# $$BUILDDIR/tmpl/gstreamer-libs-@GST_MAJORMINOR@-unused.sgml +# touch unused-build.stamp + +# these rules are added to create parallel docs using GST_MAJORMINOR +#$(basefiles): gstreamer-libs-@GST_MAJORMINOR@%: gstreamer-libs% +# cp $< $@ + +#CLEANFILES = $(basefiles) + +# The top-level SGML file. Change it if you want. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(top_srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting functions and macros. +DOC_SOURCE_DIR=$(top_srcdir)/gst-libs/gst +DOC_BUILD_DIR=$(top_builddir)/gst-libs/gst + +# Extra options to supply to gtkdoc-scan. +SCAN_OPTIONS=--deprecated-guards="GST_DISABLE_DEPRECATED" + +# FIXME : +# there's something wrong with gstreamer-sections.txt not being in the dist +# maybe it doesn't resolve; we're adding it below for now +#EXTRA_DIST = gstreamer.types.in gstreamer.hierarchy $(DOC_MODULE)-sections.txt gstreamer-sections.txt $(DOC_MAIN_SGML_FILE) + +# Extra options to supply to gtkdoc-mkdb. +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-fixref. +FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ + --extra-dir=$(GST_PREFIX)/share/gtk-doc/html + +# Used for dependencies. +HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.h +CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.c + +# this is a wingo addition +# thomasvs: another nice wingo addition would be an explanation on why +# this is useful ;) + +SCANOBJ_DEPS = \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la + +# Header files to ignore when scanning. +IGNORE_HFILES = + +# Images to copy into HTML directory. +HTML_IMAGES = + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +content_files = compiling.sgml + +# Other files to distribute. +extra_files = + +# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib +# contains GtkObjects/GObjects and you want to document signals and properties. +GTKDOC_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) +GTKDOC_LIBS = $(SCANOBJ_DEPS) $(GST_BASE_LIBS) $(GST_BAD_LIBS) + +GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC) +GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC) + +# If you need to override some of the declarations, place them in this file +# and uncomment this line. +#DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt +DOC_OVERRIDES = + +include $(top_srcdir)/common/gtk-doc.mak + diff --git a/docs/libs/compiling.sgml b/docs/libs/compiling.sgml new file mode 100644 index 0000000000..949209a068 --- /dev/null +++ b/docs/libs/compiling.sgml @@ -0,0 +1,48 @@ + + +%version-entities; +]> + + +Compiling +3 +GStreamer-Bad Library + + + +Compiling against the bad plugins libraries + +How to compile against the bad plugins libraries + + + + +Compiling against the bad plugins libraries + + +To compile against these libraries, you need to tell the compiler where to +find the header files and libraries. This is done with the +pkg-config utility. + + +The following interactive shell session demonstrates how +pkg-config is used: + +$ pkg-config --cflags gstreamer-plugins-bad-&GST_MAJORMINOR; +-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -pthread -I/usr/include/gstreamer-&GST_MAJORMINOR; -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/libxml2 +$ pkg-config --libs gstreamer-plugins-bad-&GST_MAJORMINOR; +-Wl,--export-dynamic -pthread -lgstreamer-&GST_MAJORMINOR; -lgobject-2.0 -lgmodule-2.0 -ldl -lgthread-2.0 -lxml2 -lpthread -lz -lm -lglib-2.0 + + + + +Note that, because of the number of libraries provided in this package, +the pkg-config information does not add -l flags itself +to choose the libraries to link to. You must add these yourself to select +which of the libraries you want to use. + + + + diff --git a/docs/libs/gst-plugins-bad-libs-docs.sgml b/docs/libs/gst-plugins-bad-libs-docs.sgml new file mode 100644 index 0000000000..49f0402bc9 --- /dev/null +++ b/docs/libs/gst-plugins-bad-libs-docs.sgml @@ -0,0 +1,41 @@ + + +%version-entities; +]> + + + GStreamer Bad Plugins &GST_MAJORMINOR; Library Reference Manual + + for GStreamer Bad Library &GST_MAJORMINOR; (&GST_VERSION;) + http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-bad-libs/html/. + + + + + GStreamer Bad Plugins Libraries + + This manual describes the libraries provided by the GStreamer Bad Plugins + package. + + + + + + Object Hierarchy + + + + + Index + + + + Index of deprecated API + + + + + + diff --git a/docs/libs/gst-plugins-bad-libs-overrides.txt b/docs/libs/gst-plugins-bad-libs-overrides.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/libs/gst-plugins-bad-libs.types b/docs/libs/gst-plugins-bad-libs.types new file mode 100644 index 0000000000..9f4950e70d --- /dev/null +++ b/docs/libs/gst-plugins-bad-libs.types @@ -0,0 +1 @@ +#include From c64b99339c56b504173b45748e0f93a741c68268 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 19 Jul 2011 13:52:01 +0200 Subject: [PATCH 57/85] codecparsers: mpeg: Add an mpeg video bitstream parsing library Create a new codecparsers library --- configure.ac | 1 + docs/libs/gst-plugins-bad-libs-docs.sgml | 10 + docs/libs/gst-plugins-bad-libs-sections.txt | 28 + docs/libs/gst-plugins-bad-libs.types | 2 + gst-libs/gst/Makefile.am | 4 +- gst-libs/gst/codecparsers/Makefile.am | 29 + .../gst/codecparsers/gstmpegvideoparser.c | 749 ++++++++++++++++++ .../gst/codecparsers/gstmpegvideoparser.h | 365 +++++++++ gst-plugins-bad.spec.in | 3 + pkgconfig/gstreamer-plugins-bad.pc.in | 3 +- tests/check/Makefile.am | 10 + tests/check/libs/mpegvideoparser.c | 208 +++++ 12 files changed, 1409 insertions(+), 3 deletions(-) create mode 100644 gst-libs/gst/codecparsers/Makefile.am create mode 100644 gst-libs/gst/codecparsers/gstmpegvideoparser.c create mode 100644 gst-libs/gst/codecparsers/gstmpegvideoparser.h create mode 100644 tests/check/libs/mpegvideoparser.c diff --git a/configure.ac b/configure.ac index 5d90452546..17ea3b96b1 100644 --- a/configure.ac +++ b/configure.ac @@ -1946,6 +1946,7 @@ gst-libs/gst/Makefile gst-libs/gst/basecamerabinsrc/Makefile gst-libs/gst/interfaces/Makefile gst-libs/gst/signalprocessor/Makefile +gst-libs/gst/codecparsers/Makefile gst-libs/gst/video/Makefile sys/Makefile sys/dshowdecwrapper/Makefile diff --git a/docs/libs/gst-plugins-bad-libs-docs.sgml b/docs/libs/gst-plugins-bad-libs-docs.sgml index 49f0402bc9..d1113bca3c 100644 --- a/docs/libs/gst-plugins-bad-libs-docs.sgml +++ b/docs/libs/gst-plugins-bad-libs-docs.sgml @@ -20,6 +20,16 @@ package. + + + Bitstream parsing Library + + This library should be linked to by getting cflags and libs from + gstreamer-plugins-bad-&GST_MAJORMINOR;.pc and adding + -lgscodeparsers-&GST_MAJORMINOR; to the library flags. + + + diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt index e69de29bb2..a09929c04f 100644 --- a/docs/libs/gst-plugins-bad-libs-sections.txt +++ b/docs/libs/gst-plugins-bad-libs-sections.txt @@ -0,0 +1,28 @@ +# codecparsers +
+gstmpegvideoparser +mpegvideoparser +gst/codecparsers/gstmpegvideoparser.h +GstMpegVideoPacketTypeCode +GstMpegVideoPacketExtensionCode +GstMpegVideoLevel +GstMpegVideoProfile +GstMpegVideoPictureType +GstMpegVideoPictureStructure +GstMpegVideoSequenceHdr +GstMpegVideoSequenceExt +GstMpegVideoPictureHdr +GstMpegVideoGop +GstMpegVideoPictureExt +GstMpegVideoQuantMatrixExt +GstMpegVideoTypeOffsetSize +gst_mpeg_video_parse +gst_mpeg_video_parse_sequence_header +gst_mpeg_video_parse_picture_header +gst_mpeg_video_parse_picture_extension +gst_mpeg_video_parse_gop +gst_mpeg_video_parse_sequence_extension +gst_mpeg_video_parse_quant_matrix_extension + + +
diff --git a/docs/libs/gst-plugins-bad-libs.types b/docs/libs/gst-plugins-bad-libs.types index 9f4950e70d..4ca1a2b462 100644 --- a/docs/libs/gst-plugins-bad-libs.types +++ b/docs/libs/gst-plugins-bad-libs.types @@ -1 +1,3 @@ #include + +#include diff --git a/gst-libs/gst/Makefile.am b/gst-libs/gst/Makefile.am index ac83c681fe..130956ee4c 100644 --- a/gst-libs/gst/Makefile.am +++ b/gst-libs/gst/Makefile.am @@ -1,6 +1,6 @@ -SUBDIRS = interfaces signalprocessor video basecamerabinsrc +SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers noinst_HEADERS = gst-i18n-plugin.h gettext.h -DIST_SUBDIRS = interfaces signalprocessor video basecamerabinsrc +DIST_SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers diff --git a/gst-libs/gst/codecparsers/Makefile.am b/gst-libs/gst/codecparsers/Makefile.am new file mode 100644 index 0000000000..86a7b16d9f --- /dev/null +++ b/gst-libs/gst/codecparsers/Makefile.am @@ -0,0 +1,29 @@ +lib_LTLIBRARIES = libgstcodecparsers-@GST_MAJORMINOR@.la + +libgstcodecparsers_@GST_MAJORMINOR@_la_SOURCES = gstmpegvideoparser.c + +libgstcodecparsers_@GST_MAJORMINOR@includedir = \ + $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/codecparsers + +libgstcodecparsers_@GST_MAJORMINOR@include_HEADERS = \ + gstmpegvideoparser.h + +libgstcodecparsers_@GST_MAJORMINOR@_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_CFLAGS) +libgstcodecparsers_@GST_MAJORMINOR@_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) +libgstcodecparsers_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS) + +Android.mk: $(BUILT_SOURCES) Makefile.am + androgenizer -:PROJECT libgstcodecparsers -:STATIC libgstcodecparsers-@GST_MAJORMINOR@ \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstcodecparsers_@GST_MAJORMINOR@_la_SOURCES) \ + $(built_sources) \ + -:CFLAGS $(DEFS) $(libgstcodecparsers_@GST_MAJORMINOR@_la_CFLAGS) \ + -:LDFLAGS $(libgstcodecparsers_@GST_MAJORMINOR@_la_LDFLAGS) \ + $(libgstcodecparsers@GST_MAJORMINOR@_la_LIBADD) \ + -ldl \ + -:HEADER_TARGET gstreamer-@GST_MAJORMINOR@/gst/codecparsers \ + -:HEADERS $(libgstcodecparsersinclude_HEADERS) \ + $(built_headers) \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + > $@ diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.c b/gst-libs/gst/codecparsers/gstmpegvideoparser.c new file mode 100644 index 0000000000..be34e5f99b --- /dev/null +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.c @@ -0,0 +1,749 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * From bad/sys/vdpau/mpeg/mpegutil.c: + * Copyright (C) <2007> Jan Schmidt + * Copyright (C) <2009> Carl-Anton Ingmarsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gstmpegvideoparser + * @short_description: Convenience library for mpeg1 and 2 video + * bitstream parsing. + * + * + * + * Provides useful functions for mpeg videos bitstream parsing. + * + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstmpegvideoparser.h" + +#include +#include +#include + +#define MARKER_BIT 0x1 + +#define GET_BITS(b, num, bits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint32(b, bits, num)) \ + goto failed; \ + GST_TRACE ("parsed %d bits: %d", num, *(bits)); \ +} G_STMT_END + +#define READ_UINT8(br, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint8 (br, &val, nbits)) { \ + GST_WARNING ("failed to read uint8, nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END + +#define READ_UINT16(br, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint16 (br, &val, nbits)) { \ + GST_WARNING ("failed to read uint16, nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END + +#define READ_UINT32(br, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint32 (br, &val, nbits)) { \ + GST_WARNING ("failed to read uint32, nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END + +/* default intra quant matrix, in zig-zag order */ +const guint8 default_intra_quantizer_matrix[64] = { + 8, + 16, 16, + 19, 16, 19, + 22, 22, 22, 22, + 22, 22, 26, 24, 26, + 27, 27, 27, 26, 26, 26, + 26, 27, 27, 27, 29, 29, 29, + 34, 34, 34, 29, 29, 29, 27, 27, + 29, 29, 32, 32, 34, 34, 37, + 38, 37, 35, 35, 34, 35, + 38, 38, 40, 40, 40, + 48, 48, 46, 46, + 56, 56, 58, + 69, 69, + 83 +}; + +const guint8 mpeg_zigzag_8x8[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +GST_DEBUG_CATEGORY (mpegvideo_parser_debug); +#define GST_CAT_DEFAULT mpegvideo_parser_debug + +static gboolean initialized = FALSE; + +static inline gboolean +find_start_code (GstBitReader * b) +{ + guint32 bits; + + /* 0 bits until byte aligned */ + while (b->bit != 0) { + GET_BITS (b, 1, &bits); + } + + /* 0 bytes until startcode */ + while (gst_bit_reader_peek_bits_uint32 (b, &bits, 32)) { + if (bits >> 8 == 0x1) { + return TRUE; + } else { + gst_bit_reader_skip (b, 8); + } + } + + return FALSE; + +failed: + return FALSE; +} + +/* Set the Pixel Aspect Ratio in our hdr from a DAR code in the data */ +static void +set_par_from_dar (GstMpegVideoSequenceHdr * seqhdr, guint8 asr_code) +{ + /* Pixel_width = DAR_width * display_vertical_size */ + /* Pixel_height = DAR_height * display_horizontal_size */ + switch (asr_code) { + case 0x02: /* 3:4 DAR = 4:3 pixels */ + seqhdr->par_w = 4 * seqhdr->height; + seqhdr->par_h = 3 * seqhdr->width; + break; + case 0x03: /* 9:16 DAR */ + seqhdr->par_w = 16 * seqhdr->height; + seqhdr->par_h = 9 * seqhdr->width; + break; + case 0x04: /* 1:2.21 DAR */ + seqhdr->par_w = 221 * seqhdr->height; + seqhdr->par_h = 100 * seqhdr->width; + break; + case 0x01: /* Square pixels */ + seqhdr->par_w = seqhdr->par_h = 1; + break; + default: + GST_DEBUG ("unknown/invalid aspect_ratio_information %d", asr_code); + break; + } +} + +static void +set_fps_from_code (GstMpegVideoSequenceHdr * seqhdr, guint8 fps_code) +{ + const gint framerates[][2] = { + {30, 1}, {24000, 1001}, {24, 1}, {25, 1}, + {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001}, + {60, 1}, {30, 1} + }; + + if (fps_code && fps_code < 10) { + seqhdr->fps_n = framerates[fps_code][0]; + seqhdr->fps_d = framerates[fps_code][1]; + } else { + GST_DEBUG ("unknown/invalid frame_rate_code %d", fps_code); + /* Force a valid framerate */ + /* FIXME or should this be kept unknown ?? */ + seqhdr->fps_n = 30000; + seqhdr->fps_d = 1001; + } +} + +static gboolean +gst_mpeg_video_parse_sequence (GstMpegVideoSequenceHdr * seqhdr, + GstBitReader * br) +{ + guint8 bits; + guint8 load_intra_flag, load_non_intra_flag; + + /* Setting the height/width codes */ + READ_UINT16 (br, seqhdr->width, 12); + READ_UINT16 (br, seqhdr->height, 12); + + READ_UINT8 (br, seqhdr->aspect_ratio_info, 4); + set_par_from_dar (seqhdr, seqhdr->aspect_ratio_info); + + READ_UINT8 (br, seqhdr->frame_rate_code, 4); + set_fps_from_code (seqhdr, seqhdr->frame_rate_code); + + READ_UINT32 (br, seqhdr->bitrate_value, 18); + if (seqhdr->bitrate_value == 0x3ffff) { + /* VBR stream */ + seqhdr->bitrate = 0; + } else { + /* Value in header is in units of 400 bps */ + seqhdr->bitrate *= 400; + } + + READ_UINT8 (br, bits, 1); + if (bits != MARKER_BIT) + goto failed; + + /* VBV buffer size */ + READ_UINT16 (br, seqhdr->vbv_buffer_size_value, 10); + + /* constrained_parameters_flag */ + READ_UINT8 (br, seqhdr->constrained_parameters_flag, 1); + + /* load_intra_quantiser_matrix */ + READ_UINT8 (br, load_intra_flag, 1); + if (load_intra_flag) { + gint i; + for (i = 0; i < 64; i++) + READ_UINT8 (br, seqhdr->intra_quantizer_matrix[mpeg_zigzag_8x8[i]], 8); + } else + memcpy (seqhdr->intra_quantizer_matrix, default_intra_quantizer_matrix, 64); + + /* non intra quantizer matrix */ + READ_UINT8 (br, load_non_intra_flag, 1); + if (load_non_intra_flag) { + gint i; + for (i = 0; i < 64; i++) + READ_UINT8 (br, seqhdr->non_intra_quantizer_matrix[mpeg_zigzag_8x8[i]], + 8); + } else + memset (seqhdr->non_intra_quantizer_matrix, 16, 64); + + /* dump some info */ + GST_LOG ("width x height: %d x %d", seqhdr->width, seqhdr->height); + GST_LOG ("fps: %d/%d", seqhdr->fps_n, seqhdr->fps_d); + GST_LOG ("par: %d/%d", seqhdr->par_w, seqhdr->par_h); + GST_LOG ("bitrate: %d", seqhdr->bitrate); + + return TRUE; + + /* ERRORS */ +failed: + { + GST_WARNING ("Failed to parse sequence header"); + /* clear out stuff */ + memset (seqhdr, 0, sizeof (*seqhdr)); + return FALSE; + } +} + +static inline guint +scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) +{ + const guint8 *data; + guint32 state; + guint i; + + g_return_val_if_fail (size > 0, -1); + g_return_val_if_fail ((guint64) offset + size <= reader->size - reader->byte, + -1); + + /* we can't find the pattern with less than 4 bytes */ + if (G_UNLIKELY (size < 4)) + return -1; + + data = reader->data + reader->byte + offset; + + /* set the state to something that does not match */ + state = 0xffffffff; + + /* now find data */ + for (i = 0; i < size; i++) { + /* throw away one byte and move in the next byte */ + state = ((state << 8) | data[i]); + if (G_UNLIKELY ((state & 0xffffff00) == 0x00000100)) { + /* we have a match but we need to have skipped at + * least 4 bytes to fill the state. */ + if (G_LIKELY (i >= 3)) + return offset + i - 3; + } + + /* TODO: reimplement making 010001 not detected as a sc + * Accelerate search for start code + * if (data[i] > 1) { + * while (i < (size - 4) && data[i] > 1) { + * if (data[i + 3] > 1) + * i += 4; + * else + * i += 1; + * } + * state = 0x00000100; + *} + */ + } + + /* nothing found */ + return -1; +} + +/****** API *******/ + +/** + * gst_mpeg_video_parse: + * @data: The datas from which to parse + * @size: The size of @data + * @offset: The offset from which to start the parsing + * + * Parses @data, and detects the different packets types, offset, + * and size, starting from @offset + * + * Returns: a #GList of #GstMpegVideoTypeOffsetSize + */ +GList * +gst_mpeg_video_parse (guint8 * data, gsize size, guint offset) +{ + gint off, rsize; + GstByteReader br; + GList *ret = NULL; + size = size - offset; + + if (!initialized) { + GST_DEBUG_CATEGORY_INIT (mpegvideo_parser_debug, "codecparsers_mpegvideo", + 0, "Mpegvideo parser library"); + initialized = TRUE; + } + + if (size <= 0) { + GST_DEBUG ("Can't parse from offset %d, buffer is to small", offset); + return NULL; + } + + gst_byte_reader_init (&br, &data[offset], size); + + off = scan_for_start_codes (&br, 0, size); + + if (off < 0) { + GST_DEBUG ("No start code prefix in this buffer"); + return NULL; + } + + while (off >= 0 && off + 3 < size) { + GstMpegVideoTypeOffsetSize *codoffsize; + + gst_byte_reader_skip (&br, off + 3); + + codoffsize = g_malloc (sizeof (GstMpegVideoTypeOffsetSize)); + gst_byte_reader_get_uint8 (&br, &codoffsize->type); + + codoffsize->offset = gst_byte_reader_get_pos (&br) + offset; + + rsize = gst_byte_reader_get_remaining (&br); + if (rsize <= 0) + break; + + off = scan_for_start_codes (&br, 0, rsize); + + codoffsize->size = off; + + ret = g_list_prepend (ret, codoffsize); + codoffsize = ret->data; + } + + return g_list_reverse (ret); +} + +/** + * gst_mpeg_video_parse_sequence_header: + * @seqhdr: The #GstMpegVideoSequenceHdr to set + * @data: The datas from which to parse the seqhdr + * @size: The size of @data + * @offset: The offset in byte from which to start parsing @data + * + * Sets the @seqhdr Mpeg Video Sequence Header structure members from @data + * + * Returns: %TRUE if the seqhdr could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_sequence_header (GstMpegVideoSequenceHdr * seqhdr, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size - offset < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + return gst_mpeg_video_parse_sequence (seqhdr, &br); +} + +/** + * gst_mpeg_video_parse_sequence_extension: + * @seqhdr: The #GstMpegVideoSequenceExt to set + * @data: The datas from which to parse the seqext + * @size: The size of @data + * @offset: The offset in byte from which to start parsing @data + * + * Sets the @seqext Mpeg Video Sequence Extension structure members from @data + * + * Returns: %TRUE if the seqext could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_sequence_extension (GstMpegVideoSequenceExt * seqext, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 6) { + GST_DEBUG ("not enough bytes to parse the extension"); + return FALSE; + } + + gst_bit_reader_init (&br, &data[offset], size); + + if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != + GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE) { + GST_DEBUG ("Not parsing a sequence extension"); + return FALSE; + } + + /* skip profile and level escape bit */ + gst_bit_reader_skip_unchecked (&br, 1); + + seqext->profile = gst_bit_reader_get_bits_uint8_unchecked (&br, 3); + seqext->level = gst_bit_reader_get_bits_uint8_unchecked (&br, 4); + + /* progressive */ + seqext->progressive = gst_bit_reader_get_bits_uint8_unchecked (&br, 1); + + /* chroma format */ + seqext->chroma_format = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + + /* resolution extension */ + seqext->horiz_size_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + seqext->vert_size_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + + seqext->bitrate_ext = gst_bit_reader_get_bits_uint16_unchecked (&br, 12); + + /* skip marker bits */ + gst_bit_reader_skip_unchecked (&br, 1); + + seqext->vbv_buffer_size_extension = + gst_bit_reader_get_bits_uint8_unchecked (&br, 8); + seqext->low_delay = gst_bit_reader_get_bits_uint8_unchecked (&br, 1); + + /* framerate extension */ + seqext->fps_n_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + seqext->fps_d_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + + return TRUE; +} + +/** + * gst_mpeg_video_parse_quant_matrix_extension: + * @ext: The #GstMpegVideoQuantMatrixExt to set + * @data: The datas from which to parse @quant + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * Sets the @quant Mpeg Video Quant Matrix Extension structure members from + * @data + * + * Returns: %TRUE if the quant matrix extension could be parsed correctly, + * %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_quant_matrix_extension (GstMpegVideoQuantMatrixExt * quant, + guint8 * data, gsize size, guint offset) +{ + guint8 i; + GstBitReader br; + + size = size - offset; + + if (size < 1) { + GST_DEBUG ("not enough bytes to parse the extension"); + return FALSE; + } + + gst_bit_reader_init (&br, &data[offset], size); + + if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != + GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX) { + GST_DEBUG ("Not parsing a quant matrix extension"); + return FALSE; + } + + READ_UINT8 (&br, quant->load_intra_quantiser_matrix, 1); + if (quant->load_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, quant->intra_quantiser_matrix[mpeg_zigzag_8x8[i]], 8); + } + } + + READ_UINT8 (&br, quant->load_non_intra_quantiser_matrix, 1); + if (quant->load_non_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, quant->non_intra_quantiser_matrix[mpeg_zigzag_8x8[i]], + 8); + } + } + + READ_UINT8 (&br, quant->load_chroma_intra_quantiser_matrix, 1); + if (quant->load_non_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, quant->chroma_intra_quantiser_matrix[mpeg_zigzag_8x8[i]], + 8); + } + } + + READ_UINT8 (&br, quant->load_chroma_non_intra_quantiser_matrix, 1); + if (quant->load_chroma_non_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, + quant->chroma_non_intra_quantiser_matrix[mpeg_zigzag_8x8[i]], 8); + } + } + + return TRUE; + +failed: + GST_WARNING ("error parsing \"Quant Matrix Extension\""); + return FALSE; +} + +/** + * gst_mpeg_video_parse_picture_extension: + * @ext: The #GstMpegVideoPictureExt to set + * @data: The datas from which to parse the ext + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * Sets the @ext Mpeg Video Picture Extension structure members from @data + * + * Returns: %TRUE if the picture extension could be parsed correctly, + * %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_picture_extension (GstMpegVideoPictureExt * ext, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + /* f_code */ + READ_UINT8 (&br, ext->f_code[0][0], 4); + READ_UINT8 (&br, ext->f_code[0][1], 4); + READ_UINT8 (&br, ext->f_code[1][0], 4); + READ_UINT8 (&br, ext->f_code[1][1], 4); + + /* intra DC precision */ + READ_UINT8 (&br, ext->intra_dc_precision, 2); + + /* picture structure */ + READ_UINT8 (&br, ext->picture_structure, 2); + + /* top field first */ + READ_UINT8 (&br, ext->top_field_first, 1); + + /* frame pred frame dct */ + READ_UINT8 (&br, ext->frame_pred_frame_dct, 1); + + /* concealment motion vectors */ + READ_UINT8 (&br, ext->concealment_motion_vectors, 1); + + /* q scale type */ + READ_UINT8 (&br, ext->q_scale_type, 1); + + /* intra vlc format */ + READ_UINT8 (&br, ext->intra_vlc_format, 1); + + /* alternate scan */ + READ_UINT8 (&br, ext->alternate_scan, 1); + + /* repeat first field */ + READ_UINT8 (&br, ext->repeat_first_field, 1); + + /* chroma_420_type */ + READ_UINT8 (&br, ext->chroma_420_type, 1); + + /* progressive_frame */ + READ_UINT8 (&br, ext->progressive_frame, 1); + + /* composite display */ + READ_UINT8 (&br, ext->composite_display, 1); + + if (ext->composite_display) { + + /* v axis */ + READ_UINT8 (&br, ext->v_axis, 1); + + /* field sequence */ + READ_UINT8 (&br, ext->field_sequence, 3); + + /* sub carrier */ + READ_UINT8 (&br, ext->sub_carrier, 1); + + /* burst amplitude */ + READ_UINT8 (&br, ext->burst_amplitude, 7); + + /* sub_carrier phase */ + READ_UINT8 (&br, ext->sub_carrier_phase, 8); + } + + return TRUE; + +failed: + GST_WARNING ("error parsing \"Picture Coding Extension\""); + return FALSE; + +} + +/** + * gst_mpeg_video_parse_picture_header: + * @hdr: The #GstMpegVideoPictureHdr to set + * @data: The datas from which to parse the hdr + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * Sets the @hdr Mpeg Video Picture Header structure members from @data + * + * Returns: %TRUE if the picture sequence could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_picture_header (GstMpegVideoPictureHdr * hdr, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + /* temperal sequence number */ + if (!gst_bit_reader_get_bits_uint16 (&br, &hdr->tsn, 10)) + return FALSE; + + /* frame type */ + if (!gst_bit_reader_get_bits_uint8 (&br, (guint8 *) & hdr->pic_type, 3)) + return FALSE; + + if (hdr->pic_type == 0 || hdr->pic_type > 4) + return FALSE; /* Corrupted picture packet */ + + /* skype VBV delay */ + if (!gst_bit_reader_skip (&br, 8)) + return FALSE; + + if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_P + || hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_B) { + + READ_UINT8 (&br, hdr->full_pel_forward_vector, 1); + + READ_UINT8 (&br, hdr->f_code[0][0], 3); + hdr->f_code[0][1] = hdr->f_code[0][0]; + } else { + hdr->full_pel_forward_vector = 0; + hdr->f_code[0][0] = hdr->f_code[0][1] = 0; + } + + if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_B) { + READ_UINT8 (&br, hdr->full_pel_backward_vector, 1); + + READ_UINT8 (&br, hdr->f_code[1][0], 3); + hdr->f_code[1][1] = hdr->f_code[1][0]; + } else { + hdr->full_pel_backward_vector = 0; + hdr->f_code[1][0] = hdr->f_code[1][1] = 0; + } + + return TRUE; + +failed: + { + GST_WARNING ("Failed to parse sequence extension"); + return FALSE; + } +} + +/** + * gst_mpeg_video_parse_gop: + * @gop: The #GstMpegVideoGop to set + * @data: The datas from which to parse the gop + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * + * Sets the @gop Mpeg Video Group of Picture structure members from @data + * + * Returns: %TRUE if the gop could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_gop (GstMpegVideoGop * gop, guint8 * data, + gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + READ_UINT8 (&br, gop->drop_frame_flag, 1); + + READ_UINT8 (&br, gop->hour, 5); + + READ_UINT8 (&br, gop->minute, 6); + + /* skip unused bit */ + if (!gst_bit_reader_skip (&br, 1)) + return FALSE; + + READ_UINT8 (&br, gop->second, 6); + + READ_UINT8 (&br, gop->frame, 6); + + READ_UINT8 (&br, gop->closed_gop, 1); + + READ_UINT8 (&br, gop->broken_gop, 1); + + return TRUE; + +failed: + GST_WARNING ("error parsing \"GOP\""); + return FALSE; +} diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.h b/gst-libs/gst/codecparsers/gstmpegvideoparser.h new file mode 100644 index 0000000000..8d034affcd --- /dev/null +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.h @@ -0,0 +1,365 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * From bad/sys/vdpau/mpeg/mpegutil.c: + * Copyright (C) <2007> Jan Schmidt + * Copyright (C) <2009> Carl-Anton Ingmarsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_MPEG_VIDEO_UTILS_H__ +#define __GST_MPEG_VIDEO_UTILS_H__ + +#include + +G_BEGIN_DECLS + +/** + * GstMpegVideoPacketTypeCode: + * @GST_MPEG_VIDEO_PACKET_PICTURE: Picture packet starting code + * @GST_MPEG_VIDEO_PACKET_SLICE_MIN: Picture packet starting code + * @GST_MPEG_VIDEO_PACKET_SLICE_MAX: Slice max packet starting code + * @GST_MPEG_VIDEO_PACKET_SEQUENCE : Sequence packet starting code + * @GST_MPEG_VIDEO_PACKET_EXTENSION: Extension packet starting code + * @GST_MPEG_VIDEO_PACKET_SEQUENCE_END: Sequence end packet code + * @GST_MPEG_VIDEO_PACKET_GOP: Group of Picture packet starting code + * @GST_MPEG_VIDEO_PACKET_NONE: None packet code + * + * Indicates the type of MPEG packet + */ +typedef enum { + GST_MPEG_VIDEO_PACKET_PICTURE = 0x00, + GST_MPEG_VIDEO_PACKET_SLICE_MIN = 0x01, + GST_MPEG_VIDEO_PACKET_SLICE_MAX = 0xaf, + GST_MPEG_VIDEO_PACKET_SEQUENCE = 0xb3, + GST_MPEG_VIDEO_PACKET_EXTENSION = 0xb5, + GST_MPEG_VIDEO_PACKET_SEQUENCE_END = 0xb7, + GST_MPEG_VIDEO_PACKET_GOP = 0xb8, + GST_MPEG_VIDEO_PACKET_NONE = 0xff +} GstMpegVideoPacketTypeCode; + +/** + * GstMpegVideoPacketExtensionCode: + * @GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE: Sequence extension code + * @GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY: Display extension code + * @GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX: Quantizer extension code + * @GST_MPEG_VIDEO_PACKET_EXT_GOP: Group Of Picture extension code + * + * Indicates what type of packets are in this + * block, some are mutually * exclusive though - ie, sequence packs are + * accumulated separately. GOP & Picture may occur together or separately + */ +typedef enum { + GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE = 0x01, + GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY = 0x02, + GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX = 0x03, + GST_MPEG_VIDEO_PACKET_EXT_GOP = 0x04 +} GstMpegVideoPacketExtensionCode; + +/** + * GstMpegVideoLevel: + * @GST_MPEG_VIDEO_LEVEL_LOW + * @GST_MPEG_VIDEO_LEVEL_MAIN + * @GST_MPEG_VIDEO_LEVEL_HIGH_1440 + * @GST_MPEG_VIDEO_LEVEL_HIGH + * + * Indicates the level in use + **/ +typedef enum { + GST_MPEG_VIDEO_LEVEL_HIGH = 0x04, + GST_MPEG_VIDEO_LEVEL_HIGH_1440 = 0x06, + GST_MPEG_VIDEO_LEVEL_MAIN = 0x08, + GST_MPEG_VIDEO_LEVEL_LOW = 0x0a +} GstMpegVideoLevel; + +/** + * GstMpegVideoProfile: + * @GST_MPEG_VIDEO_PROFILE_422, + * @GST_MPEG_VIDEO_PROFILE_HIGH, + * @GST_MPEG_VIDEO_PROFILE_SPATIALLY_SCALABLE, + * @GST_MPEG_VIDEO_PROFILE_SNR_SCALABLE, + * @GST_MPEG_VIDEO_PROFILE_MAIN, + * @GST_MPEG_VIDEO_PROFILE_SIMPLE, + * + * Indicates the profile type in use + **/ +typedef enum { + GST_MPEG_VIDEO_PROFILE_422 = 0x00, + GST_MPEG_VIDEO_PROFILE_HIGH = 0x01, + GST_MPEG_VIDEO_PROFILE_SPATIALLY_SCALABLE = 0x02, + GST_MPEG_VIDEO_PROFILE_SNR_SCALABLE = 0x03, + GST_MPEG_VIDEO_PROFILE_MAIN = 0x04, + GST_MPEG_VIDEO_PROFILE_SIMPLE = 0x05 +} GstMpegVideoProfile; + +/** + * GstMpegVideoPictureType: + * @GST_MPEG_VIDEO_PICTURE_TYPE_I: Type I + * @GST_MPEG_VIDEO_PICTURE_TYPE_P: Type P + * @GST_MPEG_VIDEO_PICTURE_TYPE_B: Type B + * @GST_MPEG_VIDEO_PICTURE_TYPE_D: Type D + * + * Indicates the type of picture + */ +typedef enum { + GST_MPEG_VIDEO_PICTURE_TYPE_I = 0x01, + GST_MPEG_VIDEO_PICTURE_TYPE_P = 0x02, + GST_MPEG_VIDEO_PICTURE_TYPE_B = 0x03, + GST_MPEG_VIDEO_PICTURE_TYPE_D = 0x04 +} GstMpegVideoPictureType; + +/** + * GstMpegVideoPictureStructure: + * @GST_MPEG_VIDEO_PICTURE_STRUCTURE_TOP_FIELD: Top field + * @GST_MPEG_VIDEO_PICTURE_STRUCTURE_BOTTOM_FIELD: Bottom field + * @GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME: Frame + * + * Indicates the structure of picture + */ +typedef enum { + GST_MPEG_VIDEO_PICTURE_STRUCTURE_TOP_FIELD = 0x01, + GST_MPEG_VIDEO_PICTURE_STRUCTURE_BOTTOM_FIELD = 0x02, + GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME = 0x03 +} GstMpegVideoPictureStructure; + +typedef struct _GstMpegVideoSequenceHdr GstMpegVideoSequenceHdr; +typedef struct _GstMpegVideoSequenceExt GstMpegVideoSequenceExt; +typedef struct _GstMpegVideoPictureHdr GstMpegVideoPictureHdr; +typedef struct _GstMpegVideoGop GstMpegVideoGop; +typedef struct _GstMpegVideoPictureExt GstMpegVideoPictureExt; +typedef struct _GstMpegVideoQuantMatrixExt GstMpegVideoQuantMatrixExt; +typedef struct _GstMpegVideoTypeOffsetSize GstMpegVideoTypeOffsetSize; + +/** + * GstMpegVideoSequenceHdr: + * @width: Width of each frame + * @height: Height of each frame + * @par_w: Calculated Pixel Aspect Ratio width + * @par_h: Pixel Aspect Ratio height + * @fps_n: Calculated Framrate nominator + * @fps_d: Calculated Framerate denominator + * @bitrate_value: Value of the bitrate as is in the stream (400bps unit) + * @bitrate: the real bitrate of the Mpeg video stream in bits per second, 0 if VBR stream + * @constrained_parameters_flag: %TRUE if this stream uses contrained parameters. + * @intra_quantizer_matrix: intra-quantization table + * @non_intra_quantizer_matrix: non-intra quantization table + * + * The Mpeg2 Video Sequence Header structure. + */ +struct _GstMpegVideoSequenceHdr +{ + guint16 width, height; + guint8 aspect_ratio_info; + guint8 frame_rate_code; + guint32 bitrate_value; + guint16 vbv_buffer_size_value; + + guint8 constrained_parameters_flag; + + guint8 intra_quantizer_matrix[64]; + guint8 non_intra_quantizer_matrix[64]; + + /* Calculated values */ + guint par_w, par_h; + guint fps_n, fps_d; + guint bitrate; +}; + +/** + * GstMpegVideoSequenceExt: + * @profile: mpeg2 decoder profil + * @level: mpeg2 decoder level + * @progressive: %TRUE if the frames are progressive %FALSE otherwize + * @chroma_format: indicates the chrominance format + * @horiz_size_ext: Horizontal size + * @vert_size_ext: Vertical size + * @bitrate_ext: The bitrate + * @vbv_buffer_size_extension: Vbv vuffer size + * @low_delay: %TRUE if the sequence doesn't contain any B-pitcture, %FALSE + * otherwize + * @fps_n_ext: Framerate nominator code + * @fps_d_ext: Framerate denominator code + * + * The Mpeg2 Video Sequence Extension structure. + **/ +struct _GstMpegVideoSequenceExt +{ + /* mpeg2 decoder profile */ + guint8 profile; + /* mpeg2 decoder level */ + guint8 level; + + guint8 progressive; + guint8 chroma_format; + + guint8 horiz_size_ext, vert_size_ext; + + guint16 bitrate_ext; + guint8 vbv_buffer_size_extension; + guint8 low_delay; + guint8 fps_n_ext, fps_d_ext; + +}; + +/** + * GstMpegVideoQuantMatrixExt: + * @load_intra_quantiser_matrix + * @intra_quantiser_matrix + * @load_non_intra_quantiser_matrix + * @non_intra_quantiser_matrix: + * @load_chroma_intra_quantiser_matrix + * @chroma_intra_quantiser_matrix + * @load_chroma_non_intra_quantiser_matrix + * @chroma_non_intra_quantiser_matrix + * + * The Quant Matrix Extension structure + */ +struct _GstMpegVideoQuantMatrixExt +{ + guint8 load_intra_quantiser_matrix; + guint8 intra_quantiser_matrix[64]; + guint8 load_non_intra_quantiser_matrix; + guint8 non_intra_quantiser_matrix[64]; + guint8 load_chroma_intra_quantiser_matrix; + guint8 chroma_intra_quantiser_matrix[64]; + guint8 load_chroma_non_intra_quantiser_matrix; + guint8 chroma_non_intra_quantiser_matrix[64]; +}; + +/** + * GstMpegVideoPictureHdr: + * @tsn: Temporal Sequence Number + * @pic_type: Type of the frame + * @full_pel_forward_vector: the full pel forward flag of + * the frame: 0 or 1. + * @full_pel_backward_vector: the full pel backward flag + * of the frame: 0 or 1. + * @f_code: F code + * + * The Mpeg2 Video Picture Header structure. + */ +struct _GstMpegVideoPictureHdr +{ + guint16 tsn; + guint8 pic_type; + + guint8 full_pel_forward_vector, full_pel_backward_vector; + + guint8 f_code[2][2]; +}; + +/** + * GstMpegVideoPictureExt: + * @intra_dc_precision: Intra DC precision + * @picture_structure: Structure of the picture + * @top_field_first: Top field first + * @frame_pred_frame_dct: Frame + * @concealment_motion_vectors: Concealment Motion Vectors + * @q_scale_type: Q Scale Type + * @intra_vlc_format: Intra Vlc Format + * @alternate_scan: Alternate Scan + * @repeat_first_field: Repeat First Field + * @chroma_420_type: Chroma 420 Type + * @progressive_frame: %TRUE if the frame is progressive %FALSE otherwize + * + * The Mpeg2 Video Picture Extension structure. + */ +struct _GstMpegVideoPictureExt +{ + guint8 f_code[2][2]; + + guint8 intra_dc_precision; + guint8 picture_structure; + guint8 top_field_first; + guint8 frame_pred_frame_dct; + guint8 concealment_motion_vectors; + guint8 q_scale_type; + guint8 intra_vlc_format; + guint8 alternate_scan; + guint8 repeat_first_field; + guint8 chroma_420_type; + guint8 progressive_frame; + guint8 composite_display; + guint8 v_axis; + guint8 field_sequence; + guint8 sub_carrier; + guint8 burst_amplitude; + guint8 sub_carrier_phase; +}; + +/** + * GstMpegVideoGop: + * @drop_frame_flag: Drop Frame Flag + * @hour: Hour (0-23) + * @minute: Minute (O-59) + * @second: Second (0-59) + * @frame: Frame (0-59) + * @closed_gop: Closed Gop + * @broken_gop: Broken Gop + * + * The Mpeg Video Group of Picture structure. + */ +struct _GstMpegVideoGop +{ + guint8 drop_frame_flag; + + guint8 hour, minute, second, frame; + + guint8 closed_gop; + guint8 broken_gop; +}; + +/** + * GstMpegVideoTypeOffsetSize: + * @type: the type of the packet that start at @offset + * @offset: the offset of the packet start in bytes, it is the exact, start of the packet, no sync code included + * @size: The size in bytes of the packet or -1 if the end wasn't found. It is the exact size of the packet, no sync code included + * + * A structure that contains the type of a packet, its offset and its size + */ +struct _GstMpegVideoTypeOffsetSize +{ + guint8 type; + guint offset; + gint size; +}; + +GList * gst_mpeg_video_parse (guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_sequence_header (GstMpegVideoSequenceHdr * params, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_picture_header (GstMpegVideoPictureHdr* hdr, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_picture_extension (GstMpegVideoPictureExt *ext, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_gop (GstMpegVideoGop * gop, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_sequence_extension (GstMpegVideoSequenceExt * seqext, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_quant_matrix_extension (GstMpegVideoQuantMatrixExt * quant, + guint8 * data, gsize size, guint offset); + +G_END_DECLS + +#endif diff --git a/gst-plugins-bad.spec.in b/gst-plugins-bad.spec.in index c1c81f198f..adbd33df61 100644 --- a/gst-plugins-bad.spec.in +++ b/gst-plugins-bad.spec.in @@ -204,6 +204,7 @@ make ERROR_CFLAGS='' ERROR_CXXFLAGS='' %{_libdir}/libgstbasevideo-%{majorminor}.so.* %{_libdir}/libgstphotography-%{majorminor}.so.* %{_libdir}/libgstsignalprocessor-%{majorminor}.so.* +%{_libdir}/libgstcodecparsers-%{majorminor}.so.* # Plugins without external dependencies %{_libdir}/gstreamer-%{majorminor}/libgstadpcmdec.so %{_libdir}/gstreamer-%{majorminor}/libgstadpcmenc.so @@ -329,8 +330,10 @@ make ERROR_CFLAGS='' ERROR_CXXFLAGS='' %{_libdir}/libgstbasevideo-%{majorminor}.so %{_libdir}/libgstphotography-%{majorminor}.so %{_libdir}/libgstsignalprocessor-%{majorminor}.so +%{_libdir}/libgstcodecparsers-%{majorminor}.so %{_libdir}/libgstbasecamerabinsrc-%{majorminor}.so %{_includedir}/gstreamer-%{majorminor}/gst/interfaces/photography* +%{_includedir}/gstreamer-%{majorminor}/gst/codecparsers %{_includedir}/gstreamer-%{majorminor}/gst/signalprocessor %{_includedir}/gstreamer-%{majorminor}/gst/video %{_includedir}/gstreamer-%{majorminor}/gst/basecamerabinsrc/gstbasecamerasrc.h diff --git a/pkgconfig/gstreamer-plugins-bad.pc.in b/pkgconfig/gstreamer-plugins-bad.pc.in index c75ff59c77..1fad717474 100644 --- a/pkgconfig/gstreamer-plugins-bad.pc.in +++ b/pkgconfig/gstreamer-plugins-bad.pc.in @@ -7,6 +7,7 @@ Name: GStreamer Bad Plugin libraries Description: Currently includes the photography interface library Requires: gstreamer-@GST_MAJORMINOR@ gstreamer-base-@GST_MAJORMINOR@ Version: @VERSION@ -Libs: -L${libdir} -lgstphotography-@GST_MAJORMINOR@ +Libs: -L${libdir} -lgstphotography-@GST_MAJORMINOR@\ + -L${libdir} -lgstcodecparsers-@GST_MAJORMINOR@\ Cflags: -I${includedir} diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 174c73036d..8528eb2352 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -183,6 +183,7 @@ check_PROGRAMS = \ pipelines/colorspace \ $(check_mimic) \ elements/rtpmux \ + libs/mpegvideoparser \ $(check_schro) \ $(check_vp8) \ elements/viewfinderbin \ @@ -214,6 +215,15 @@ elements_h263parse_LDADD = libparser.la $(LDADD) elements_h264parse_LDADD = libparser.la $(LDADD) +libs_mpegvideoparser_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) + +libs_mpegvideoparser_LDADD = \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la \ + $(GST_PLUGINS_BAD_LIBS) -lgstcodecparsers-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) + elements_voaacenc_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) diff --git a/tests/check/libs/mpegvideoparser.c b/tests/check/libs/mpegvideoparser.c new file mode 100644 index 0000000000..414a45e232 --- /dev/null +++ b/tests/check/libs/mpegvideoparser.c @@ -0,0 +1,208 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +/* actually seq + gop */ +static guint8 mpeg2_seq[] = { + 0x00, 0x00, 0x01, 0xb3, 0x02, 0x00, 0x18, 0x15, 0xff, 0xff, 0xe0, 0x28, + 0x00, 0x00, 0x01, 0xb3, 0x78, 0x04, 0x38, 0x37, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0x00, 0x01, 0xb5, 0x14, 0x8a, 0x00, 0x11, 0x03, 0x71, + 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x03, 0x00, 0x08, 0x00, 0x00 +}; + +static guint8 mis_identified_datas[] = { + 0x00, 0x00, 0x01, 0x1f, 0x4a, 0xf4, 0xd4, 0xd8, 0x08, 0x23, 0xdd, + 0x7c, 0xd3, 0x75, 0x21, 0x43, 0x85, 0x31, 0x43, 0x04, 0x24, 0x30, + 0x18, 0x43, 0xba, 0x1a, 0x50, 0x60, 0xbb, 0x53, 0x56, 0x80, 0x41, + 0xb9, 0xd4, 0x25, 0x42, 0xea, 0x71, 0xb7, 0x49, 0x84, 0x0b, 0x14, + 0x24, 0xc2, 0xaa, 0xba, 0xf9, 0xf7, 0x5b, 0x78, 0xa2, 0xba, 0xd3, + 0xc7, 0x12, 0xee, 0xbe, 0xba, 0xfa, 0xeb, 0xeb, 0xaf, 0xbe, 0x6f, + 0xce, 0x92, 0x05, 0x15, 0x22, 0x44, 0xf4, 0xc9, 0x1b, 0xcd, 0x84, + 0x80, 0x87, 0x35, 0x6c, 0x07, 0x82, 0xaf, 0x3c, 0x3a, 0x89, 0x48, + 0x3a, 0x26, 0x00, 0x64, 0x03, 0x12, 0x60, 0x03, 0xf4, 0x8c, 0x21, + 0x16, 0xbe, 0x3c, 0x7c, 0x18, 0x03, 0x10, 0x0c, 0x80, 0xa0, 0x05, + 0xe1, 0x85, 0x94, 0x90, 0xc4, 0x74, 0x05, 0x72, 0x80, 0x7a, 0x8e, + 0x3e, 0x00, 0x30, + /* The accelerated version of scan_for_start_codes() + * mis-identifies the following as a start code */ + 0x01, 0x00, 0x01, 0x80, 0x68, 0x14, + 0x26, 0xe4, 0x80, 0x98, 0x0a, 0xba, 0x77, 0x01, 0xc2, 0x42, 0x12, + 0xc4, 0x59, 0x2a, 0xbb, 0x49, 0xf2, 0xc5, 0xa8, 0xd9, 0x30, 0x33, + 0x16, 0x50, 0x60, 0x61, 0x41, 0xaa, 0x0d, 0x41, 0x5b, 0x17, 0x77, + 0x76, 0x1a, 0x14, 0x3a, 0x08, 0x19, 0x3d, 0x6c, 0x94, 0x55, 0xd0, + 0x94, 0x5a, 0xeb, 0x61, 0x22, 0xa7, 0xa6, 0x83, 0x47, 0x6d, 0x4d, + 0x84, 0xc4, 0x6f, 0x78, 0xd8, 0x3a, 0xb4, 0x02, 0x0c, 0x36, 0xa6, + 0x0b, 0x18, 0x49, 0xf7, 0xad, 0x00, 0x82, 0x09, 0xba, 0x12, 0xba, + 0x1d, 0x44, 0x94, 0x0a, 0x1b, 0x03, 0xbb, 0xa2, 0x53, 0x02, 0xc0, + 0x41, 0xac, 0x22, + /* the real start code is here */ + 0x00, 0x00, 0x01, 0x20, 0x4a, 0xfd, 0xf5, 0x50 +}; + +static GstMpegVideoPacketTypeCode ordercode[] = { + GST_MPEG_VIDEO_PACKET_SEQUENCE, + GST_MPEG_VIDEO_PACKET_EXTENSION, + GST_MPEG_VIDEO_PACKET_GOP, +}; + +GST_START_TEST (test_mpeg_parse) +{ + gint i; + GList *list, *tmp; + GstMpegVideoTypeOffsetSize *typeoffsz; + + list = gst_mpeg_video_parse (mpeg2_seq, sizeof (mpeg2_seq), 12); + + assert_equals_int (g_list_length (list), 4); + for (tmp = list, i = 0; tmp; tmp = g_list_next (tmp), i++) { + typeoffsz = tmp->data; + if (i == 3) { + fail_unless (GST_MPEG_VIDEO_PACKET_SLICE_MIN <= typeoffsz->type && + typeoffsz->type <= GST_MPEG_VIDEO_PACKET_SLICE_MAX); + fail_unless (typeoffsz->size < 0); + } else + assert_equals_int (ordercode[i], typeoffsz->type); + } + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +GST_START_TEST (test_mpeg_parse_sequence_header) +{ + GList *list; + GstMpegVideoTypeOffsetSize *typeoffsz; + GstMpegVideoSequenceHdr seqhdr; + + list = gst_mpeg_video_parse (mpeg2_seq, sizeof (mpeg2_seq), 12); + + typeoffsz = list->data; + fail_unless (typeoffsz->type == GST_MPEG_VIDEO_PACKET_SEQUENCE); + fail_unless (gst_mpeg_video_parse_sequence_header (&seqhdr, mpeg2_seq, + sizeof (mpeg2_seq), typeoffsz->offset)); + assert_equals_int (seqhdr.width, 1920); + assert_equals_int (seqhdr.height, 1080); + assert_equals_int (seqhdr.aspect_ratio_info, 3); + assert_equals_int (seqhdr.par_w, 17280); + assert_equals_int (seqhdr.par_h, 17280); + assert_equals_int (seqhdr.frame_rate_code, 7); + assert_equals_int (seqhdr.fps_n, 60000); + assert_equals_int (seqhdr.fps_d, 1001); + assert_equals_int (seqhdr.bitrate_value, 262143); + assert_equals_int (seqhdr.bitrate, 0); + assert_equals_int (seqhdr.vbv_buffer_size_value, 512); + fail_unless (seqhdr.constrained_parameters_flag == FALSE); + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +GST_START_TEST (test_mpeg_parse_sequence_extension) +{ + GList *list; + GstMpegVideoTypeOffsetSize *typeoffsz; + GstMpegVideoSequenceExt seqext; + + list = gst_mpeg_video_parse (mpeg2_seq, sizeof (mpeg2_seq), 12); + + typeoffsz = list->next->data; + fail_unless (typeoffsz->type == GST_MPEG_VIDEO_PACKET_EXTENSION); + fail_unless (gst_mpeg_video_parse_sequence_extension (&seqext, + mpeg2_seq, sizeof (mpeg2_seq), typeoffsz->offset)); + assert_equals_int (seqext.profile, 4); + assert_equals_int (seqext.level, 8); + assert_equals_int (seqext.progressive, 1); + assert_equals_int (seqext.chroma_format, 1); + assert_equals_int (seqext.horiz_size_ext, 0); + assert_equals_int (seqext.vert_size_ext, 0); + assert_equals_int (seqext.vert_size_ext, 0); + assert_equals_int (seqext.bitrate_ext, 8); + assert_equals_int (seqext.vbv_buffer_size_extension, 3); + assert_equals_int (seqext.low_delay, 0); + assert_equals_int (seqext.fps_n_ext, 3); + assert_equals_int (seqext.fps_d_ext, 2); + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +GST_START_TEST (test_mis_identified_datas) +{ + GList *list, *tmp; + GstMpegVideoTypeOffsetSize *typeoffsz; + guint8 *data = mis_identified_datas; + + list = gst_mpeg_video_parse (mis_identified_datas, + sizeof (mis_identified_datas), 0); + + assert_equals_int (g_list_length (list), 2); + for (tmp = list; tmp; tmp = g_list_next (tmp)) { + typeoffsz = tmp->data; + + assert_equals_int (data[typeoffsz->offset - 4], 0); + assert_equals_int (data[typeoffsz->offset - 3], 0); + assert_equals_int (data[typeoffsz->offset - 2], 1); + } + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +static Suite * +videoparsers_suite (void) +{ + Suite *s = suite_create ("Video Parsers library"); + + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_mpeg_parse); + tcase_add_test (tc_chain, test_mpeg_parse_sequence_header); + tcase_add_test (tc_chain, test_mpeg_parse_sequence_extension); + tcase_add_test (tc_chain, test_mis_identified_datas); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = videoparsers_suite (); + + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} From a983b29a4921497d325dd64c5fc9630158742635 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 23 Jun 2011 12:54:43 -0400 Subject: [PATCH 58/85] mpegvideoparse: Port to the new mpeg parsing library --- gst/videoparsers/Makefile.am | 9 +- gst/videoparsers/gstmpegvideoparse.c | 308 ++++++++++++-------------- gst/videoparsers/gstmpegvideoparse.h | 13 +- gst/videoparsers/mpegvideoparse.c | 274 ----------------------- gst/videoparsers/mpegvideoparse.h | 77 ------- tests/check/elements/mpegvideoparse.c | 5 +- 6 files changed, 160 insertions(+), 526 deletions(-) delete mode 100644 gst/videoparsers/mpegvideoparse.c delete mode 100644 gst/videoparsers/mpegvideoparse.h diff --git a/gst/videoparsers/Makefile.am b/gst/videoparsers/Makefile.am index 6cebef5330..4fa87b8a79 100644 --- a/gst/videoparsers/Makefile.am +++ b/gst/videoparsers/Makefile.am @@ -4,10 +4,13 @@ libgstvideoparsersbad_la_SOURCES = plugin.c \ h263parse.c gsth263parse.c \ gsth264parse.c h264parse.c \ gstdiracparse.c dirac_parse.c \ - gstmpegvideoparse.c mpegvideoparse.c + gstmpegvideoparse.c + libgstvideoparsersbad_la_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) -libgstvideoparsersbad_la_LIBADD = \ +libgstvideoparsersbad_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-$(GST_MAJORMINOR).la \ $(GST_BASE_LIBS) $(GST_LIBS) libgstvideoparsersbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static @@ -15,7 +18,7 @@ libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gsth263parse.h h263parse.h \ gsth264parse.h h264parse.h \ gstdiracparse.h dirac_parse.h \ - gstmpegvideoparse.h mpegvideoparse.h + gstmpegvideoparse.h Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer \ diff --git a/gst/videoparsers/gstmpegvideoparse.c b/gst/videoparsers/gstmpegvideoparse.c index 7ebbdf36a4..12d98d5d77 100644 --- a/gst/videoparsers/gstmpegvideoparse.c +++ b/gst/videoparsers/gstmpegvideoparse.c @@ -1,8 +1,10 @@ /* GStreamer * Copyright (C) <2007> Jan Schmidt * Copyright (C) <2011> Mark Nauwelaerts - * Copyright (C) <2011> Collabora Multimedia + * Copyright (C) <2011> Thibault Saunier + * Copyright (C) <2011> Collabora ltd * Copyright (C) <2011> Nokia Corporation + * Copyright (C) <2011> Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -168,6 +170,7 @@ gst_mpegv_parse_class_init (GstMpegvParseClass * klass) static void gst_mpegv_parse_init (GstMpegvParse * parse, GstMpegvParseClass * g_class) { + parse->mpeg_version = 0; } static void @@ -175,7 +178,7 @@ gst_mpegv_parse_reset_frame (GstMpegvParse * mpvparse) { /* done parsing; reset state */ mpvparse->last_sc = -1; - mpvparse->seq_offset = -1; + mpvparse->seq_offset = G_MAXUINT; mpvparse->pic_offset = -1; } @@ -187,7 +190,9 @@ gst_mpegv_parse_reset (GstMpegvParse * mpvparse) mpvparse->update_caps = TRUE; gst_buffer_replace (&mpvparse->config, NULL); - memset (&mpvparse->params, 0, sizeof (mpvparse->params)); + memset (&mpvparse->sequencehdr, 0, sizeof (mpvparse->sequencehdr)); + memset (&mpvparse->sequenceext, 0, sizeof (mpvparse->sequenceext)); + memset (&mpvparse->pichdr, 0, sizeof (mpvparse->pichdr)); } static gboolean @@ -217,23 +222,45 @@ gst_mpegv_parse_stop (GstBaseParse * parse) } static gboolean -gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, const guint8 * data, - gsize size) +gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf, + guint size) { + GList *tmp; + guint8 *data = GST_BUFFER_DATA (buf); + data = data + mpvparse->seq_offset; + /* only do stuff if something new */ if (mpvparse->config && size == GST_BUFFER_SIZE (mpvparse->config) && memcmp (GST_BUFFER_DATA (mpvparse->config), data, size) == 0) return TRUE; - if (!gst_mpeg_video_params_parse_config (&mpvparse->params, data, size)) { - GST_DEBUG_OBJECT (mpvparse, "failed to parse config data (size %" - G_GSSIZE_FORMAT ")", size); + if (!gst_mpeg_video_parse_sequence_header (&mpvparse->sequencehdr, data, + GST_BUFFER_SIZE (buf) - mpvparse->seq_offset, 0)) { + GST_DEBUG_OBJECT (mpvparse, + "failed to parse config data (size %" G_GSSIZE_FORMAT ") at offset %d", + size, mpvparse->seq_offset); return FALSE; } GST_LOG_OBJECT (mpvparse, "accepting parsed config size %" G_GSSIZE_FORMAT, size); + /* Set mpeg version, and parse sequence extension */ + if (mpvparse->mpeg_version <= 0) { + GstMpegVideoTypeOffsetSize *tpoffsz; + + mpvparse->mpeg_version = 1; + for (tmp = mpvparse->typeoffsize; tmp; tmp = tmp->next) { + tpoffsz = tmp->data; + + if (tpoffsz->type == GST_MPEG_VIDEO_PACKET_EXTENSION) { + mpvparse->mpeg_version = 2; + gst_mpeg_video_parse_sequence_extension (&mpvparse->sequenceext, + GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), tpoffsz->offset); + } + } + } + /* parsing ok, so accept it as new config */ if (mpvparse->config != NULL) gst_buffer_unref (mpvparse->config); @@ -309,22 +336,18 @@ picture_type_name (guint8 pct) /* for off == 0 initial code; returns TRUE if code starts a frame, * otherwise returns TRUE if code terminates preceding frame */ static gboolean -gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse, GstBuffer * buf, gint off) +gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse, + GstBuffer * buf, guint off, guint8 code) { - gboolean ret = FALSE, do_seq = TRUE; - guint8 *data; - guint code; + gboolean ret = FALSE, packet = TRUE; g_return_val_if_fail (buf && GST_BUFFER_SIZE (buf) >= 4, FALSE); - data = GST_BUFFER_DATA (buf); - code = data[off + 3]; - GST_LOG_OBJECT (mpvparse, "process startcode %x (%s)", code, picture_start_code_name (code)); switch (code) { - case MPEG_PACKET_PICTURE: + case GST_MPEG_VIDEO_PACKET_PICTURE: GST_LOG_OBJECT (mpvparse, "startcode is PICTURE"); /* picture is aggregated with preceding sequence/gop, if any. * so, picture start code only ends if already a previous one */ @@ -332,103 +355,64 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse, GstBuffer * buf, gint off) mpvparse->pic_offset = off; else ret = TRUE; - if (!off) + if (off == 4) ret = TRUE; break; - case MPEG_PACKET_SEQUENCE: + case GST_MPEG_VIDEO_PACKET_SEQUENCE: GST_LOG_OBJECT (mpvparse, "startcode is SEQUENCE"); - if (off == 0) + if (off < mpvparse->seq_offset) mpvparse->seq_offset = off; ret = TRUE; break; - case MPEG_PACKET_GOP: + case GST_MPEG_VIDEO_PACKET_GOP: GST_LOG_OBJECT (mpvparse, "startcode is GOP"); - if (mpvparse->seq_offset >= 0) + if (mpvparse->seq_offset < G_MAXUINT) ret = mpvparse->gop_split; else ret = TRUE; break; default: - do_seq = FALSE; + packet = FALSE; break; } - /* process config data */ - if (G_UNLIKELY (mpvparse->seq_offset >= 0 && off && do_seq)) { - g_assert (mpvparse->seq_offset == 0); - gst_mpegv_parse_process_config (mpvparse, GST_BUFFER_DATA (buf), off); - /* avoid accepting again for a PICTURE sc following a GOP sc */ - mpvparse->seq_offset = -1; + if (mpvparse->seq_offset != G_MAXUINT && off != mpvparse->seq_offset && + packet) { + gst_mpegv_parse_process_config (mpvparse, buf, off - mpvparse->seq_offset); + mpvparse->seq_offset = G_MAXUINT; } /* extract some picture info if there is any in the frame being terminated */ - if (G_UNLIKELY (ret && off)) { - if (G_LIKELY (mpvparse->pic_offset >= 0 && mpvparse->pic_offset < off)) { - if (G_LIKELY (GST_BUFFER_SIZE (buf) >= mpvparse->pic_offset + 6)) { - gint pct = (data[mpvparse->pic_offset + 5] >> 3) & 0x7; - - GST_LOG_OBJECT (mpvparse, "picture_coding_type %d (%s)", pct, - picture_type_name (pct)); - mpvparse->intra_frame = (pct == MPEG_PICTURE_TYPE_I); - } else { - GST_WARNING_OBJECT (mpvparse, "no data following PICTURE startcode"); - mpvparse->intra_frame = FALSE; - } - } else { - /* frame without picture must be some config, consider as keyframe */ - mpvparse->intra_frame = TRUE; - } - GST_LOG_OBJECT (mpvparse, "ending frame of size %d, is intra %d", off, - mpvparse->intra_frame); + if (ret && mpvparse->pic_offset >= 0 && mpvparse->pic_offset < off) { + if (gst_mpeg_video_parse_picture_header (&mpvparse->pichdr, + GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), mpvparse->pic_offset)) + GST_LOG_OBJECT (mpvparse, "picture_coding_type %d (%s), ending" + "frame of size %d", mpvparse->pichdr.pic_type, + picture_type_name (mpvparse->pichdr.pic_type), off - 4); + else + GST_LOG_OBJECT (mpvparse, "Couldn't parse picture at offset %d", + mpvparse->pic_offset); } return ret; } -static inline guint -scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) +static inline gint +get_frame_size (GstMpegvParse * mpvparse, GstBuffer * buf, GList * l_codoffsz) { - const guint8 *data; - guint32 state; - guint i; + GList *tmp; + GstMpegVideoTypeOffsetSize *codoffsz; - g_return_val_if_fail (size > 0, -1); - g_return_val_if_fail ((guint64) offset + size <= reader->size - reader->byte, - -1); + for (tmp = l_codoffsz; tmp; tmp = tmp->next) { + codoffsz = tmp->data; - /* we can't find the pattern with less than 4 bytes */ - if (G_UNLIKELY (size < 4)) - return -1; + GST_LOG_OBJECT (mpvparse, "next start code at %d", codoffsz->offset); - data = reader->data + reader->byte + offset; - - /* set the state to something that does not match */ - state = 0xffffffff; - - /* now find data */ - for (i = 0; i < size; i++) { - /* throw away one byte and move in the next byte */ - state = ((state << 8) | data[i]); - if (G_UNLIKELY ((state & 0xffffff00) == 0x00000100)) { - /* we have a match but we need to have skipped at - * least 4 bytes to fill the state. */ - if (G_LIKELY (i >= 3)) - return offset + i - 3; - } - - /* Accelerate search for start code */ - if (data[i] > 1) { - while (i < (size - 4) && data[i] > 1) { - if (data[i + 3] > 1) - i += 4; - else - i += 1; - } - state = 0x00000100; - } + if (gst_mpegv_parse_process_sc (mpvparse, buf, codoffsz->offset, + codoffsz->type)) + return codoffsz->offset - 4; } - /* nothing found */ return -1; } @@ -436,21 +420,10 @@ scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) * see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */ #define GST_BASE_PARSE_FRAME_FLAG_PARSING 0x10000 -static gboolean -gst_mpegv_parse_check_valid_frame (GstBaseParse * parse, - GstBaseParseFrame * frame, guint * framesize, gint * skipsize) +static inline void +update_frame_parsing_status (GstMpegvParse * mpvparse, + GstBaseParseFrame * frame) { - GstMpegvParse *mpvparse = GST_MPEGVIDEO_PARSE (parse); - GstBuffer *buf = frame->buffer; - GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); - gint off = 0; - gboolean ret; - -retry: - /* at least start code and subsequent byte */ - if (G_UNLIKELY (GST_BUFFER_SIZE (buf) - off < 5)) - return FALSE; - /* avoid stale cached parsing state */ if (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_PARSING)) { GST_LOG_OBJECT (mpvparse, "parsing new frame"); @@ -459,72 +432,75 @@ retry: } else { GST_LOG_OBJECT (mpvparse, "resuming frame parsing"); } +} - /* if already found a previous start code, e.g. start of frame, go for next */ - if (mpvparse->last_sc >= 0) { + +static gboolean +gst_mpegv_parse_check_valid_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, guint * framesize, gint * skipsize) +{ + GstMpegvParse *mpvparse = GST_MPEGVIDEO_PARSE (parse); + GstBuffer *buf = frame->buffer; + gboolean ret = FALSE; + GList *tmp; + gint off = 0, fsize = -1; + + update_frame_parsing_status (mpvparse, frame); + + if (mpvparse->last_sc >= 0) off = mpvparse->last_sc; - goto next; + + mpvparse->typeoffsize = + gst_mpeg_video_parse (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), off); + + /* No sc found */ + if (mpvparse->typeoffsize == NULL) + goto end; + + /* Already found the start code looking for the end */ + if (mpvparse->last_sc >= 0) { + *skipsize = 0; + fsize = get_frame_size (mpvparse, buf, mpvparse->typeoffsize); + + goto end; } - off = scan_for_start_codes (&reader, off, GST_BUFFER_SIZE (buf) - off); + for (tmp = mpvparse->typeoffsize; tmp; tmp = g_list_next (tmp)) { + GstMpegVideoTypeOffsetSize *codoffsz = tmp->data; - GST_LOG_OBJECT (mpvparse, "possible sync at buffer offset %d", off); + GST_LOG_OBJECT (mpvparse, "next start code at %d", codoffsz->offset); - /* didn't find anything that looks like a sync word, skip */ - if (G_UNLIKELY (off < 0)) { - *skipsize = GST_BUFFER_SIZE (buf) - 3; - return FALSE; - } + if (codoffsz->size < 0) + break; - /* possible frame header, but not at offset 0? skip bytes before sync */ - if (G_UNLIKELY (off > 0)) { - *skipsize = off; - return FALSE; - } + ret = gst_mpegv_parse_process_sc (mpvparse, buf, codoffsz->offset, + codoffsz->type); - /* note: initial start code is assumed at offset 0 by subsequent code */ - - /* examine start code, see if it looks like an initial start code */ - if (gst_mpegv_parse_process_sc (mpvparse, buf, 0)) { - /* found sc */ - mpvparse->last_sc = 0; - } else { - off++; - goto retry; - } - -next: - /* start is fine as of now */ - *skipsize = 0; - /* position a bit further than last sc */ - off++; - /* so now we have start code at start of data; locate next start code */ - off = scan_for_start_codes (&reader, off, GST_BUFFER_SIZE (buf) - off); - - GST_LOG_OBJECT (mpvparse, "next start code at %d", off); - if (off < 0) { - /* if draining, take all */ - if (GST_BASE_PARSE_DRAINING (parse)) { - off = GST_BUFFER_SIZE (buf); - ret = TRUE; - } else { - /* resume scan where we left it */ - mpvparse->last_sc = GST_BUFFER_SIZE (buf) - 4; - /* request best next available */ - *framesize = G_MAXUINT; - return FALSE; + if (ret) { + *skipsize = 0; + fsize = get_frame_size (mpvparse, buf, tmp->next); + break; } - } else { - /* decide whether this startcode ends a frame */ - ret = gst_mpegv_parse_process_sc (mpvparse, buf, off); } - if (ret) { - *framesize = off; +end: + if (fsize > 0) { + *framesize = fsize; + ret = TRUE; + } else if (GST_BASE_PARSE_DRAINING (parse)) { + *framesize = GST_BUFFER_SIZE (buf); + ret = TRUE; } else { - goto next; + /* resume scan where we left it */ + mpvparse->last_sc = GST_BUFFER_SIZE (buf); + /* request best next available */ + *framesize = G_MAXUINT; + ret = FALSE; } + g_list_free_full (mpvparse->typeoffsize, (GDestroyNotify) g_free); + mpvparse->typeoffsize = NULL; + return ret; } @@ -550,22 +526,23 @@ gst_mpegv_parse_update_src_caps (GstMpegvParse * mpvparse) * config data, so we should at least know about version. * If not, it means it has been requested not to drop data, and * upstream and/or app must know what they are doing ... */ - if (G_LIKELY (mpvparse->params.mpeg_version)) + + if (G_LIKELY (mpvparse->mpeg_version)) gst_caps_set_simple (caps, - "mpegversion", G_TYPE_INT, mpvparse->params.mpeg_version, NULL); + "mpegversion", G_TYPE_INT, mpvparse->mpeg_version, NULL); gst_caps_set_simple (caps, "systemstream", G_TYPE_BOOLEAN, FALSE, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); - if (mpvparse->params.width > 0 && mpvparse->params.height > 0) { - gst_caps_set_simple (caps, "width", G_TYPE_INT, mpvparse->params.width, - "height", G_TYPE_INT, mpvparse->params.height, NULL); + if (mpvparse->sequencehdr.width > 0 && mpvparse->sequencehdr.height > 0) { + gst_caps_set_simple (caps, "width", G_TYPE_INT, mpvparse->sequencehdr.width, + "height", G_TYPE_INT, mpvparse->sequencehdr.height, NULL); } /* perhaps we have a framerate */ - if (mpvparse->params.fps_n > 0 && mpvparse->params.fps_d > 0) { - gint fps_num = mpvparse->params.fps_n; - gint fps_den = mpvparse->params.fps_d; + if (mpvparse->sequencehdr.fps_n > 0 && mpvparse->sequencehdr.fps_d > 0) { + gint fps_num = mpvparse->sequencehdr.fps_n; + gint fps_den = mpvparse->sequencehdr.fps_d; GstClockTime latency = gst_util_uint64_scale (GST_SECOND, fps_den, fps_num); gst_caps_set_simple (caps, "framerate", @@ -576,9 +553,9 @@ gst_mpegv_parse_update_src_caps (GstMpegvParse * mpvparse) } /* or pixel-aspect-ratio */ - if (mpvparse->params.par_w && mpvparse->params.par_h > 0) { + if (mpvparse->sequencehdr.par_w && mpvparse->sequencehdr.par_h > 0) { gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, - mpvparse->params.par_w, mpvparse->params.par_h, NULL); + mpvparse->sequencehdr.par_w, mpvparse->sequencehdr.par_h, NULL); } if (mpvparse->config != NULL) { @@ -586,9 +563,9 @@ gst_mpegv_parse_update_src_caps (GstMpegvParse * mpvparse) GST_TYPE_BUFFER, mpvparse->config, NULL); } - if (mpvparse->params.mpeg_version == 2) { - const guint profile_c = mpvparse->params.profile; - const guint level_c = mpvparse->params.level; + if (mpvparse->mpeg_version == 2) { + const guint profile_c = mpvparse->sequenceext.profile; + const guint level_c = mpvparse->sequenceext.level; const gchar *profile = NULL, *level = NULL; /* * Profile indication - 1 => High, 2 => Spatially Scalable, @@ -657,7 +634,7 @@ gst_mpegv_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_mpegv_parse_update_src_caps (mpvparse); - if (G_UNLIKELY (mpvparse->intra_frame)) + if (G_UNLIKELY (mpvparse->pichdr.pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_I)) GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); else GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); @@ -694,8 +671,7 @@ gst_mpegv_parse_set_caps (GstBaseParse * parse, GstCaps * caps) /* best possible parse attempt, * src caps are based on sink caps so it will end up in there * whether sucessful or not */ - gst_mpegv_parse_process_config (mpvparse, GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf)); + gst_mpegv_parse_process_config (mpvparse, buf, GST_BUFFER_SIZE (buf)); } /* let's not interfere and accept regardless of config parsing success */ diff --git a/gst/videoparsers/gstmpegvideoparse.h b/gst/videoparsers/gstmpegvideoparse.h index a3706a41a4..6837a316e4 100644 --- a/gst/videoparsers/gstmpegvideoparse.h +++ b/gst/videoparsers/gstmpegvideoparse.h @@ -1,8 +1,10 @@ /* GStreamer * Copyright (C) <2007> Jan Schmidt * Copyright (C) <2011> Mark Nauwelaerts - * Copyright (C) <2011> Collabora Multimedia + * Copyright (C) <2011> Thibault Saunier + * Copyright (C) <2011> Collabora ltd * Copyright (C) <2011> Nokia Corporation + * Copyright (C) <2011> Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,7 +28,7 @@ #include #include -#include "mpegvideoparse.h" +#include G_BEGIN_DECLS @@ -49,15 +51,18 @@ struct _GstMpegvParse { GstBaseParse element; /* parse state */ + GList *typeoffsize; gint last_sc; gint seq_offset; gint pic_offset; - gboolean intra_frame; gboolean update_caps; GstBuffer *config; guint8 profile; - MPEGVParams params; + guint mpeg_version; + GstMpegVideoSequenceHdr sequencehdr; + GstMpegVideoSequenceExt sequenceext; + GstMpegVideoPictureHdr pichdr; /* properties */ gboolean drop; diff --git a/gst/videoparsers/mpegvideoparse.c b/gst/videoparsers/mpegvideoparse.c deleted file mode 100644 index e85d77bdbb..0000000000 --- a/gst/videoparsers/mpegvideoparse.c +++ /dev/null @@ -1,274 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Jan Schmidt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "mpegvideoparse.h" - -#include -#include - -GST_DEBUG_CATEGORY_EXTERN (mpegv_parse_debug); -#define GST_CAT_DEFAULT mpegv_parse_debug - - -#define GET_BITS(b, num, bits) G_STMT_START { \ - if (!gst_bit_reader_get_bits_uint32(b, bits, num)) \ - goto failed; \ - GST_TRACE ("parsed %d bits: %d", num, *(bits)); \ -} G_STMT_END - -#define MARKER_BIT(b) G_STMT_START { \ - guint32 i; \ - GET_BITS(b, 1, &i); \ - if (i != 0x1) \ - goto failed; \ -} G_STMT_END - -static inline gboolean -find_start_code (GstBitReader * b) -{ - guint32 bits; - - /* 0 bits until byte aligned */ - while (b->bit != 0) { - GET_BITS (b, 1, &bits); - } - - /* 0 bytes until startcode */ - while (gst_bit_reader_peek_bits_uint32 (b, &bits, 32)) { - if (bits >> 8 == 0x1) { - return TRUE; - } else { - gst_bit_reader_skip (b, 8); - } - } - - return FALSE; - -failed: - return FALSE; -} - -static gboolean -gst_mpeg_video_params_parse_extension (MPEGVParams * params, GstBitReader * br) -{ - guint32 bits; - - /* double-check */ - GET_BITS (br, 32, &bits); - if (bits != 0x100 + MPEG_PACKET_EXTENSION) - goto failed; - - /* extension_start_code identifier */ - GET_BITS (br, 4, &bits); - - /* profile_and_level_indication */ - GET_BITS (br, 4, &bits); - params->profile = bits; - GET_BITS (br, 4, &bits); - params->level = bits; - - /* progressive_sequence */ - GET_BITS (br, 1, &bits); - params->progressive = bits; - - /* chroma_format */ - GET_BITS (br, 2, &bits); - - /* horizontal_size_extension */ - GET_BITS (br, 2, &bits); - params->width += (bits << 12); - /* vertical_size_extension */ - GET_BITS (br, 2, &bits); - params->height += (bits << 12); - - /* bit_rate_extension */ - GET_BITS (br, 12, &bits); - if (params->bitrate) - params->bitrate += (bits << 18) * 400; - /* marker_bit */ - MARKER_BIT (br); - /* vbv_buffer_size_extension */ - GET_BITS (br, 8, &bits); - /* low_delay */ - GET_BITS (br, 1, &bits); - - /* frame_rate_extension_n */ - GET_BITS (br, 2, &bits); - params->fps_n *= bits + 1; - /* frame_rate_extension_d */ - GET_BITS (br, 5, &bits); - params->fps_d *= bits + 1; - - return TRUE; - - /* ERRORS */ -failed: - { - GST_WARNING ("Failed to parse sequence extension"); - return FALSE; - } -} - -/* Set the Pixel Aspect Ratio in our hdr from a DAR code in the data */ -static void -set_par_from_dar (MPEGVParams * params, guint8 asr_code) -{ - /* Pixel_width = DAR_width * display_vertical_size */ - /* Pixel_height = DAR_height * display_horizontal_size */ - switch (asr_code) { - case 0x02: /* 3:4 DAR = 4:3 pixels */ - params->par_w = 4 * params->height; - params->par_h = 3 * params->width; - break; - case 0x03: /* 9:16 DAR */ - params->par_w = 16 * params->height; - params->par_h = 9 * params->width; - break; - case 0x04: /* 1:2.21 DAR */ - params->par_w = 221 * params->height; - params->par_h = 100 * params->width; - break; - case 0x01: /* Square pixels */ - params->par_w = params->par_h = 1; - break; - default: - GST_DEBUG ("unknown/invalid aspect_ratio_information %d", asr_code); - break; - } -} - -static void -set_fps_from_code (MPEGVParams * params, guint8 fps_code) -{ - const gint framerates[][2] = { - {30, 1}, {24000, 1001}, {24, 1}, {25, 1}, - {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001}, - {60, 1}, {30, 1} - }; - - if (fps_code && fps_code < 10) { - params->fps_n = framerates[fps_code][0]; - params->fps_d = framerates[fps_code][1]; - } else { - GST_DEBUG ("unknown/invalid frame_rate_code %d", fps_code); - /* Force a valid framerate */ - /* FIXME or should this be kept unknown ?? */ - params->fps_n = 30000; - params->fps_d = 1001; - } -} - -static gboolean -gst_mpeg_video_params_parse_sequence (MPEGVParams * params, GstBitReader * br) -{ - guint32 bits; - - GET_BITS (br, 32, &bits); - if (bits != 0x100 + MPEG_PACKET_SEQUENCE) - goto failed; - - /* assume MPEG-1 till otherwise discovered */ - params->mpeg_version = 1; - - GET_BITS (br, 12, &bits); - params->width = bits; - GET_BITS (br, 12, &bits); - params->height = bits; - - GET_BITS (br, 4, &bits); - set_par_from_dar (params, bits); - GET_BITS (br, 4, &bits); - set_fps_from_code (params, bits); - - GET_BITS (br, 18, &bits); - if (bits == 0x3ffff) { - /* VBR stream */ - params->bitrate = 0; - } else { - /* Value in header is in units of 400 bps */ - params->bitrate *= 400; - } - - /* skip 1 + VBV buffer size */ - if (!gst_bit_reader_skip (br, 11)) - goto failed; - - /* constrained_parameters_flag */ - GET_BITS (br, 1, &bits); - - /* load_intra_quantiser_matrix */ - GET_BITS (br, 1, &bits); - if (bits) { - if (!gst_bit_reader_skip (br, 8 * 64)) - goto failed; - } - - /* load_non_intra_quantiser_matrix */ - GET_BITS (br, 1, &bits); - if (bits) { - if (!gst_bit_reader_skip (br, 8 * 64)) - goto failed; - } - - /* check for MPEG-2 sequence extension */ - while (find_start_code (br)) { - gst_bit_reader_peek_bits_uint32 (br, &bits, 32); - if (bits == 0x100 + MPEG_PACKET_EXTENSION) { - if (!gst_mpeg_video_params_parse_extension (params, br)) - goto failed; - params->mpeg_version = 2; - } - } - - /* dump some info */ - GST_LOG ("width x height: %d x %d", params->width, params->height); - GST_LOG ("fps: %d/%d", params->fps_n, params->fps_d); - GST_LOG ("par: %d/%d", params->par_w, params->par_h); - GST_LOG ("profile/level: %d/%d", params->profile, params->level); - GST_LOG ("bitrate/progressive: %d/%d", params->bitrate, params->progressive); - - return TRUE; - - /* ERRORS */ -failed: - { - GST_WARNING ("Failed to parse sequence header"); - /* clear out stuff */ - memset (params, 0, sizeof (*params)); - return FALSE; - } -} - -gboolean -gst_mpeg_video_params_parse_config (MPEGVParams * params, const guint8 * data, - guint size) -{ - GstBitReader br; - - if (size < 4) - return FALSE; - - gst_bit_reader_init (&br, data, size); - - return gst_mpeg_video_params_parse_sequence (params, &br); -} diff --git a/gst/videoparsers/mpegvideoparse.h b/gst/videoparsers/mpegvideoparse.h deleted file mode 100644 index f0092b7137..0000000000 --- a/gst/videoparsers/mpegvideoparse.h +++ /dev/null @@ -1,77 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Jan Schmidt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_MPEGVIDEO_PARAMS_H__ -#define __GST_MPEGVIDEO_PARAMS_H__ - -#include - -G_BEGIN_DECLS - -/* Packet ID codes for different packet types we - * care about */ -#define MPEG_PACKET_PICTURE 0x00 -#define MPEG_PACKET_SLICE_MIN 0x01 -#define MPEG_PACKET_SLICE_MAX 0xaf -#define MPEG_PACKET_SEQUENCE 0xb3 -#define MPEG_PACKET_EXTENSION 0xb5 -#define MPEG_PACKET_SEQUENCE_END 0xb7 -#define MPEG_PACKET_GOP 0xb8 -#define MPEG_PACKET_NONE 0xff - -/* Extension codes we care about */ -#define MPEG_PACKET_EXT_SEQUENCE 0x01 -#define MPEG_PACKET_EXT_SEQUENCE_DISPLAY 0x02 -#define MPEG_PACKET_EXT_QUANT_MATRIX 0x03 - -/* Flags indicating what type of packets are in this block, some are mutually - * exclusive though - ie, sequence packs are accumulated separately. GOP & - * Picture may occur together or separately */ -#define MPEG_BLOCK_FLAG_SEQUENCE 0x01 -#define MPEG_BLOCK_FLAG_PICTURE 0x02 -#define MPEG_BLOCK_FLAG_GOP 0x04 - -#define MPEG_PICTURE_TYPE_I 0x01 -#define MPEG_PICTURE_TYPE_P 0x02 -#define MPEG_PICTURE_TYPE_B 0x03 -#define MPEG_PICTURE_TYPE_D 0x04 - -typedef struct _MPEGVParams MPEGVParams; - -struct _MPEGVParams -{ - gint mpeg_version; - - gint profile; - gint level; - - gint width, height; - gint par_w, par_h; - gint fps_n, fps_d; - - gint bitrate; - gboolean progressive; -}; - -GstFlowReturn gst_mpeg_video_params_parse_config (MPEGVParams * params, - const guint8 * data, guint size); - -G_END_DECLS - -#endif diff --git a/tests/check/elements/mpegvideoparse.c b/tests/check/elements/mpegvideoparse.c index 9dac4633d2..9aab78f9f1 100644 --- a/tests/check/elements/mpegvideoparse.c +++ b/tests/check/elements/mpegvideoparse.c @@ -183,8 +183,9 @@ mpeg_video_parse_check_caps (guint version, guint8 * seq, gint size) buf = gst_value_get_buffer (val); fail_unless (buf != NULL); /* codec-data = header - GOP */ - fail_unless (GST_BUFFER_SIZE (buf) == size - 8); - fail_unless (memcmp (GST_BUFFER_DATA (buf), seq, GST_BUFFER_SIZE (buf)) == 0); + assert_equals_int (GST_BUFFER_SIZE (buf), size - 8); + fail_unless (memcmp (GST_BUFFER_DATA (buf), seq + 4, + GST_BUFFER_SIZE (buf)) == 0); gst_caps_unref (caps); } From a1cfba43f649adc617fe41e265d5ed91b2813392 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 29 Jul 2011 10:56:15 +0200 Subject: [PATCH 59/85] codecparsers: h264: Add an h.264 bitstream parsing library --- docs/libs/gst-plugins-bad-libs-docs.sgml | 1 + docs/libs/gst-plugins-bad-libs-sections.txt | 44 + docs/libs/gst-plugins-bad-libs.types | 1 + gst-libs/gst/codecparsers/Makefile.am | 5 +- gst-libs/gst/codecparsers/gsth264parser.c | 1808 +++++++++++++++++++ gst-libs/gst/codecparsers/gsth264parser.h | 657 +++++++ tests/check/Makefile.am | 10 + tests/check/libs/h264parser.c | 182 ++ 8 files changed, 2706 insertions(+), 2 deletions(-) create mode 100644 gst-libs/gst/codecparsers/gsth264parser.c create mode 100644 gst-libs/gst/codecparsers/gsth264parser.h create mode 100644 tests/check/libs/h264parser.c diff --git a/docs/libs/gst-plugins-bad-libs-docs.sgml b/docs/libs/gst-plugins-bad-libs-docs.sgml index d1113bca3c..b31d6414e9 100644 --- a/docs/libs/gst-plugins-bad-libs-docs.sgml +++ b/docs/libs/gst-plugins-bad-libs-docs.sgml @@ -28,6 +28,7 @@ gstreamer-plugins-bad-&GST_MAJORMINOR;.pc and adding -lgscodeparsers-&GST_MAJORMINOR; to the library flags. +
diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt index a09929c04f..251d453cce 100644 --- a/docs/libs/gst-plugins-bad-libs-sections.txt +++ b/docs/libs/gst-plugins-bad-libs-sections.txt @@ -1,4 +1,48 @@ # codecparsers +
+gsth264parser +h264parser +gst/codecparsers/gsth264parser.h +GST_H264_MAX_SPS_COUNT +GST_H264_MAX_PPS_COUNT +GST_H264_IS_P_SLICE +GST_H264_IS_B_SLICE +GST_H264_IS_I_SLICE +GST_H264_IS_SP_SLICE +GST_H264_IS_SI_SLICE +GstH264NalUnitType +GstH264ParserResult +GstH264SEIPayloadType +GstH264SEIPicStructType +GstH264SliceType +GstH264NalParser +GstH264NalUnit +GstH264SPS +GstH264PPS +GstH264HRDParams +GstH264VUIParams +GstH264DecRefPicMarking +GstH264RefPicMarking +GstH264PredWeightTable +GstH264SliceHdr +GstH264ClockTimestamp +GstH264PicTiming +GstH264BufferingPeriod +GstH264SEIMessage +gst_h264_parser_identify_nalu +gst_h264_parser_identify_nalu_avc +gst_h264_parser_parse_nal +gst_h264_parser_parse_slice_hdr +gst_h264_parser_parse_sps +gst_h264_parser_parse_pps +gst_h264_parser_parse_sei +gst_h264_nal_parser_new +gst_h264_parse_sps +gst_h264_parse_pps + + +
+
gstmpegvideoparser mpegvideoparser diff --git a/docs/libs/gst-plugins-bad-libs.types b/docs/libs/gst-plugins-bad-libs.types index 4ca1a2b462..493215718a 100644 --- a/docs/libs/gst-plugins-bad-libs.types +++ b/docs/libs/gst-plugins-bad-libs.types @@ -1,3 +1,4 @@ #include +#include #include diff --git a/gst-libs/gst/codecparsers/Makefile.am b/gst-libs/gst/codecparsers/Makefile.am index 86a7b16d9f..7fa44f1585 100644 --- a/gst-libs/gst/codecparsers/Makefile.am +++ b/gst-libs/gst/codecparsers/Makefile.am @@ -1,12 +1,13 @@ lib_LTLIBRARIES = libgstcodecparsers-@GST_MAJORMINOR@.la -libgstcodecparsers_@GST_MAJORMINOR@_la_SOURCES = gstmpegvideoparser.c +libgstcodecparsers_@GST_MAJORMINOR@_la_SOURCES = \ + gstmpegvideoparser.c gsth264parser.c libgstcodecparsers_@GST_MAJORMINOR@includedir = \ $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/codecparsers libgstcodecparsers_@GST_MAJORMINOR@include_HEADERS = \ - gstmpegvideoparser.h + gstmpegvideoparser.h gsth264parser.h libgstcodecparsers_@GST_MAJORMINOR@_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_CFLAGS) libgstcodecparsers_@GST_MAJORMINOR@_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c new file mode 100644 index 0000000000..bdd2899bd0 --- /dev/null +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -0,0 +1,1808 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * Some bits C-c,C-v'ed and s/4/3 from h264parse and videoparsers/h264parse.c: + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Nokia Corporation + * + * (C) 2005 Michal Benes + * (C) 2008 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gsth264parser + * @short_description: Convenience library for h264 video + * bitstream parsing. + * + * It offers you basic parsing in AVC mode or not. Tp identify Nals in a bitstream and + * parse its basic headers, you should call: + * + * + * gst_h264_parser_identify_nalu to identify the following nalu in not AVC bitstreams + * + * + * gst_h264_parser_identify_nalu_avc to identify the following nalu in AVC bitstreams + * + * + * + * Then, depending on the #GstH264NalUnitType of the newly parsed #GstH264NalUnit, you should + * call the differents functions to parse the struct. + * + * Note: You should always call gst_h264_parser_parse_nal if you don't actually need + * #GstH264NalUnitType to be parsed for your personnal use. This, to guarantee that the + * #GstH264NalParser is always up to date. + * + * For more details about the structures, look at the ISO specifications. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gsth264parser.h" + +#include +#include +#include + +GST_DEBUG_CATEGORY (h264_parser_debug); +#define GST_CAT_DEFAULT h264_parser_debug + +/**** Default scaling_lists according to Table 7-2 *****/ +const guint8 default_4x4_intra[16] = { + 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, + 32, 37, 37, 42 +}; + +const guint8 default_4x4_inter[16] = { + 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, + 27, 30, 30, 34 +}; + +const guint8 default_8x8_intra[64] = { + 6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, + 18, 18, 18, 23, 23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, + 27, 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, 31, 33, + 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42 +}; + +const guint8 default_8x8_inter[64] = { + 9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, + 19, 19, 19, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, + 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 28, + 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35 +}; + +const guint8 zigzag_8x8[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +const guint8 zigzag_4x4[16] = { + 0, 1, 4, 8, + 5, 2, 3, 6, + 9, 12, 13, 10, + 7, 11, 14, 15, +}; + +/****** Nal parser ******/ + +typedef struct +{ + const guint8 *data; + guint size; + + guint byte; /* Byte position */ + guint bits_in_cache; /* bitpos in the cache of next bit */ + guint8 first_byte; + guint64 cache; /* cached bytes */ +} NalReader; + +static void +nal_reader_init (NalReader * nr, const guint8 * data, guint size) +{ + nr->data = data; + nr->size = size; + + nr->byte = 0; + nr->bits_in_cache = 0; + /* fill with something other than 0 to detect emulation prevention bytes */ + nr->first_byte = 0xff; + nr->cache = 0xff; +} + +static gboolean +nal_reader_read (NalReader * nr, guint nbits) +{ + if (G_UNLIKELY (nr->byte * 8 + (nbits - nr->bits_in_cache) > nr->size * 8)) { + GST_DEBUG ("Can not read %u bits, bits in cache %u, Byte * 8 %u, size in " + "bits %u", nbits, nr->bits_in_cache, nr->byte * 8, nr->size * 8); + return FALSE; + } + + while (nr->bits_in_cache < nbits) { + guint8 byte; + gboolean check_three_byte; + + check_three_byte = TRUE; + next_byte: + if (G_UNLIKELY (nr->byte >= nr->size)) + return FALSE; + + byte = nr->data[nr->byte++]; + + /* check if the byte is a emulation_prevention_three_byte */ + if (check_three_byte && byte == 0x03 && nr->first_byte == 0x00 && + ((nr->cache & 0xff) == 0)) { + /* next byte goes unconditionally to the cache, even if it's 0x03 */ + check_three_byte = FALSE; + goto next_byte; + } + nr->cache = (nr->cache << 8) | nr->first_byte; + nr->first_byte = byte; + nr->bits_in_cache += 8; + } + + return TRUE; +} + +static inline gboolean +nal_reader_skip (NalReader * nr, guint nbits) +{ + g_return_val_if_fail (nr != NULL, FALSE); + + if (G_UNLIKELY (!nal_reader_read (nr, nbits))) + return FALSE; + + nr->bits_in_cache -= nbits; + + return TRUE; +} + +static inline gboolean +nal_reader_skip_to_byte (NalReader * nr) +{ + g_return_val_if_fail (nr != NULL, FALSE); + + if (nr->bits_in_cache == 0) { + if (G_LIKELY ((nr->size - nr->byte) > 0)) + nr->byte++; + else + return FALSE; + } + + nr->bits_in_cache = 0; + + return TRUE; +} + +static inline guint +nal_reader_get_pos (const NalReader * nr) +{ + return nr->byte * 8 - nr->bits_in_cache; +} + +static inline guint +nal_reader_get_remaining (const NalReader * nr) +{ + return (nr->size - nr->byte) * 8 + nr->bits_in_cache; +} + +#define GST_NAL_READER_READ_BITS(bits) \ +static gboolean \ +nal_reader_get_bits_uint##bits (NalReader *nr, guint##bits *val, guint nbits) \ +{ \ + guint shift; \ + \ + g_return_val_if_fail (nr != NULL, FALSE); \ + g_return_val_if_fail (val != NULL, FALSE); \ + g_return_val_if_fail (nbits <= bits, FALSE); \ + \ + if (!nal_reader_read (nr, nbits)) \ + return FALSE; \ + \ + /* bring the required bits down and truncate */ \ + shift = nr->bits_in_cache - nbits; \ + *val = nr->first_byte >> shift; \ + \ + *val |= nr->cache << (8 - shift); \ + /* mask out required bits */ \ + if (nbits < bits) \ + *val &= ((guint##bits)1 << nbits) - 1; \ + \ + nr->bits_in_cache = shift; \ + \ + return TRUE; \ +} \ + +GST_NAL_READER_READ_BITS (8); +GST_NAL_READER_READ_BITS (16); +GST_NAL_READER_READ_BITS (32); + +#define GST_NAL_READER_PEAK_BITS(bits) \ +static gboolean \ +nal_reader_peek_bits_uint##bits (const NalReader *nr, guint##bits *val, guint nbits) \ +{ \ + NalReader tmp; \ + \ + g_return_val_if_fail (nr != NULL, FALSE); \ + tmp = *nr; \ + return nal_reader_get_bits_uint##bits (&tmp, val, nbits); \ +} + +GST_NAL_READER_PEAK_BITS (8); + +static gboolean +nal_reader_get_ue (NalReader * nr, guint32 * val) +{ + guint i = 0; + guint8 bit; + guint32 value; + + if (G_UNLIKELY (!nal_reader_get_bits_uint8 (nr, &bit, 1))) { + + return FALSE; + } + + while (bit == 0) { + i++; + if G_UNLIKELY + ((!nal_reader_get_bits_uint8 (nr, &bit, 1))) + return FALSE; + } + + g_return_val_if_fail (i <= 32, FALSE); + + if (G_UNLIKELY (!nal_reader_get_bits_uint32 (nr, &value, i))) + return FALSE; + + *val = (1 << i) - 1 + value; + + return TRUE; +} + +static gboolean +nal_reader_get_se (NalReader * nr, gint32 * val) +{ + guint32 value; + + if (G_UNLIKELY (!nal_reader_get_ue (nr, &value))) + return FALSE; + + if (value % 2) + *val = (value / 2) + 1; + else + *val = -(value / 2); + + return TRUE; +} + +#define CHECK_ALLOWED(val, min, max) { \ + if (val < min || val > max) { \ + GST_WARNING ("value not in allowed range. value: %d, range %d-%d", \ + val, min, max); \ + goto error; \ + } \ +} + +#define READ_UINT8(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint8 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint8, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT16(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint16 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint16, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT32(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint32 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint32, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT64(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint64 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint32, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UE(nr, val) { \ + if (!nal_reader_get_ue (nr, &val)) { \ + GST_WARNING ("failed to read UE"); \ + goto error; \ + } \ +} + +#define READ_UE_ALLOWED(nr, val, min, max) { \ + guint32 tmp; \ + READ_UE (nr, tmp); \ + CHECK_ALLOWED (tmp, min, max); \ + val = tmp; \ +} + +#define READ_SE(nr, val) { \ + if (!nal_reader_get_se (nr, &val)) { \ + GST_WARNING ("failed to read SE"); \ + goto error; \ + } \ +} + +#define READ_SE_ALLOWED(nr, val, min, max) { \ + gint32 tmp; \ + READ_SE (nr, tmp); \ + CHECK_ALLOWED (tmp, min, max); \ + val = tmp; \ +} + +/*********** end of nal parser ***************/ + +/***** Utils ****/ +#define EXTENDED_SAR 255 + +static GstH264SPS * +gst_h264_parser_get_sps (GstH264NalParser * nalparser, guint8 sps_id) +{ + GstH264SPS *sps; + + sps = &nalparser->sps[sps_id]; + + if (sps->valid) + return sps; + + return NULL; +} + +static GstH264PPS * +gst_h264_parser_get_pps (GstH264NalParser * nalparser, guint8 pps_id) +{ + GstH264PPS *pps; + + pps = &nalparser->pps[pps_id]; + + if (pps->valid) + return pps; + + return NULL; +} + +static inline void +set_nalu_datas (GstH264NalUnit * nalu) +{ + guint8 *data = nalu->data + nalu->offset; + + nalu->type = (data[0] & 0x1f); + nalu->ref_idc = (data[0] & 0x60) >> 5; + nalu->idr_pic_flag = (nalu->type == 5 ? 1 : 0); + + GST_DEBUG ("Nal type %u, ref_idc %u", nalu->type, nalu->ref_idc); +} + +static inline gint +scan_for_start_codes (const guint8 * data, guint size) +{ + GstByteReader br; + gst_byte_reader_init (&br, data, size); + + /* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */ + return gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100, + 0, size); +} + +static gboolean +gst_h264_parser_more_data (NalReader * nr) +{ + guint remaining; + + remaining = nal_reader_get_remaining (nr); + if (remaining == 0) + return FALSE; + + if (remaining <= 8) { + guint8 rbsp_stop_one_bit; + + if (!nal_reader_peek_bits_uint8 (nr, &rbsp_stop_one_bit, 1)) + return FALSE; + + if (rbsp_stop_one_bit == 1) { + guint8 zero_bits; + + if (remaining == 1) + return FALSE; + + if (!nal_reader_peek_bits_uint8 (nr, &zero_bits, remaining)) + return FALSE; + + if ((zero_bits - (1 << (remaining - 1))) == 0) + return FALSE; + } + } + + return TRUE; +} + +/****** Parsing functions *****/ + +static gboolean +gst_h264_parse_hrd_parameters (GstH264HRDParams * hrd, NalReader * nr) +{ + guint sched_sel_idx; + + GST_DEBUG ("parsing \"HRD Parameters\""); + + READ_UE_ALLOWED (nr, hrd->cpb_cnt_minus1, 0, 31); + READ_UINT8 (nr, hrd->bit_rate_scale, 4); + READ_UINT8 (nr, hrd->cpb_size_scale, 4); + + for (sched_sel_idx = 0; sched_sel_idx <= hrd->cpb_cnt_minus1; sched_sel_idx++) { + READ_UE (nr, hrd->bit_rate_value_minus1[sched_sel_idx]); + READ_UE (nr, hrd->cpb_size_value_minus1[sched_sel_idx]); + } + + READ_UINT8 (nr, hrd->initial_cpb_removal_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->cpb_removal_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->dpb_output_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->time_offset_length, 5); + + return TRUE; + +error: + GST_WARNING ("error parsing \"HRD Parameters\""); + return FALSE; +} + +static gboolean +gst_h264_parse_vui_parameters (GstH264SPS * sps, NalReader * nr) +{ + GstH264VUIParams *vui = &sps->vui_parameters; + + GST_DEBUG ("parsing \"VUI Parameters\""); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + vui->aspect_ratio_idc = 0; + vui->video_format = 5; + vui->video_full_range_flag = 0; + vui->colour_primaries = 2; + vui->transfer_characteristics = 2; + vui->matrix_coefficients = 2; + vui->chroma_sample_loc_type_top_field = 0; + vui->chroma_sample_loc_type_bottom_field = 0; + vui->low_delay_hrd_flag = 0; + + READ_UINT8 (nr, vui->aspect_ratio_info_present_flag, 1); + if (vui->aspect_ratio_info_present_flag) { + READ_UINT8 (nr, vui->aspect_ratio_idc, 8); + if (vui->aspect_ratio_idc == EXTENDED_SAR) { + READ_UINT16 (nr, vui->sar_width, 16); + READ_UINT16 (nr, vui->sar_height, 16); + } + } + + READ_UINT8 (nr, vui->overscan_info_present_flag, 1); + if (vui->overscan_info_present_flag) + READ_UINT8 (nr, vui->overscan_appropriate_flag, 1); + + READ_UINT8 (nr, vui->video_signal_type_present_flag, 1); + if (vui->video_signal_type_present_flag) { + + READ_UINT8 (nr, vui->video_format, 3); + READ_UINT8 (nr, vui->video_full_range_flag, 1); + READ_UINT8 (nr, vui->colour_description_present_flag, 1); + if (vui->colour_description_present_flag) { + READ_UINT8 (nr, vui->colour_primaries, 8); + READ_UINT8 (nr, vui->transfer_characteristics, 8); + READ_UINT8 (nr, vui->matrix_coefficients, 8); + } + } + + READ_UINT8 (nr, vui->chroma_loc_info_present_flag, 1); + if (vui->chroma_loc_info_present_flag) { + READ_UE_ALLOWED (nr, vui->chroma_sample_loc_type_top_field, 0, 5); + READ_UE_ALLOWED (nr, vui->chroma_sample_loc_type_bottom_field, 0, 5); + } + + READ_UINT8 (nr, vui->timing_info_present_flag, 1); + if (vui->timing_info_present_flag) { + READ_UINT32 (nr, vui->num_units_in_tick, 32); + if (vui->num_units_in_tick == 0) + GST_WARNING ("num_units_in_tick = 0 detected in stream " + "(incompliant to H.264 E.2.1)."); + + READ_UINT32 (nr, vui->time_scale, 32); + if (vui->time_scale == 0) + GST_WARNING ("time_scale = 0 detected in stream " + "(incompliant to H.264 E.2.1)."); + + READ_UINT8 (nr, vui->fixed_frame_rate_flag, 1); + } + + READ_UINT8 (nr, vui->nal_hrd_parameters_present_flag, 1); + if (vui->nal_hrd_parameters_present_flag) { + if (!gst_h264_parse_hrd_parameters (&vui->nal_hrd_parameters, nr)) + goto error; + } + + READ_UINT8 (nr, vui->vcl_hrd_parameters_present_flag, 1); + if (vui->vcl_hrd_parameters_present_flag) { + if (!gst_h264_parse_hrd_parameters (&vui->vcl_hrd_parameters, nr)) + goto error; + } + + if (vui->nal_hrd_parameters_present_flag || + vui->vcl_hrd_parameters_present_flag) + READ_UINT8 (nr, vui->low_delay_hrd_flag, 1); + + READ_UINT8 (nr, vui->pic_struct_present_flag, 1); + READ_UINT8 (nr, vui->bitstream_restriction_flag, 1); + if (vui->bitstream_restriction_flag) { + READ_UINT8 (nr, vui->motion_vectors_over_pic_boundaries_flag, 1); + READ_UE (nr, vui->max_bytes_per_pic_denom); + READ_UE_ALLOWED (nr, vui->max_bits_per_mb_denom, 0, 16); + READ_UE_ALLOWED (nr, vui->log2_max_mv_length_horizontal, 0, 16); + READ_UE_ALLOWED (nr, vui->log2_max_mv_length_vertical, 0, 16); + READ_UE_ALLOWED (nr, vui->log2_max_mv_length_vertical, 0, 16); + READ_UE (nr, vui->num_reorder_frames); + READ_UE (nr, vui->max_dec_frame_buffering); + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"VUI Parameters\""); + return FALSE; +} + +static gboolean +gst_h264_parser_parse_scaling_list (NalReader * nr, + guint8 scaling_lists_4x4[6][16], guint8 scaling_lists_8x8[6][64], + const guint8 fallback_4x4_inter[16], const guint8 fallback_4x4_intra[16], + const guint8 fallback_8x8_inter[64], const guint8 fallback_8x8_intra[64], + guint8 n_lists) +{ + guint i; + + GST_DEBUG ("parsing scaling lists"); + + for (i = 0; i < 12; i++) { + gboolean use_default = FALSE; + + if (i < n_lists) { + guint8 scaling_list_present_flag; + + READ_UINT8 (nr, scaling_list_present_flag, 1); + if (scaling_list_present_flag) { + guint8 *scaling_list; + const guint8 *scan; + guint size; + guint j; + guint8 last_scale, next_scale; + + if (i < 6) { + scaling_list = scaling_lists_4x4[i]; + scan = zigzag_4x4; + size = 16; + } else { + scaling_list = scaling_lists_8x8[i - 6]; + scan = zigzag_8x8; + size = 64; + } + + last_scale = 8; + next_scale = 8; + for (j = 0; j < size; j++) { + if (next_scale != 0) { + gint32 delta_scale; + + READ_SE (nr, delta_scale); + next_scale = (last_scale + delta_scale) & 0xff; + } + if (j == 0 && next_scale == 0) { + use_default = TRUE; + break; + } + last_scale = scaling_list[scan[j]] = + (next_scale == 0) ? last_scale : next_scale; + } + } else + use_default = TRUE; + } else + use_default = TRUE; + + if (use_default) { + switch (i) { + case 0: + memcpy (scaling_lists_4x4[0], fallback_4x4_intra, 16); + break; + case 1: + memcpy (scaling_lists_4x4[1], scaling_lists_4x4[0], 16); + break; + case 2: + memcpy (scaling_lists_4x4[2], scaling_lists_4x4[1], 16); + break; + case 3: + memcpy (scaling_lists_4x4[3], fallback_4x4_inter, 16); + break; + case 4: + memcpy (scaling_lists_4x4[4], scaling_lists_4x4[3], 16); + break; + case 5: + memcpy (scaling_lists_4x4[5], scaling_lists_4x4[4], 16); + break; + case 6: + memcpy (scaling_lists_8x8[0], fallback_8x8_intra, 64); + break; + case 7: + memcpy (scaling_lists_8x8[1], fallback_8x8_inter, 64); + break; + case 8: + memcpy (scaling_lists_8x8[2], scaling_lists_8x8[0], 64); + break; + case 9: + memcpy (scaling_lists_8x8[3], scaling_lists_8x8[1], 64); + break; + case 10: + memcpy (scaling_lists_8x8[4], scaling_lists_8x8[2], 64); + break; + case 11: + memcpy (scaling_lists_8x8[5], scaling_lists_8x8[3], 64); + break; + + default: + break; + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing scaling lists"); + return FALSE; +} + +static gboolean +slice_parse_ref_pic_list_reordering (GstH264SliceHdr * slice, NalReader * nr) +{ + GST_DEBUG ("parsing \"Reference picture list reordering\""); + + if (!(slice->type == GST_H264_I_SLICE) && !(slice->type == GST_H264_SI_SLICE)) { + guint8 ref_pic_list_reordering_flag_l0; + guint32 reordering_of_pic_nums_idc; + + READ_UINT8 (nr, ref_pic_list_reordering_flag_l0, 1); + if (ref_pic_list_reordering_flag_l0) + do { + READ_UE (nr, reordering_of_pic_nums_idc); + if (reordering_of_pic_nums_idc == 0 || reordering_of_pic_nums_idc == 1) { + guint32 abs_diff_pic_num_minus1 G_GNUC_UNUSED; + + READ_UE_ALLOWED (nr, abs_diff_pic_num_minus1, 0, + slice->max_pic_num - 1); + } else if (reordering_of_pic_nums_idc == 2) { + guint32 long_term_pic_num; + + READ_UE (nr, long_term_pic_num); + } + } while (reordering_of_pic_nums_idc != 3); + } + + if (slice->type == GST_H264_B_SLICE) { + guint8 ref_pic_list_reordering_flag_l1; + guint32 reordering_of_pic_nums_idc; + + READ_UINT8 (nr, ref_pic_list_reordering_flag_l1, 1); + if (ref_pic_list_reordering_flag_l1) + do { + READ_UE (nr, reordering_of_pic_nums_idc); + if (reordering_of_pic_nums_idc == 0 || reordering_of_pic_nums_idc == 1) { + guint32 abs_diff_num_minus1; + + READ_UE (nr, abs_diff_num_minus1); + } else if (reordering_of_pic_nums_idc == 2) { + guint32 long_term_pic_num; + + READ_UE (nr, long_term_pic_num); + } + } while (reordering_of_pic_nums_idc != 3); + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Reference picture list reordering\""); + return FALSE; +} + +static gboolean +gst_h264_slice_parse_dec_ref_pic_marking (GstH264SliceHdr * slice, + GstH264NalUnit * nalu, NalReader * nr) +{ + GstH264DecRefPicMarking *dec_ref_pic_m; + + GST_DEBUG ("parsing \"Decoded reference picture marking\""); + + dec_ref_pic_m = &slice->dec_ref_pic_marking; + + if (nalu->idr_pic_flag) { + READ_UINT8 (nr, dec_ref_pic_m->no_output_of_prior_pics_flag, 1); + READ_UINT8 (nr, dec_ref_pic_m->long_term_reference_flag, 1); + } else { + READ_UINT8 (nr, dec_ref_pic_m->adaptive_ref_pic_marking_mode_flag, 1); + if (dec_ref_pic_m->adaptive_ref_pic_marking_mode_flag) { + guint32 mem_mgmt_ctrl_op; + GstH264RefPicMarking *refpicmarking; + + dec_ref_pic_m->n_ref_pic_marking = 0; + while (1) { + refpicmarking = + &dec_ref_pic_m->ref_pic_marking[dec_ref_pic_m->n_ref_pic_marking]; + + READ_UE (nr, mem_mgmt_ctrl_op); + if (mem_mgmt_ctrl_op == 0) + break; + + refpicmarking->memory_management_control_operation = mem_mgmt_ctrl_op; + + if (mem_mgmt_ctrl_op == 1 || mem_mgmt_ctrl_op == 3) + READ_UE (nr, refpicmarking->difference_of_pic_nums_minus1); + + if (mem_mgmt_ctrl_op == 2) + READ_UE (nr, refpicmarking->long_term_pic_num); + + if (mem_mgmt_ctrl_op == 3 || mem_mgmt_ctrl_op == 6) + READ_UE (nr, refpicmarking->long_term_frame_idx); + + if (mem_mgmt_ctrl_op == 4) + READ_UE (nr, refpicmarking->max_long_term_frame_idx_plus1); + + dec_ref_pic_m->n_ref_pic_marking++; + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Decoded reference picture marking\""); + return FALSE; +} + +static gboolean +gst_h264_slice_parse_pred_weight_table (GstH264SliceHdr * slice, + NalReader * nr, guint8 chroma_array_type) +{ + GstH264PredWeightTable *p; + gint i; + + GST_DEBUG ("parsing \"Prediction weight table\""); + + p = &slice->pred_weight_table; + + READ_UE_ALLOWED (nr, p->luma_log2_weight_denom, 0, 7); + /* set default values */ + default_luma_weight = 1 << p->luma_log2_weight_denom; + for (i = 0; i < G_N_ELEMENTS (p->luma_weight_l0); i++) + p->luma_weight_l0[i] = default_luma_weight; + memset (p->luma_offset_l0, 0, sizeof (p->luma_offset_l0)); + if (GST_H264_IS_B_SLICE (slice)) { + for (i = 0; i < G_N_ELEMENTS (p->luma_weight_l1); i++) + p->luma_weight_l1[i] = default_luma_weight; + memset (p->luma_offset_l1, 0, sizeof (p->luma_offset_l1)); + } + + if (chroma_array_type != 0) { + READ_UE_ALLOWED (nr, p->chroma_log2_weight_denom, 0, 7); + /* set default values */ + default_chroma_weight = 1 << p->chroma_log2_weight_denom; + for (i = 0; i < G_N_ELEMENTS (p->chroma_weight_l0); i++) { + p->chroma_weight_l0[i][0] = default_chroma_weight; + p->chroma_weight_l0[i][1] = default_chroma_weight; + } + memset (p->chroma_offset_l0, 0, sizeof (p->chroma_offset_l0)); + if (GST_H264_IS_B_SLICE (slice)) { + for (i = 0; i < G_N_ELEMENTS (p->chroma_weight_l1); i++) { + p->chroma_weight_l1[i][0] = default_chroma_weight; + p->chroma_weight_l1[i][1] = default_chroma_weight; + } + memset (p->chroma_offset_l1, 0, sizeof (p->chroma_offset_l1)); + } + } + + for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) { + guint8 luma_weight_l0_flag; + + READ_UINT8 (nr, luma_weight_l0_flag, 1); + if (luma_weight_l0_flag) { + READ_SE_ALLOWED (nr, p->luma_weight_l0[i], -128, 127); + READ_SE_ALLOWED (nr, p->luma_offset_l0[i], -128, 127); + } + if (chroma_array_type != 0) { + guint8 chroma_weight_l0_flag; + gint j; + + READ_UINT8 (nr, chroma_weight_l0_flag, 1); + if (chroma_weight_l0_flag) { + for (j = 0; j < 2; j++) { + READ_SE_ALLOWED (nr, p->chroma_weight_l0[i][j], -128, 127); + READ_SE_ALLOWED (nr, p->chroma_offset_l0[i][j], -128, 127); + } + } + } + } + + if (GST_H264_IS_B_SLICE (slice)) { + for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) { + guint8 luma_weight_l1_flag; + + READ_UINT8 (nr, luma_weight_l1_flag, 1); + if (luma_weight_l1_flag) { + READ_SE_ALLOWED (nr, p->luma_weight_l1[i], -128, 127); + READ_SE_ALLOWED (nr, p->luma_offset_l1[i], -128, 127); + } + if (chroma_array_type != 0) { + guint8 chroma_weight_l1_flag; + gint j; + + READ_UINT8 (nr, chroma_weight_l1_flag, 1); + if (chroma_weight_l1_flag) { + for (j = 0; j < 2; j++) { + READ_SE_ALLOWED (nr, p->chroma_weight_l1[i][j], -128, 127); + READ_SE_ALLOWED (nr, p->chroma_offset_l1[i][j], -128, 127); + } + } + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Prediction weight table\""); + return FALSE; +} + +static gboolean +gst_h264_parser_parse_buffering_period (GstH264NalParser * nalparser, + GstH264BufferingPeriod * per, NalReader * nr) +{ + GstH264SPS *sps; + guint8 sps_id; + + GST_DEBUG ("parsing \"Buffering period\""); + + READ_UE_ALLOWED (nr, sps_id, 0, GST_H264_MAX_SPS_COUNT); + sps = gst_h264_parser_get_sps (nalparser, sps_id); + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + sps_id); + return GST_H264_PARSER_BROKEN_LINK; + } + per->sps = sps; + + if (sps->vui_parameters_present_flag) { + GstH264VUIParams *vui = &sps->vui_parameters; + + if (vui->nal_hrd_parameters_present_flag) { + GstH264HRDParams *hrd = &vui->nal_hrd_parameters; + guint8 sched_sel_idx; + + for (sched_sel_idx = 0; sched_sel_idx <= hrd->cpb_cnt_minus1; + sched_sel_idx++) { + READ_UINT8 (nr, per->nal_initial_cpb_removal_delay[sched_sel_idx], 5); + READ_UINT8 (nr, + per->nal_initial_cpb_removal_delay_offset[sched_sel_idx], 5); + } + } + + if (vui->vcl_hrd_parameters_present_flag) { + GstH264HRDParams *hrd = &vui->vcl_hrd_parameters; + guint8 sched_sel_idx; + + for (sched_sel_idx = 0; sched_sel_idx <= hrd->cpb_cnt_minus1; + sched_sel_idx++) { + READ_UINT8 (nr, per->vcl_initial_cpb_removal_delay[sched_sel_idx], 5); + READ_UINT8 (nr, + per->vcl_initial_cpb_removal_delay_offset[sched_sel_idx], 5); + } + } + } + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Buffering period\""); + return GST_H264_PARSER_ERROR; +} + +static gboolean +gst_h264_parse_clock_timestamp (GstH264ClockTimestamp * tim, + GstH264VUIParams * vui, NalReader * nr) +{ + guint8 full_timestamp_flag; + guint8 time_offset_length; + + GST_DEBUG ("parsing \"Clock timestamp\""); + + /* defalt values */ + tim->time_offset = 0; + + READ_UINT8 (nr, tim->ct_type, 2); + READ_UINT8 (nr, tim->nuit_field_based_flag, 1); + READ_UINT8 (nr, tim->counting_type, 5); + READ_UINT8 (nr, full_timestamp_flag, 1); + READ_UINT8 (nr, tim->discontinuity_flag, 1); + READ_UINT8 (nr, tim->cnt_dropped_flag, 1); + READ_UINT8 (nr, tim->n_frames, 8); + + if (full_timestamp_flag) { + tim->seconds_flag = TRUE; + READ_UINT8 (nr, tim->seconds_value, 6); + + tim->minutes_flag = TRUE; + READ_UINT8 (nr, tim->minutes_value, 6); + + tim->hours_flag = TRUE; + READ_UINT8 (nr, tim->hours_value, 5); + } else { + READ_UINT8 (nr, tim->seconds_flag, 1); + if (tim->seconds_flag) { + READ_UINT8 (nr, tim->seconds_value, 6); + READ_UINT8 (nr, tim->minutes_flag, 1); + if (tim->minutes_flag) { + READ_UINT8 (nr, tim->minutes_value, 6); + READ_UINT8 (nr, tim->hours_flag, 1); + if (tim->hours_flag) + READ_UINT8 (nr, tim->hours_value, 5); + } + } + } + + time_offset_length = 0; + if (vui->nal_hrd_parameters_present_flag) + time_offset_length = vui->nal_hrd_parameters.time_offset_length; + else if (vui->vcl_hrd_parameters_present_flag) + time_offset_length = vui->vcl_hrd_parameters.time_offset_length; + + if (time_offset_length > 0) + READ_UINT32 (nr, tim->time_offset, time_offset_length); + +error: + GST_WARNING ("error parsing \"Clock timestamp\""); + return FALSE; +} + +static gboolean +gst_h264_parser_parse_pic_timing (GstH264NalParser * nalparser, + GstH264PicTiming * tim, NalReader * nr) +{ + GST_DEBUG ("parsing \"Picture timing\""); + if (!nalparser->last_sps || !nalparser->last_sps->valid) { + GST_WARNING ("didn't get the associated sequence paramater set for the " + "current access unit"); + goto error; + } + + /* default values */ + memset (tim->clock_timestamp_flag, 0, 3); + + if (nalparser->last_sps->vui_parameters_present_flag) { + GstH264VUIParams *vui = &nalparser->last_sps->vui_parameters; + + if (vui->nal_hrd_parameters_present_flag) { + READ_UINT32 (nr, tim->cpb_removal_delay, + vui->nal_hrd_parameters.cpb_removal_delay_length_minus1 + 1); + READ_UINT32 (nr, tim->dpb_output_delay, + vui->nal_hrd_parameters.dpb_output_delay_length_minus1 + 1); + } else if (vui->nal_hrd_parameters_present_flag) { + READ_UINT32 (nr, tim->cpb_removal_delay, + vui->vcl_hrd_parameters.cpb_removal_delay_length_minus1 + 1); + READ_UINT32 (nr, tim->dpb_output_delay, + vui->vcl_hrd_parameters.dpb_output_delay_length_minus1 + 1); + } + + if (vui->pic_struct_present_flag) { + const guint8 num_clock_ts_table[9] = { + 1, 1, 1, 2, 2, 3, 3, 2, 3 + }; + guint8 num_clock_num_ts; + guint i; + + READ_UINT8 (nr, tim->pic_struct, 4); + CHECK_ALLOWED (tim->pic_struct, 0, 8); + + num_clock_num_ts = num_clock_ts_table[tim->pic_struct]; + for (i = 0; i < num_clock_num_ts; i++) { + READ_UINT8 (nr, tim->clock_timestamp_flag[i], 1); + if (tim->clock_timestamp_flag[i]) { + if (!gst_h264_parse_clock_timestamp (&tim->clock_timestamp[i], vui, + nr)) + goto error; + } + } + } + } + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Picture timing\""); + return GST_H264_PARSER_ERROR; +} + +/******** API *************/ + +/** + * gst_h264_nal_parser_new: + * + * Creates a nez #GstH264NalParser + * + * Returns: a new #GstH264NalParser + */ +GstH264NalParser * +gst_h264_nal_parser_new (void) +{ + GstH264NalParser *nalparser; + + nalparser = g_malloc0 (sizeof (GstH264NalParser)); + GST_DEBUG_CATEGORY_INIT (h264_parser_debug, "codecparsers_h264", 0, + "h264 parser library"); + + return nalparser; +} + +/** + * gst_h264_parser_identify_nalu: + * @nalparser: a #GstH264NalParser + * @data: The data to parse + * @offset: the offset from which to parse @data + * @size: the size of @data + * @nalu: The #GstH264NalUnit where to store parsed nal headers + * + * Parses the buffer and set @nalu from the next nalu data from @data + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_identify_nalu (GstH264NalParser * nalparser, + const guint8 * data, guint offset, gsize size, GstH264NalUnit * nalu) +{ + gint off1, off2; + + if (size - 4 <= offset) { + GST_DEBUG ("Can't parse, buffer is to small size %u, offset %u", size, + offset); + return GST_H264_PARSER_ERROR; + } + + off1 = scan_for_start_codes (data + offset, size - offset); + + if (off1 < 0) { + GST_DEBUG ("No start code prefix in this buffer"); + return GST_H264_PARSER_NO_NAL; + } + + if (offset + off1 == size - 1) { + GST_DEBUG ("Missing data to identify nal unit"); + + return GST_H264_PARSER_ERROR; + } + + nalu->valid = TRUE; + nalu->sc_offset = offset + off1; + /* sc might have 2 or 3 0-bytes */ + if (nalu->sc_offset > 0 && data[nalu->sc_offset - 1] == 00) + nalu->sc_offset--; + + nalu->offset = offset + off1 + 3; + nalu->data = (guint8 *) data; + set_nalu_datas (nalu); + + off2 = scan_for_start_codes (data + nalu->offset, size - nalu->offset); + if (off2 < 0) { + GST_DEBUG ("Nal start %d, No end found", nalu->offset); + + return GST_H264_PARSER_NO_NAL_END; + } + + if (off2 > 0 && data[nalu->offset + off2 - 1] == 00) + off2--; + + nalu->size = off2; + if (nalu->size < 2) + return GST_H264_PARSER_BROKEN_DATA; + + GST_DEBUG ("Complete nal found. Off: %d, Size: %d", nalu->offset, nalu->size); + return GST_H264_PARSER_OK; +} + +/** + * gst_h264_parser_identify_nalu_avc: + * @data: The data to parse, must be the beging of the Nal unit + * @size: the size of @data + * @nal_length_size: the size in bytes of the AVC nal length prefix. + * @nalu: The #GstH264NalUnit where to store parsed nal headers + * + * Parses the data and sets @nalu from @data. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_identify_nalu_avc (GstH264NalParser * nalparser, + const guint8 * data, guint offset, gsize size, guint8 nal_length_size, + GstH264NalUnit * nalu) +{ + GstBitReader br; + + size = size - offset; + gst_bit_reader_init (&br, data + offset, size); + + gst_bit_reader_get_bits_uint32 (&br, &nalu->size, nal_length_size * 8); + nalu->sc_offset = offset; + nalu->offset = offset + nal_length_size; + + if (size < nalu->size + nal_length_size) { + nalu->size = 0; + + return GST_H264_PARSER_NO_NAL_END; + } + + nalu->data = (guint8 *) data; + + set_nalu_datas (nalu); + + if (nalu->size < 2) + return GST_H264_PARSER_BROKEN_DATA; + + nalu->valid = TRUE; + + return GST_H264_PARSER_OK; +} + +GstH264ParserResult +gst_h264_parser_parse_nal (GstH264NalParser * nalparser, GstH264NalUnit * nalu) +{ + GstH264SPS sps; + GstH264PPS pps; + + switch (nalu->type) { + case GST_H264_NAL_SPS: + return gst_h264_parser_parse_sps (nalparser, nalu, &sps, FALSE); + break; + case GST_H264_NAL_PPS: + return gst_h264_parser_parse_pps (nalparser, nalu, &pps); + } + + return GST_H264_PARSER_OK; +} + +/** + * gst_h264_parser_parse_sps: + * @nalparser: a #GstH264NalParser + * @nalu: The #GST_H264_NAL_SPS #GstH264NalUnit you want to parse + * @slice: The #GstH264SPS to set. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses the @data, and sets the @sps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_sps (GstH264NalParser * nalparser, GstH264NalUnit * nalu, + GstH264SPS * sps, gboolean parse_vui_params) +{ + GstH264ParserResult res = gst_h264_parse_sps (nalu, sps, parse_vui_params); + + if (res == GST_H264_PARSER_OK) { + GST_DEBUG ("adding sequence parameter set with id: %d to array", sps->id); + + nalparser->sps[sps->id] = *sps; + nalparser->last_sps = &nalparser->sps[sps->id]; + } + + + + return res; +} + +/** + * gst_h264_parse_sps: + * @nalu: The #GST_H264_NAL_SPS #GstH264NalUnit you want to parse + * @slice: The #GstH264SPS to set. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses the @data, and sets the @sps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parse_sps (GstH264NalUnit * nalu, GstH264SPS * sps, + gboolean parse_vui_params) +{ + NalReader nr; + gint width, height; + guint8 frame_cropping_flag; + guint subwc[] = { 1, 2, 2, 1 }; + guint subhc[] = { 1, 2, 1, 1 }; + GstH264VUIParams *vui = NULL; + + GST_DEBUG ("parsing SPS"); + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + sps->chroma_format_idc = 1; + sps->separate_colour_plane_flag = 0; + sps->bit_depth_luma_minus8 = 0; + sps->bit_depth_chroma_minus8 = 0; + memset (sps->scaling_lists_4x4, 16, 96); + memset (sps->scaling_lists_8x8, 16, 384); + sps->mb_adaptive_frame_field_flag = 0; + sps->frame_crop_left_offset = 0; + sps->frame_crop_right_offset = 0; + sps->frame_crop_top_offset = 0; + sps->frame_crop_bottom_offset = 0; + + READ_UINT8 (&nr, sps->profile_idc, 8); + READ_UINT8 (&nr, sps->constraint_set0_flag, 1); + READ_UINT8 (&nr, sps->constraint_set1_flag, 1); + READ_UINT8 (&nr, sps->constraint_set2_flag, 1); + READ_UINT8 (&nr, sps->constraint_set3_flag, 1); + + /* skip reserved_zero_4bits */ + if (!nal_reader_skip (&nr, 4)) + goto error; + + READ_UINT8 (&nr, sps->level_idc, 8); + + READ_UE_ALLOWED (&nr, sps->id, 0, GST_H264_MAX_SPS_COUNT); + + if (sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 244 || + sps->profile_idc == 44 || sps->profile_idc == 83 || + sps->profile_idc == 86) { + READ_UE_ALLOWED (&nr, sps->chroma_format_idc, 0, 3); + if (sps->chroma_format_idc == 3) + READ_UINT8 (&nr, sps->separate_colour_plane_flag, 1); + + READ_UE_ALLOWED (&nr, sps->bit_depth_luma_minus8, 0, 6); + READ_UE_ALLOWED (&nr, sps->bit_depth_chroma_minus8, 0, 6); + READ_UINT8 (&nr, sps->qpprime_y_zero_transform_bypass_flag, 1); + + READ_UINT8 (&nr, sps->scaling_matrix_present_flag, 1); + if (sps->scaling_matrix_present_flag) { + guint8 n_lists; + + n_lists = (sps->chroma_format_idc != 3) ? 8 : 12; + if (!gst_h264_parser_parse_scaling_list (&nr, + sps->scaling_lists_4x4, sps->scaling_lists_8x8, + default_4x4_inter, default_4x4_intra, + default_8x8_inter, default_8x8_intra, n_lists)) + goto error; + } + } + + READ_UE_ALLOWED (&nr, sps->log2_max_frame_num_minus4, 0, 12); + + sps->max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4); + + READ_UE_ALLOWED (&nr, sps->pic_order_cnt_type, 0, 2); + if (sps->pic_order_cnt_type == 0) { + READ_UE_ALLOWED (&nr, sps->log2_max_pic_order_cnt_lsb_minus4, 0, 12); + } else if (sps->pic_order_cnt_type == 1) { + guint i; + + READ_UINT8 (&nr, sps->delta_pic_order_always_zero_flag, 1); + READ_SE (&nr, sps->offset_for_non_ref_pic); + READ_SE (&nr, sps->offset_for_top_to_bottom_field); + READ_UE_ALLOWED (&nr, sps->num_ref_frames_in_pic_order_cnt_cycle, 0, 255); + + for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) + READ_SE (&nr, sps->offset_for_ref_frame[i]); + } + + READ_UE (&nr, sps->num_ref_frames); + READ_UINT8 (&nr, sps->gaps_in_frame_num_value_allowed_flag, 1); + READ_UE (&nr, sps->pic_width_in_mbs_minus1); + READ_UE (&nr, sps->pic_height_in_map_units_minus1); + READ_UINT8 (&nr, sps->frame_mbs_only_flag, 1); + + if (!sps->frame_mbs_only_flag) + READ_UINT8 (&nr, sps->mb_adaptive_frame_field_flag, 1); + + READ_UINT8 (&nr, sps->direct_8x8_inference_flag, 1); + READ_UINT8 (&nr, frame_cropping_flag, 1); + if (frame_cropping_flag) { + READ_UE (&nr, sps->frame_crop_left_offset); + READ_UE (&nr, sps->frame_crop_right_offset); + READ_UE (&nr, sps->frame_crop_top_offset); + READ_UE (&nr, sps->frame_crop_bottom_offset); + } + + READ_UINT8 (&nr, sps->vui_parameters_present_flag, 1); + if (sps->vui_parameters_present_flag && parse_vui_params) { + if (!gst_h264_parse_vui_parameters (sps, &nr)) + goto error; + vui = &sps->vui_parameters; + } + + /* calculate ChromaArrayType */ + if (sps->separate_colour_plane_flag) + sps->chroma_array_type = 0; + else + sps->chroma_array_type = sps->chroma_format_idc; + + /* Calculate width and height */ + width = (sps->pic_width_in_mbs_minus1 + 1); + width *= 16; + height = (sps->pic_height_in_map_units_minus1 + 1); + height *= 16 * (2 - sps->frame_mbs_only_flag); + GST_LOG ("initial width=%d, height=%d", width, height); + + width -= (sps->frame_crop_left_offset + sps->frame_crop_right_offset) + * subwc[sps->chroma_format_idc]; + height -= (sps->frame_crop_top_offset + sps->frame_crop_bottom_offset + * subhc[sps->chroma_format_idc] * (2 - sps->frame_mbs_only_flag)); + if (width < 0 || height < 0) { + GST_WARNING ("invalid width/height in SPS"); + return FALSE; + } + GST_LOG ("final width=%u, height=%u", width, height); + sps->width = width; + sps->height = height; + + /* derive framerate */ + /* FIXME verify / also handle other cases */ + GST_LOG ("Framerate: %u %u %u %u", parse_vui_params, + vui->fixed_frame_rate_flag, sps->frame_mbs_only_flag, + vui->pic_struct_present_flag); + + if (parse_vui_params && vui->fixed_frame_rate_flag && + sps->frame_mbs_only_flag && !vui->pic_struct_present_flag) { + sps->fps_num = vui->time_scale; + sps->fps_den = vui->num_units_in_tick; + /* picture is a frame = 2 fields */ + sps->fps_den *= 2; + GST_LOG ("framerate %d/%d", sps->fps_num, sps->fps_den); + } + + sps->valid = TRUE; + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Sequence parameter set\""); + + return GST_H264_PARSER_ERROR; +} + +/** + * gst_h264_parse_pps: + * @nalparser: a #GstH264NalParser + * @data: the data to parse + * @size: the size of @data + * @nalu: The #GST_H264_NAL_PPS #GstH264NalUnit you want to parse + * @slice: The #GstH264PPS to set. + * + * Parses the @data, and sets the @pps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parse_pps (GstH264NalParser * nalparser, GstH264NalUnit * nalu, + GstH264PPS * pps) +{ + NalReader nr; + GstH264SPS *sps; + gint sps_id; + guint8 pic_scaling_matrix_present_flag; + gint qp_bd_offset; + + GST_DEBUG ("parsing PPS"); + + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + + READ_UE_ALLOWED (&nr, pps->id, 0, GST_H264_MAX_PPS_COUNT); + READ_UE_ALLOWED (&nr, sps_id, 0, GST_H264_MAX_SPS_COUNT); + + sps = gst_h264_parser_get_sps (nalparser, sps_id); + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + sps_id); + return GST_H264_PARSER_BROKEN_LINK; + } + pps->sequence = sps; + qp_bd_offset = 6 * (sps->bit_depth_luma_minus8 + + sps->separate_colour_plane_flag); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + pps->slice_group_id = NULL; + pps->transform_8x8_mode_flag = 0; + memcpy (&pps->scaling_lists_4x4, &sps->scaling_lists_4x4, 96); + memcpy (&pps->scaling_lists_8x8, &sps->scaling_lists_8x8, 384); + + READ_UINT8 (&nr, pps->entropy_coding_mode_flag, 1); + READ_UINT8 (&nr, pps->pic_order_present_flag, 1); + READ_UE_ALLOWED (&nr, pps->num_slice_groups_minus1, 0, 7); + if (pps->num_slice_groups_minus1 > 0) { + READ_UE_ALLOWED (&nr, pps->slice_group_map_type, 0, 6); + + if (pps->slice_group_map_type == 0) { + gint i; + + for (i = 0; i <= pps->num_slice_groups_minus1; i++) + READ_UE (&nr, pps->run_length_minus1[i]); + } else if (pps->slice_group_map_type == 2) { + gint i; + + for (i = 0; i <= pps->num_slice_groups_minus1; i++) { + READ_UE (&nr, pps->top_left[i]); + READ_UE (&nr, pps->bottom_right[i]); + } + } else if (pps->slice_group_map_type >= 3 && pps->slice_group_map_type <= 5) { + READ_UINT8 (&nr, pps->slice_group_change_direction_flag, 1); + READ_UE (&nr, pps->slice_group_change_rate_minus1); + } else if (pps->slice_group_map_type == 6) { + gint bits; + gint i; + + READ_UE (&nr, pps->pic_size_in_map_units_minus1); + bits = g_bit_storage (pps->num_slice_groups_minus1); + + pps->slice_group_id = + g_new (guint8, pps->pic_size_in_map_units_minus1 + 1); + for (i = 0; i <= pps->pic_size_in_map_units_minus1; i++) + READ_UINT8 (&nr, pps->slice_group_id[i], bits); + } + } + + READ_UE_ALLOWED (&nr, pps->num_ref_idx_l0_active_minus1, 0, 31); + READ_UE_ALLOWED (&nr, pps->num_ref_idx_l1_active_minus1, 0, 31); + READ_UINT8 (&nr, pps->weighted_pred_flag, 1); + READ_UINT8 (&nr, pps->weighted_bipred_idc, 2); + READ_SE_ALLOWED (&nr, pps->pic_init_qp_minus26, -(26 + qp_bd_offset), 25); + READ_SE_ALLOWED (&nr, pps->pic_init_qs_minus26, -26, 25); + READ_SE_ALLOWED (&nr, pps->chroma_qp_index_offset, -12, 12); + pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset; + READ_UINT8 (&nr, pps->deblocking_filter_control_present_flag, 1); + READ_UINT8 (&nr, pps->constrained_intra_pred_flag, 1); + READ_UINT8 (&nr, pps->redundant_pic_cnt_present_flag, 1); + + if (!gst_h264_parser_more_data (&nr)) + goto done; + + READ_UINT8 (&nr, pps->transform_8x8_mode_flag, 1); + + READ_UINT8 (&nr, pic_scaling_matrix_present_flag, 1); + if (pic_scaling_matrix_present_flag) { + guint8 n_lists; + + n_lists = 6 + ((sps->chroma_format_idc != 3) ? 2 : 6) * + pps->transform_8x8_mode_flag; + + if (sps->scaling_matrix_present_flag) { + if (!gst_h264_parser_parse_scaling_list (&nr, + pps->scaling_lists_4x4, pps->scaling_lists_8x8, + sps->scaling_lists_4x4[0], sps->scaling_lists_4x4[3], + sps->scaling_lists_8x8[0], sps->scaling_lists_8x8[3], n_lists)) + goto error; + } else { + if (!gst_h264_parser_parse_scaling_list (&nr, + pps->scaling_lists_4x4, pps->scaling_lists_8x8, + default_4x4_inter, default_4x4_intra, + default_8x8_inter, default_8x8_intra, n_lists)) + goto error; + } + } + + /* FIXME For some reson second_chroma_qp_index_offset is not always present */ + if (G_UNLIKELY (nr.byte * 8 + (8 - nr.bits_in_cache) > nr.size * 8)) + READ_SE_ALLOWED (&nr, pps->second_chroma_qp_index_offset, -12, 12); + + pps->valid = TRUE; + +done: + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Picture parameter set\""); + return GST_H264_PARSER_ERROR; +} + +/** + * gst_h264_parser_parse_pps: + * @nalparser: a #GstH264NalParser + * @data: the data to parse + * @size: the size of @data + * @nalu: The #GST_H264_NAL_PPS #GstH264NalUnit you want to parse + * @slice: The #GstH264PPS to set. + * + * Parses the @data, and sets the @pps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_pps (GstH264NalParser * nalparser, + GstH264NalUnit * nalu, GstH264PPS * pps) +{ + GstH264ParserResult res = gst_h264_parse_pps (nalparser, nalu, pps); + + if (res == GST_H264_PARSER_OK) { + GST_DEBUG ("adding picture parameter set with id: %d to array", pps->id); + + nalparser->pps[pps->id] = *pps; + nalparser->last_pps = &nalparser->pps[pps->id]; + } + + return res; +} + +/** + * gst_h264_parser_parse_slice_hdr: + * @nalu: The #GST_H264_NAL_SLICE #GstH264NalUnit you want to parse + * @slice: The #GstH264SliceHdr to set. + * @parse_pred_weight_table: Whether to parse the pred_weight_table or not + * @parse_dec_ref_pic_marking: Whether to parse the dec_ref_pic_marking or not + * + * Parses the @data, and sets the @slice. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_slice_hdr (GstH264NalParser * nalparser, + GstH264NalUnit * nalu, GstH264SliceHdr * slice, + gboolean parse_pred_weight_table, gboolean parse_dec_ref_pic_marking) +{ + NalReader nr; + gint pps_id; + GstH264PPS *pps; + GstH264SPS *sps; + + if (!nalu->size) { + GST_DEBUG ("Invalid Nal Unit"); + return GST_H264_PARSER_ERROR; + } + + + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + + READ_UE (&nr, slice->first_mb_in_slice); + READ_UE (&nr, slice->type); + + GST_DEBUG ("parsing \"Slice header\", slice type %u", slice->type); + + READ_UE_ALLOWED (&nr, pps_id, 0, GST_H264_MAX_PPS_COUNT); + pps = gst_h264_parser_get_pps (nalparser, pps_id); + + if (!pps) { + GST_WARNING ("couldn't find associated picture parameter set with id: %d", + pps_id); + + return GST_H264_PARSER_BROKEN_LINK; + } + + slice->pps = pps; + sps = pps->sequence; + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + pps->id); + return GST_H264_PARSER_BROKEN_LINK; + } + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + slice->field_pic_flag = 0; + slice->bottom_field_flag = 0; + slice->delta_pic_order_cnt_bottom = 0; + slice->delta_pic_order_cnt[0] = 0; + slice->delta_pic_order_cnt[1] = 0; + slice->redundant_pic_cnt = 0; + slice->num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_active_minus1; + slice->num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_active_minus1; + slice->disable_deblocking_filter_idc = 0; + slice->slice_alpha_c0_offset_div2 = 0; + + if (sps->separate_colour_plane_flag) + READ_UINT8 (&nr, slice->colour_plane_id, 2); + + READ_UINT16 (&nr, slice->frame_num, sps->log2_max_frame_num_minus4 + 4); + + if (!sps->frame_mbs_only_flag) { + READ_UINT8 (&nr, slice->field_pic_flag, 1); + if (slice->field_pic_flag) + READ_UINT8 (&nr, slice->bottom_field_flag, 1); + } + + /* calculate MaxPicNum */ + if (slice->field_pic_flag) + slice->max_pic_num = sps->max_frame_num; + else + slice->max_pic_num = 2 * sps->max_frame_num; + + if (nalu->type == 5) + READ_UE_ALLOWED (&nr, slice->idr_pic_id, 0, G_MAXUINT16); + + if (sps->pic_order_cnt_type == 0) { + READ_UINT16 (&nr, slice->pic_order_cnt_lsb, + sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + + if (pps->pic_order_present_flag && !slice->field_pic_flag) + READ_SE (&nr, slice->delta_pic_order_cnt_bottom); + } + + if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag) { + READ_SE (&nr, slice->delta_pic_order_cnt[0]); + if (pps->pic_order_present_flag && !slice->field_pic_flag) + READ_SE (&nr, slice->delta_pic_order_cnt[1]); + } + + if (pps->redundant_pic_cnt_present_flag) + READ_UE_ALLOWED (&nr, slice->redundant_pic_cnt, 0, G_MAXINT8); + + if (GST_H264_IS_B_SLICE (slice)) + READ_UINT8 (&nr, slice->direct_spatial_mv_pred_flag, 1); + + if (GST_H264_IS_P_SLICE (slice) || GST_H264_IS_SP_SLICE (slice) || + GST_H264_IS_B_SLICE (slice)) { + guint8 num_ref_idx_active_override_flag; + + READ_UINT8 (&nr, num_ref_idx_active_override_flag, 1); + if (num_ref_idx_active_override_flag) { + READ_UE_ALLOWED (&nr, slice->num_ref_idx_l0_active_minus1, 0, 31); + + if (GST_H264_IS_B_SLICE (slice)) + READ_UE_ALLOWED (&nr, slice->num_ref_idx_l1_active_minus1, 0, 31); + } + } + + if (!slice_parse_ref_pic_list_reordering (slice, &nr)) + goto error; + + if ((pps->weighted_pred_flag && (GST_H264_IS_P_SLICE (slice) + || GST_H264_IS_SP_SLICE (slice))) + || (pps->weighted_bipred_idc == 1 && GST_H264_IS_B_SLICE (slice))) { + if (!gst_h264_slice_parse_pred_weight_table (slice, &nr, + sps->chroma_array_type)) + goto error; + } + + if (nalu->ref_idc != 0) { + if (!gst_h264_slice_parse_dec_ref_pic_marking (slice, nalu, &nr)) + goto error; + } + + if (pps->entropy_coding_mode_flag && !GST_H264_IS_I_SLICE (slice) && + !GST_H264_IS_SI_SLICE (slice)) + READ_UE_ALLOWED (&nr, slice->cabac_init_idc, 0, 2); + + READ_SE_ALLOWED (&nr, slice->slice_qp_delta, -87, 77); + + if (GST_H264_IS_SP_SLICE (slice) || GST_H264_IS_SI_SLICE (slice)) { + guint8 sp_for_switch_flag; + + if (GST_H264_IS_SP_SLICE (slice)) + READ_UINT8 (&nr, sp_for_switch_flag, 1); + READ_SE_ALLOWED (&nr, slice->slice_qs_delta, -51, 51); + } + + if (pps->deblocking_filter_control_present_flag) { + READ_UE_ALLOWED (&nr, slice->disable_deblocking_filter_idc, 0, 2); + if (slice->disable_deblocking_filter_idc != 1) { + READ_SE_ALLOWED (&nr, slice->slice_alpha_c0_offset_div2, -6, 6); + READ_SE_ALLOWED (&nr, slice->slice_beta_offset_div2, -6, 6); + } + } + + if (pps->num_slice_groups_minus1 > 0 && + pps->slice_group_map_type >= 3 && pps->slice_group_map_type <= 5) { + /* Ceil(Log2(PicSizeInMapUnits / SliceGroupChangeRate + 1)) [7-33] */ + guint32 PicWidthInMbs = sps->pic_width_in_mbs_minus1 + 1; + guint32 PicHeightInMapUnits = sps->pic_height_in_map_units_minus1 + 1; + guint32 PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits; + guint32 SliceGroupChangeRate = pps->slice_group_change_rate_minus1 + 1; + const guint n = ceil_log2 (PicSizeInMapUnits / SliceGroupChangeRate + 1); + READ_UINT16 (&nr, slice->slice_group_change_cycle, n); + } + + slice->header_size = nal_reader_get_pos (&nr); + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Slice header\""); + return GST_H264_PARSER_ERROR; +} + +/** + * gst_h264_parser_parse_sei: + * @nalparser: a #GstH264NalParser + * @nalu: The #GST_H264_NAL_SEI #GstH264NalUnit you want to parse + * @slice: The #GstH264SEIMessage to set. + * + * Parses the @data, and sets the @pps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_sei (GstH264NalParser * nalparser, GstH264NalUnit * nalu, + GstH264SEIMessage * sei) +{ + NalReader nr; + + guint32 payloadSize; + guint8 payload_type_byte, payload_size_byte; + guint remaining, payload_size; + gboolean res; + + GST_DEBUG ("parsing \"Sei message\""); + + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + + sei->payloadType = 0; + do { + READ_UINT8 (&nr, payload_type_byte, 8); + sei->payloadType += payload_type_byte; + } while (payload_type_byte == 0xff); + + payloadSize = 0; + do { + READ_UINT8 (&nr, payload_size_byte, 8); + payloadSize += payload_size_byte; + } + while (payload_size_byte == 0xff); + + remaining = nal_reader_get_remaining (&nr) * 8; + payload_size = payloadSize < remaining ? payloadSize : remaining; + + GST_DEBUG ("SEI message received: payloadType %u, payloadSize = %u bytes", + sei->payloadType, payload_size); + + if (sei->payloadType == GST_H264_SEI_BUF_PERIOD) { + /* Set the nal reader size properly */ + nr.size = payload_size; + res = gst_h264_parser_parse_buffering_period (nalparser, + &sei->buffering_period, &nr); + } else if (sei->payloadType == GST_H264_SEI_PIC_TIMING) { + /* Set the nal reader size properly */ + nr.size = payload_size; + res = gst_h264_parser_parse_pic_timing (nalparser, &sei->pic_timing, &nr); + } else + res = GST_H264_PARSER_OK; + + return res; + +error: + GST_WARNING ("error parsing \"Sei message\""); + return GST_H264_PARSER_ERROR; +} diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h new file mode 100644 index 0000000000..c7fae9584d --- /dev/null +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -0,0 +1,657 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * Some bits C-c,C-v'ed and s/4/3 from h264parse and videoparsers/h264parse.c: + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Nokia Corporation + * + * (C) 2005 Michal Benes + * (C) 2008 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_H264_PARSER_H__ +#define __GST_H264_PARSER_H__ + +#include + +G_BEGIN_DECLS + +#define GST_H264_MAX_SPS_COUNT 32 +#define GST_H264_MAX_PPS_COUNT 256 + +#define GST_H264_IS_P_SLICE(slice) (((slice)->type % 5) == GST_H264_P_SLICE) +#define GST_H264_IS_B_SLICE(slice) (((slice)->type % 5) == GST_H264_B_SLICE) +#define GST_H264_IS_I_SLICE(slice) (((slice)->type % 5) == GST_H264_I_SLICE) +#define GST_H264_IS_SP_SLICE(slice) (((slice)->type % 5) == GST_H264_SP_SLICE) +#define GST_H264_IS_SI_SLICE(slice) (((slice)->type % 5) == GST_H264_SI_SLICE) + +/** + * GstH264NalUnitType: + * @GST_H264_NAL_UNKNOWN: Unkonw nal type + * @GST_H264_NAL_SLICE: Slice nal + * @GST_H264_NAL_SLICE_DPA: DPA slice nal + * @GST_H264_NAL_SLICE_DPB: DPB slice nal + * @GST_H264_NAL_SLICE_DPC: DPC slice nal + * @GST_H264_NAL_SLICE_IDR: DPR slice nal + * @GST_H264_NAL_SEI: Supplemental enhancement information nal unit + * @GST_H264_NAL_SPS: Sequence parameter set nal unit + * @GST_H264_NAL_PPS: Picture parameter set nal unit + * @GST_H264_NAL_AU_DELIMITER: Access unit delimiter nal unit + * @GST_H264_NAL_SEQ_END: End of sequence nal unit + * @GST_H264_NAL_STREAM_END: End of stream nal unit + * @GST_H264_NAL_FILTER_DATA: Filler data na lunit + * + * Indicates the type of H264 Nal Units + */ +typedef enum +{ + GST_H264_NAL_UNKNOWN = 0, + GST_H264_NAL_SLICE = 1, + GST_H264_NAL_SLICE_DPA = 2, + GST_H264_NAL_SLICE_DPB = 3, + GST_H264_NAL_SLICE_DPC = 4, + GST_H264_NAL_SLICE_IDR = 5, + GST_H264_NAL_SEI = 6, + GST_H264_NAL_SPS = 7, + GST_H264_NAL_PPS = 8, + GST_H264_NAL_AU_DELIMITER = 9, + GST_H264_NAL_SEQ_END = 10, + GST_H264_NAL_STREAM_END = 11, + GST_H264_NAL_FILLER_DATA = 12 +} GstH264NalUnitType; + +/** + * GstH264ParserResult: + * @GST_H264_PARSER_OK: The parsing succeded + * @GST_H264_PARSER_BROKEN_DATA: The data we parsed where broken + * @GST_H264_PARSER_BROKEN_LINK: The link to a needed struct for the parsing couldn't be found + * @GST_H264_PARSER_ERROR: An error accured when parsing + * @GST_H264_PARSER_NO_NAL: No nal found during the parsing + * @GST_H264_PARSER_NO_NAL_END: Start of the nal found, not the end. + * + * Information about how the parsing of a H264 elements went. + */ +typedef enum +{ + GST_H264_PARSER_OK, + GST_H264_PARSER_BROKEN_DATA, + GST_H264_PARSER_BROKEN_LINK, + GST_H264_PARSER_ERROR, + GST_H264_PARSER_NO_NAL, + GST_H264_PARSER_NO_NAL_END +} GstH264ParserResult; + +/** + * GstH264SEIPayloadType: + * @GST_H264_SEI_BUF_PERIOD: The Sei Message contains a buffering period message + * @GST_H264_SEI_PIC_TIMING: The Sei Message contains a picture timing message + * ... + * + * The type of the SEI message information + */ +typedef enum +{ + GST_H264_SEI_BUF_PERIOD = 0, + GST_H264_SEI_PIC_TIMING = 1 + /* and more... */ +} GstH264SEIPayloadType; + +/** + * GstH264SEIPicStructType: + * @GST_H264_SEI_PIC_STRUCT_FRAME: Picture is a frame + * @GST_H264_SEI_PIC_STRUCT_TOP_FIELD: Top field of frame + * @GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD: Botom field of frame + * @GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM: Top bottom field of frame + * @GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP: bottom top field of frame + * @GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: top bottom top field of frame + * @GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: bottom top bottom field of frame + * @GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING: indicates that the frame should + * be displayed two times consecutively + * @GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING: indicates that the frame should be + * displayed three times consecutively + * + * SEI pic_struct type + */ +typedef enum +{ + GST_H264_SEI_PIC_STRUCT_FRAME = 0, + GST_H264_SEI_PIC_STRUCT_TOP_FIELD = 1, + GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD = 2, + GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM = 3, + GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP = 4, + GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5, + GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, + GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, + GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING = 8 +} GstH264SEIPicStructType; + +typedef enum +{ + GST_H264_P_SLICE = 0, + GST_H264_B_SLICE = 1, + GST_H264_I_SLICE = 2, + GST_H264_SP_SLICE = 3, + GST_H264_SI_SLICE = 4, + GST_H264_S_P_SLICE = 5, + GST_H264_S_B_SLICE = 6, + GST_H264_S_I_SLICE = 7, + GST_H264_S_SP_SLICE = 8, + GST_H264_S_SI_SLICE = 9 +} GstH264SliceType; + +typedef struct _GstH264NalParser GstH264NalParser; + +typedef struct _GstH264NalUnit GstH264NalUnit; + +typedef struct _GstH264SPS GstH264SPS; +typedef struct _GstH264PPS GstH264PPS; +typedef struct _GstH264HRDParams GstH264HRDParams; +typedef struct _GstH264VUIParams GstH264VUIParams; + +typedef struct _GstH264DecRefPicMarking GstH264DecRefPicMarking; +typedef struct _GstH264RefPicMarking GstH264RefPicMarking; +typedef struct _GstH264PredWeightTable GstH264PredWeightTable; +typedef struct _GstH264SliceHdr GstH264SliceHdr; + +typedef struct _GstH264ClockTimestamp GstH264ClockTimestamp; +typedef struct _GstH264PicTiming GstH264PicTiming; +typedef struct _GstH264BufferingPeriod GstH264BufferingPeriod; +typedef struct _GstH264SEIMessage GstH264SEIMessage; + +/** + * GstH264NalUnit: + * @ref_idc: not equal to 0 specifies that the content of the NAL unit contains a sequence + * parameter set, a sequence * parameter set extension, a subset sequence parameter set, a + * picture parameter set, a slice of a reference picture, a slice data partition of a + * reference picture, or a prefix NAL unit preceding a slice of a reference picture. + * @type: A #GstH264NalUnitType + * @idr_pic_flag: calculated idr_pic_flag + * @size: The size of the nal unit starting from @offset + * @offset: The offset of the actual start of the nal unit + * @sc_offset:The offset of the start code of the nal unit + * @valid: If the nal unit is valid, which mean it has + * already been parsed + * @data: The data from which the Nalu has been parsed + * + * Structure defining the Nal unit headers + */ +struct _GstH264NalUnit +{ + guint16 ref_idc; + guint16 type; + + /* calculated values */ + guint8 idr_pic_flag; + guint size; + guint offset; + guint sc_offset; + gboolean valid; + + guint8 *data; +}; + +/** + * GstH264HRDParams: + * @cpb_cnt_minus1: plus 1 specifies the number of alternative + * CPB specifications in the bitstream + * @bit_rate_scale: specifies the maximum input bit rate of the + * SchedSelIdx-th CPB + * @cpb_size_scale: specifies the CPB size of the SchedSelIdx-th CPB + * @guint32 bit_rate_value_minus1: specifies the maximum input bit rate for the + * SchedSelIdx-th CPB + * @cpb_size_value_minus1: is used together with cpb_size_scale to specify the + * SchedSelIdx-th CPB size + * @cbr_flag: Specifies if running in itermediate bitrate mode or constant + * @initial_cpb_removal_delay_length_minus1: specifies the length in bits of + * the cpb_removal_delay syntax element + * @cpb_removal_delay_length_minus1: specifies the length in bits of the + * dpb_output_delay syntax element + * @dpb_output_delay_length_minus1: >0 specifies the length in bits of the time_offset syntax element. + * =0 specifies that the time_offset syntax element is not present + * @time_offset_length: Length of the time offset + * + * Defines the HRD parameters + */ +struct _GstH264HRDParams +{ + guint8 cpb_cnt_minus1; + guint8 bit_rate_scale; + guint8 cpb_size_scale; + + guint32 bit_rate_value_minus1[32]; + guint32 cpb_size_value_minus1[32]; + guint8 cbr_flag[32]; + + guint8 initial_cpb_removal_delay_length_minus1; + guint8 cpb_removal_delay_length_minus1; + guint8 dpb_output_delay_length_minus1; + guint8 time_offset_length; +}; + +/** + * GstH264VUIParams: + * @aspect_ratio_info_present_flag: %TRUE specifies that aspect_ratio_idc is present. + * %FALSE specifies that aspect_ratio_idc is not present + * @aspect_ratio_idc specifies the value of the sample aspect ratio of the luma samples + * @sar_width indicates the horizontal size of the sample aspect ratio + * @sar_height indicates the vertical size of the sample aspect ratio + * @overscan_info_present_flag: %TRUE overscan_appropriate_flag is present %FALSE otherwize + * @overscan_appropriate_flag: %TRUE indicates that the cropped decoded pictures + * output are suitable for display using overscan. %FALSE the cropped decoded pictures + * output contain visually important information + * @video_signal_type_present_flag: %TRUE specifies that video_format, video_full_range_flag and + * colour_description_present_flag are present. + * @video_format: indicates the representation of the picture + * @video_full_range_flag: indicates the black level and range of the luma and chroma signals + * @colour_description_present_flag: %TRUE specifies that colour_primaries, + * transfer_characteristics and matrix_coefficients are present + * @colour_primaries: indicates the chromaticity coordinates of the source primaries + * @transfer_characteristics: indicates the opto-electronic transfer characteristic + * @matrix_coefficients: describes the matrix coefficients used in deriving luma and chroma signals + * @chroma_loc_info_present_flag: %TRUE specifies that chroma_sample_loc_type_top_field and + * chroma_sample_loc_type_bottom_field are present, %FALSE otherwize + * @chroma_sample_loc_type_top_field: specify the location of chroma for top field + * @chroma_sample_loc_type_bottom_field specify the location of chroma for bottom field + * @timing_info_present_flag: %TRUE specifies that num_units_in_tick, + * time_scale and fixed_frame_rate_flag are present in the bitstream + * @num_units_in_tick: is the number of time units of a clock operating at the frequency time_scale Hz + * time_scale: is the number of time units that pass in one second + * @fixed_frame_rate_flag: %TRUE indicates that the temporal distance between the HRD output times + * of any two consecutive pictures in output order is constrained as specified in the spec, %FALSE + * otherwize. + * @nal_hrd_parameters_present_flag: %TRUE if nal hrd parameters present in the bitstream + * @vcl_hrd_parameters_present_flag: %TRUE if nal vlc hrd parameters present in the bitstream + * @low_delay_hrd_flag: specifies the HRD operational mode + * @pic_struct_present_flag: %TRUE specifies that picture timing SEI messages are present or not + * @bitstream_restriction_flag: %TRUE specifies that the following coded video sequence bitstream restriction + * parameters are present + * @motion_vectors_over_pic_boundaries_flag: %FALSE indicates that no sample outside the + * picture boundaries and no sample at a fractional sample position, %TRUE indicates that one or more + * samples outside picture boundaries may be used in inter prediction + * @max_bytes_per_pic_denom: indicates a number of bytes not exceeded by the sum of the sizes of + * the VCL NAL units associated with any coded picture in the coded video sequence. + * @max_bits_per_mb_denom: indicates the maximum number of coded bits of macroblock_layer() + * @log2_max_mv_length_horizontal: indicate the maximum absolute value of a decoded horizontal + * motion vector component + * @log2_max_mv_length_vertical: indicate the maximum absolute value of a decoded vertical + * motion vector component + * @num_reorder_frames: indicates the maximum number of frames, complementary field pairs, + * or non-paired fields that precede any frame, + * @max_dec_frame_buffering: specifies the required size of the HRD decoded picture buffer in + * units of frame buffers. + * + * The structure representing the VUI parameters. + */ +struct _GstH264VUIParams +{ + guint8 aspect_ratio_info_present_flag; + guint8 aspect_ratio_idc; + /* if aspect_ratio_idc == 255 */ + guint16 sar_width; + guint16 sar_height; + + guint8 overscan_info_present_flag; + /* if overscan_info_present_flag */ + guint8 overscan_appropriate_flag; + + guint8 video_signal_type_present_flag; + guint8 video_format; + guint8 video_full_range_flag; + guint8 colour_description_present_flag; + guint8 colour_primaries; + guint8 transfer_characteristics; + guint8 matrix_coefficients; + + guint8 chroma_loc_info_present_flag; + guint8 chroma_sample_loc_type_top_field; + guint8 chroma_sample_loc_type_bottom_field; + + guint8 timing_info_present_flag; + /* if timing_info_present_flag */ + guint32 num_units_in_tick; + guint32 time_scale; + guint8 fixed_frame_rate_flag; + + guint8 nal_hrd_parameters_present_flag; + /* if nal_hrd_parameters_present_flag */ + GstH264HRDParams nal_hrd_parameters; + + guint8 vcl_hrd_parameters_present_flag; + /* if nal_hrd_parameters_present_flag */ + GstH264HRDParams vcl_hrd_parameters; + + guint8 low_delay_hrd_flag; + guint8 pic_struct_present_flag; + + guint8 bitstream_restriction_flag; + /* if bitstream_restriction_flag */ + guint8 motion_vectors_over_pic_boundaries_flag; + guint32 max_bytes_per_pic_denom; + guint32 max_bits_per_mb_denom; + guint32 log2_max_mv_length_horizontal; + guint32 log2_max_mv_length_vertical; + guint32 num_reorder_frames; + guint32 max_dec_frame_buffering; +}; + +/** + * GstH264SPS: + * @id: The ID of the sequence parameter set + * @profile_idc: indicate the profile to which the coded video sequence conforms + * + * + */ +struct _GstH264SPS +{ + gint id; + + guint8 profile_idc; + guint8 constraint_set0_flag; + guint8 constraint_set1_flag; + guint8 constraint_set2_flag; + guint8 constraint_set3_flag; + guint8 level_idc; + + guint8 chroma_format_idc; + guint8 separate_colour_plane_flag; + guint8 bit_depth_luma_minus8; + guint8 bit_depth_chroma_minus8; + guint8 qpprime_y_zero_transform_bypass_flag; + + guint8 scaling_matrix_present_flag; + guint8 scaling_lists_4x4[6][16]; + guint8 scaling_lists_8x8[6][64]; + + guint8 log2_max_frame_num_minus4; + guint8 pic_order_cnt_type; + + /* if pic_order_cnt_type == 0 */ + guint8 log2_max_pic_order_cnt_lsb_minus4; + + /* else if pic_order_cnt_type == 1 */ + guint8 delta_pic_order_always_zero_flag; + gint32 offset_for_non_ref_pic; + gint32 offset_for_top_to_bottom_field; + guint8 num_ref_frames_in_pic_order_cnt_cycle; + gint32 offset_for_ref_frame[255]; + + guint32 num_ref_frames; + guint8 gaps_in_frame_num_value_allowed_flag; + guint32 pic_width_in_mbs_minus1; + guint32 pic_height_in_map_units_minus1; + guint8 frame_mbs_only_flag; + + guint8 mb_adaptive_frame_field_flag; + + guint8 direct_8x8_inference_flag; + + guint8 frame_cropping_flag; + + /* if frame_cropping_flag */ + guint32 frame_crop_left_offset; + guint32 frame_crop_right_offset; + guint32 frame_crop_top_offset; + guint32 frame_crop_bottom_offset; + + guint8 vui_parameters_present_flag; + /* if vui_parameters_present_flag */ + GstH264VUIParams vui_parameters; + + /* calculated values */ + guint8 chroma_array_type; + guint32 max_frame_num; + gint width, height; + gint fps_num, fps_den; + gboolean valid; +}; + +struct _GstH264PPS +{ + gint id; + + GstH264SPS *sequence; + + guint8 entropy_coding_mode_flag; + guint8 pic_order_present_flag; + + guint32 num_slice_groups_minus1; + + /* if num_slice_groups_minus1 > 0 */ + guint8 slice_group_map_type; + /* and if slice_group_map_type == 0 */ + guint32 run_length_minus1[8]; + /* or if slice_group_map_type == 2 */ + guint32 top_left[8]; + guint32 bottom_right[8]; + /* or if slice_group_map_type == (3, 4, 5) */ + guint8 slice_group_change_direction_flag; + guint32 slice_group_change_rate_minus1; + /* or if slice_group_map_type == 6 */ + guint32 pic_size_in_map_units_minus1; + guint8 *slice_group_id; + + guint8 num_ref_idx_l0_active_minus1; + guint8 num_ref_idx_l1_active_minus1; + guint8 weighted_pred_flag; + guint8 weighted_bipred_idc; + gint8 pic_init_qp_minus26; + gint8 pic_init_qs_minus26; + gint8 chroma_qp_index_offset; + guint8 deblocking_filter_control_present_flag; + guint8 constrained_intra_pred_flag; + guint8 redundant_pic_cnt_present_flag; + + guint8 transform_8x8_mode_flag; + + guint8 scaling_lists_4x4[6][16]; + guint8 scaling_lists_8x8[6][64]; + + guint8 second_chroma_qp_index_offset; + + gboolean valid; +}; + +struct _GstH264PredWeightTable +{ + guint8 luma_log2_weight_denom; + guint8 chroma_log2_weight_denom; + + guint8 luma_weight_l0[32]; + guint8 luma_offset_l0[32]; + + /* if seq->ChromaArrayType != 0 */ + guint8 chroma_weight_l0[32][2]; + guint8 chroma_offset_l0[32][2]; + + /* if slice->slice_type % 5 == 1 */ + guint8 luma_weight_l1[32]; + guint8 luma_offset_l1[32]; + /* and if seq->ChromaArrayType != 0 */ + guint8 chroma_weight_l1[32][2]; + guint8 chroma_offset_l1[32][2]; +}; + +struct _GstH264RefPicMarking +{ + guint8 memory_management_control_operation; + + guint32 difference_of_pic_nums_minus1; + guint32 long_term_pic_num; + guint32 long_term_frame_idx; + guint32 max_long_term_frame_idx_plus1; +}; + +struct _GstH264DecRefPicMarking +{ + /* if slice->nal_unit.IdrPicFlag */ + guint8 no_output_of_prior_pics_flag; + guint8 long_term_reference_flag; + + guint8 adaptive_ref_pic_marking_mode_flag; + GstH264RefPicMarking ref_pic_marking[10]; + guint8 n_ref_pic_marking; +}; + + +struct _GstH264SliceHdr +{ + guint32 first_mb_in_slice; + guint32 type; + GstH264PPS *pps; + + /* if seq->separate_colour_plane_flag */ + guint8 colour_plane_id; + + guint16 frame_num; + + guint8 field_pic_flag; + guint8 bottom_field_flag; + + /* if nal_unit.type == 5 */ + guint16 idr_pic_id; + + /* if seq->pic_order_cnt_type == 0 */ + guint16 pic_order_cnt_lsb; + /* if seq->pic_order_present_flag && !field_pic_flag */ + gint32 delta_pic_order_cnt_bottom; + + gint32 delta_pic_order_cnt[2]; + guint8 redundant_pic_cnt; + + /* if slice_type == B_SLICE */ + guint8 direct_spatial_mv_pred_flag; + + guint8 num_ref_idx_l0_active_minus1; + guint8 num_ref_idx_l1_active_minus1; + + GstH264PredWeightTable pred_weight_table; + /* if nal_unit.ref_idc != 0 */ + GstH264DecRefPicMarking dec_ref_pic_marking; + + /* calculated values */ + guint32 max_pic_num; + gboolean valid; +}; + + +struct _GstH264ClockTimestamp +{ + guint8 ct_type; + guint8 nuit_field_based_flag; + guint8 counting_type; + guint8 discontinuity_flag; + guint8 cnt_dropped_flag; + guint8 n_frames; + + guint8 seconds_flag; + guint8 seconds_value; + + guint8 minutes_flag; + guint8 minutes_value; + + guint8 hours_flag; + guint8 hours_value; + + guint32 time_offset; +}; + +struct _GstH264PicTiming +{ + guint32 cpb_removal_delay; + guint32 dpb_output_delay; + + guint8 pic_struct_present_flag; + /* if pic_struct_present_flag */ + guint8 pic_struct; + + guint8 clock_timestamp_flag[3]; + GstH264ClockTimestamp clock_timestamp[3]; +}; + +struct _GstH264BufferingPeriod +{ + GstH264SPS *sps; + + /* seq->vui_parameters->nal_hrd_parameters_present_flag */ + guint8 nal_initial_cpb_removal_delay[32]; + guint8 nal_initial_cpb_removal_delay_offset[32]; + + /* seq->vui_parameters->vcl_hrd_parameters_present_flag */ + guint8 vcl_initial_cpb_removal_delay[32]; + guint8 vcl_initial_cpb_removal_delay_offset[32]; +}; + +struct _GstH264SEIMessage +{ + GstH264SEIPayloadType payloadType; + + union { + GstH264BufferingPeriod buffering_period; + GstH264PicTiming pic_timing; + /* ... could implement more */ + }; +}; + +/* Opaque structure */ +struct _GstH264NalParser +{ + GstH264SPS sps[GST_H264_MAX_SPS_COUNT]; + GstH264PPS pps[GST_H264_MAX_PPS_COUNT]; + GstH264SPS *last_sps; + GstH264PPS *last_pps; +}; + +GstH264NalParser *gst_h264_nal_parser_new (void); + +GstH264ParserResult gst_h264_parser_identify_nalu (GstH264NalParser *nalparser, + const guint8 *data, guint offset, + gsize size, GstH264NalUnit *nalu); + +GstH264ParserResult gst_h264_parser_identify_nalu_avc (GstH264NalParser *nalparser, const guint8 *data, + guint offset, gsize size, guint8 nal_length_size, + GstH264NalUnit *nalu); + +GstH264ParserResult gst_h264_parser_parse_nal (GstH264NalParser *nalparser, + GstH264NalUnit *nalu); + +GstH264ParserResult gst_h264_parser_parse_slice_hdr (GstH264NalParser *nalparser, GstH264NalUnit *nalu, + GstH264SliceHdr *slice, gboolean parse_pred_weight_table, + gboolean parse_dec_ref_pic_marking); + +GstH264ParserResult gst_h264_parser_parse_sps (GstH264NalParser *nalparser, GstH264NalUnit *nalu, + GstH264SPS *sps, gboolean parse_vui_params); + +GstH264ParserResult gst_h264_parser_parse_pps (GstH264NalParser *nalparser, + GstH264NalUnit *nalu, GstH264PPS *pps); + +GstH264ParserResult gst_h264_parser_parse_sei (GstH264NalParser *nalparser, + GstH264NalUnit *nalu, GstH264SEIMessage *sei); + +void gst_h264_nal_parser_free (GstH264NalParser *nalparser); + +GstH264ParserResult gst_h264_parse_sps (GstH264NalUnit *nalu, + GstH264SPS *sps, gboolean parse_vui_params); + +GstH264ParserResult gst_h264_parse_pps (GstH264NalParser *nalparser, + GstH264NalUnit *nalu, GstH264PPS *pps); + +G_END_DECLS +#endif diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 8528eb2352..47bd813c98 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -184,6 +184,7 @@ check_PROGRAMS = \ $(check_mimic) \ elements/rtpmux \ libs/mpegvideoparser \ + libs/h264parser \ $(check_schro) \ $(check_vp8) \ elements/viewfinderbin \ @@ -224,6 +225,15 @@ libs_mpegvideoparser_LDADD = \ $(GST_PLUGINS_BAD_LIBS) -lgstcodecparsers-@GST_MAJORMINOR@ \ $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) +libs_h264parser_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) + +libs_h264parser_LDADD = \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la \ + $(GST_PLUGINS_BAD_LIBS) -lgstcodecparsers-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) + elements_voaacenc_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) diff --git a/tests/check/libs/h264parser.c b/tests/check/libs/h264parser.c new file mode 100644 index 0000000000..3bc8c49e57 --- /dev/null +++ b/tests/check/libs/h264parser.c @@ -0,0 +1,182 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include + +static guint8 slice_dpa[] = { + 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x03, 0x00, + 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x09, 0x00, 0x0a, 0x00, + 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, + 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, + 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, + 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, 0x28, 0x00, + 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00, 0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, + 0x2f, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, + 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x40, 0x00, + 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, + 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00, 0x4c, 0x00, + 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, + 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, + 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, + 0x5f, 0x00, 0x60, 0x00, 0x61, 0x01, 0x04, 0x00, 0xc4, 0x00, 0xa6, 0x00, + 0xc5, 0x00, 0xab, 0x00, 0x82, 0x00, 0xc2, 0x00, 0xd8, 0x00, 0xc6, 0x00, + 0xe4, 0x00, 0xbe, 0x00, 0xb0, 0x00, 0xe6, 0x00, 0xb6, 0x00, 0xb7, 0x00, + 0xb4, 0x00, 0xb5, 0x00, 0x87, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xd9, 0x00, + 0x8c, 0x00, 0xe5, 0x00, 0xbf, 0x00, 0xb1, 0x00, 0xe7, 0x00, 0xbb, 0x00, + 0xa3, 0x00, 0x84, 0x00, 0x85, 0x00, 0xbd, 0x00, 0x96, 0x00, 0xe8, 0x00, + 0x86, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x9d, 0x00, 0xa9, 0x00, 0x8a, 0x01, + 0x05, 0x00, 0x83, 0x00, 0xf2, 0x00, 0xf3, 0x00, 0x8d, 0x00, 0x97, 0x00, + 0x88, 0x00, 0xde, 0x00, 0xf1, 0x00, 0x9e, 0x00, 0xaa, 0x00, 0xf5, 0x00, + 0xf4, 0x00, 0xf6, 0x00, 0xa2, 0x00, 0xad, 0x00, 0xc9, 0x00, 0xc7, 0x00, + 0xae, 0x00, 0x62, 0x00, 0x63, 0x00, 0x90, 0x00, 0x64, 0x00, 0xcb, 0x00, + 0x65, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xcd, 0x00, + 0xce, 0x00, 0xe9, 0x00, 0x66, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xd1, 0x00, + 0xaf, 0x00, 0x67, 0x00, 0x91, 0x00, 0xd6, 0x00, 0xd4, 0x00, 0xd5, 0x00, + 0x68, 0x00, 0xeb, 0x00, 0xed, 0x00, 0x89, 0x00, 0x6a, 0x00, 0x69, 0x00, + 0x6b, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x6e, 0x00, 0xa0, 0x00, 0x6f, 0x00, + 0x71, 0x00, 0x70, 0x00, 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x74, 0x00, + 0x76, 0x00, 0x77, 0x00, 0xea, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x79, 0x00, + 0x7b, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0xa1, 0x00, 0x7f, 0x00, 0x7e, 0x00, + 0x80, 0x00, 0x81, 0x00, 0xec, 0x00, 0xee, 0x00, 0xba, 0x01, 0x06, 0x00, + 0xef, 0x00, 0xe1, 0x00, 0xe0, 0x00, 0xdc, 0x01, 0x07, 0x01, 0x08, 0x01, + 0x09, 0x01, 0x0a, 0x01, 0x0b, 0x01, 0x0c, 0x00, 0xdb, 0x00, 0xe2, 0x01, + 0x0d, 0x01, 0x0e, 0x01, 0x0f, 0x01, 0x10, 0x01, 0x11, 0x01, 0x12, 0x00, + 0xdf, 0x01, 0x13, 0x01, 0x14, 0x01, 0x15, 0x01, 0x16, 0x01, 0x17, 0x00, + 0xfd, 0x00, 0xff, 0x01, 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1b, 0x01, + 0x1c, 0x01, 0x1d, 0x01, 0x1e, 0x01, 0x1f, 0x01, 0x20, 0x01, 0x21, 0x01, + 0x22, 0x01, 0x23, 0x01, 0x24, 0x01, 0x25, 0x01, 0x26, 0x00, 0xfe, 0x01, + 0x00, 0x01, 0x27, 0x01, 0x28, 0x01, 0x29, 0x01, 0x2a, 0x01, 0x2b, 0x01, + 0x2c, 0x01, 0x2d, 0x01, 0x2e, 0x01, 0x2f, 0x01, 0x30, 0x01, 0x31, 0x00, + 0xe3, 0x00, 0xd7, 0x01, 0x32, 0x00, 0xf8, 0x00, 0xf9, 0x01, 0x33, 0x01, + 0x34, 0x01, 0x35, 0x01, 0x36, 0x01, 0x37, 0x01, 0x38, 0x01, 0x39, 0x01, + 0x3a, 0x01, 0x3b, 0x01, 0x3c, 0x01, 0x3d, 0x01, 0x3e, 0x01, 0x3f, 0x01, + 0x40, 0x01, 0x41, 0x01, 0x42, 0x01, 0x43, 0x01, 0x44, 0x01, 0x45, 0x01, + 0x46, 0x01, 0x47, 0x01, 0x48, 0x01, 0x49, 0x01, 0x4a, 0x01, 0x4b, 0x01, + 0x4c, 0x00, 0x08, 0x05, 0x2e, 0x6e, 0x75, 0x6c, 0x6c, 0x0c, 0x76, 0x69, + 0x73, 0x69, 0x62, 0x6c, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x04, 0x45, + 0x75, 0x72, 0x6f, 0x06, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x0a, 0x62, + 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x74, 0x68, 0x06, 0x53, 0x61, + 0x63, 0x75, 0x74, 0x65, 0x06, 0x54, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, + 0x5a, 0x61, 0x63, 0x75, 0x74, 0x65, 0x06, 0x73, 0x61, 0x63, 0x75, 0x74, + 0x65, 0x06, 0x74, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x7a, 0x61, 0x63, + 0x75, 0x74, 0x65, 0x07, 0x41, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, + 0x61, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x0c, 0x73, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x53, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0a, 0x5a, 0x64, + 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x4c, 0x63, 0x61, + 0x72, 0x6f, 0x6e, 0x06, 0x6c, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x0a, 0x7a, + 0x64, 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x52, 0x61, + 0x63, 0x75, 0x74, 0x65, 0x06, 0x41, 0x62, 0x72, 0x65, 0x76, 0x65, 0x06, + 0x4c, 0x61, 0x63, 0x75, 0x74, 0x65, 0x07, 0x45, 0x6f, 0x67, 0x6f, 0x6e, + 0x65, 0x6b, 0x06, 0x45, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x44, 0x63, + 0x61, 0x72, 0x6f, 0x6e, 0x07, 0x44, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, + 0x06, 0x4e, 0x61, 0x63, 0x75, 0x74, 0x65, 0x06, 0x4e, 0x63, 0x61, 0x72, + 0x6f, 0x6e, 0x0d, 0x4f, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, + 0x6c, 0x61, 0x75, 0x74, 0x06, 0x52, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x05, + 0x55, 0x72, 0x69, 0x6e, 0x67, 0x09, 0x6e, 0x75, 0x6e, 0x67, 0x61, 0x64, + 0x65, 0x73, 0x68, 0x0d, 0x55, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, + 0x6d, 0x6c, 0x61, 0x75, 0x74, 0x0c, 0x54, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x72, 0x61, 0x63, 0x75, 0x74, + 0x65, 0x06, 0x61, 0x62, 0x72, 0x65, 0x76, 0x65, 0x06, 0x6c, 0x61, 0x63, + 0x75, 0x74, 0x65, 0x07, 0x65, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x06, + 0x65, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x64, 0x63, 0x61, 0x72, 0x6f, + 0x6e, 0x07, 0x64, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x06, 0x6e, 0x61, + 0x63, 0x75, 0x74, 0x65, 0x06, 0x6e, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x0d, + 0x6f, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, 0x6c, 0x61, 0x75, + 0x74, 0x06, 0x72, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x05, 0x75, 0x72, 0x69, + 0x6e, 0x67, 0x0d, 0x75, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, + 0x6c, 0x61, 0x75, 0x74, 0x0c, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0a, 0x49, 0x64, 0x6f, 0x74, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x52, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x72, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x49, 0x6f, 0x67, 0x6f, 0x6e, + 0x65, 0x6b, 0x07, 0x41, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x07, 0x45, + 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x0a, 0x45, 0x64, 0x6f, 0x74, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x47, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x4b, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x49, 0x6d, 0x61, 0x63, + 0x72, 0x6f, 0x6e, 0x0c, 0x4c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x4e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x4f, 0x6d, 0x61, 0x63, 0x72, 0x6f, + 0x6e, 0x07, 0x55, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x55, 0x6d, + 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x07, 0x69, 0x6f, 0x67, 0x6f, 0x6e, 0x65, + 0x6b, 0x07, 0x61, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x07, 0x65, 0x6d, + 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x0a, 0x65, 0x64, 0x6f, 0x74, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x67, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x6b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x69, 0x6d, 0x61, 0x63, 0x72, + 0x6f, 0x6e, 0x0c, 0x6c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, + 0x65, 0x6e, 0x74, 0x0c, 0x6e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x07, 0x6f, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, + 0x07, 0x75, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x75, 0x6d, 0x61, + 0x63, 0x72, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02 +}; + +GST_START_TEST (test_h264_parse_slice_dpa) +{ + GstH264ParserResult res; + GstH264NalUnit nalu; + + GstH264NalParser *parser = gst_h264_nal_parser_new (); + + res = gst_h264_parser_identify_nalu (parser, slice_dpa, 0, + sizeof (slice_dpa), &nalu); + + assert_equals_int (res, GST_H264_PARSER_OK); + assert_equals_int (nalu.type, GST_H264_NAL_SLICE_DPA); + + g_free (parser); +} + +GST_END_TEST; + +static Suite * +h264parser_suite (void) +{ + Suite *s = suite_create ("H264 Parser library"); + + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_h264_parse_slice_dpa); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = h264parser_suite (); + + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} From b968f82b18326f935c8b8e80e31d6baec2444df2 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 2 Aug 2011 12:37:02 +0200 Subject: [PATCH 60/85] h264parse: Port to the new h.264 parsing library --- gst/videoparsers/Makefile.am | 6 +- gst/videoparsers/gsth264parse.c | 608 ++++++++++++------ gst/videoparsers/gsth264parse.h | 34 +- gst/videoparsers/h264parse.c | 1046 ------------------------------- gst/videoparsers/h264parse.h | 186 ------ 5 files changed, 459 insertions(+), 1421 deletions(-) delete mode 100644 gst/videoparsers/h264parse.c delete mode 100644 gst/videoparsers/h264parse.h diff --git a/gst/videoparsers/Makefile.am b/gst/videoparsers/Makefile.am index 4fa87b8a79..811d5f860a 100644 --- a/gst/videoparsers/Makefile.am +++ b/gst/videoparsers/Makefile.am @@ -2,9 +2,8 @@ plugin_LTLIBRARIES = libgstvideoparsersbad.la libgstvideoparsersbad_la_SOURCES = plugin.c \ h263parse.c gsth263parse.c \ - gsth264parse.c h264parse.c \ gstdiracparse.c dirac_parse.c \ - gstmpegvideoparse.c + gsth264parse.c gstmpegvideoparse.c libgstvideoparsersbad_la_CFLAGS = \ $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ @@ -16,9 +15,8 @@ libgstvideoparsersbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gsth263parse.h h263parse.h \ - gsth264parse.h h264parse.h \ gstdiracparse.h dirac_parse.h \ - gstmpegvideoparse.h + gsth264parse.h gstmpegvideoparse.h Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer \ diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c index 089b5a7878..875f04f96b 100644 --- a/gst/videoparsers/gsth264parse.c +++ b/gst/videoparsers/gsth264parse.c @@ -1,7 +1,10 @@ /* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Collabora ltd * Copyright (C) <2010> Nokia Corporation + * Copyright (C) <2011> Intel Corporation + * + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2011> Thibault Saunier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -163,9 +166,14 @@ gst_h264_parse_finalize (GObject * object) static void gst_h264_parse_reset_frame (GstH264Parse * h264parse) { + GST_DEBUG_OBJECT (h264parse, "reset frame"); + /* done parsing; reset state */ - h264parse->last_nal_pos = 0; - h264parse->next_sc_pos = 0; + h264parse->nalu.valid = FALSE; + h264parse->nalu.offset = 0; + h264parse->nalu.size = 0; + h264parse->current_off = 0; + h264parse->picture_start = FALSE; h264parse->update_caps = FALSE; h264parse->idr_pos = -1; @@ -202,7 +210,13 @@ gst_h264_parse_start (GstBaseParse * parse) GST_DEBUG_OBJECT (parse, "start"); gst_h264_parse_reset (h264parse); - gst_h264_params_create (&h264parse->params, GST_ELEMENT (h264parse)); + h264parse->nalparser = gst_h264_nal_parser_new (); + + h264parse->dts = GST_CLOCK_TIME_NONE; + h264parse->ts_trn_nb = GST_CLOCK_TIME_NONE; + h264parse->sei_pic_struct_pres_flag = FALSE; + h264parse->sei_pic_struct = 0; + h264parse->field_pic_flag = 0; gst_base_parse_set_min_frame_size (parse, 6); @@ -212,13 +226,19 @@ gst_h264_parse_start (GstBaseParse * parse) static gboolean gst_h264_parse_stop (GstBaseParse * parse) { + guint i; GstH264Parse *h264parse = GST_H264_PARSE (parse); GST_DEBUG_OBJECT (parse, "stop"); gst_h264_parse_reset (h264parse); - gst_h264_params_free (h264parse->params); - h264parse->params = NULL; + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) + gst_buffer_replace (&h264parse->sps_nals[i], NULL); + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) + gst_buffer_replace (&h264parse->pps_nals[i], NULL); + + g_free (h264parse->nalparser); + h264parse->nalparser = NULL; return TRUE; } @@ -318,6 +338,8 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data, GstBuffer *buf; const guint nl = h264parse->nal_length_size; + GST_DEBUG ("Nal length %d %d", size, h264parse->nal_length_size); + buf = gst_buffer_new_and_alloc (size + nl + 4); if (format == GST_H264_PARSE_FORMAT_AVC) { GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl)); @@ -332,65 +354,158 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data, return buf; } +static void +gst_h264_parser_store_nal (GstH264Parse * h264parse, guint id, + GstH264NalUnitType naltype, GstH264NalUnit * nalu) +{ + GstBuffer *buf, **store; + guint size = nalu->size, store_size; + + if (naltype == GST_H264_NAL_SPS) { + store_size = GST_H264_MAX_SPS_COUNT; + store = h264parse->sps_nals; + GST_DEBUG ("Storing sps %u", id); + } else if (naltype == GST_H264_NAL_PPS) { + store_size = GST_H264_MAX_PPS_COUNT; + store = h264parse->pps_nals; + GST_DEBUG ("Storing pps %u", id); + } else + return; + + if (id >= store_size) { + GST_DEBUG_OBJECT (h264parse, "unable to store nal, id out-of-range %d", id); + return; + } + + buf = gst_buffer_new_and_alloc (size); + memcpy (GST_BUFFER_DATA (buf), nalu->data + nalu->offset, size); + + if (store[id]) + gst_buffer_unref (store[id]); + + store[id] = buf; +} + /* SPS/PPS/IDR considered key, all others DELTA; * so downstream waiting for keyframe can pick up at SPS/PPS/IDR */ #define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8)) /* caller guarantees 2 bytes of nal payload */ static void -gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, - gint sc_pos, gint nal_pos, guint nal_size) +gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu) { guint nal_type; + GstH264SliceHdr slice; + GstH264PPS pps; + GstH264SPS sps; + GstH264SEIMessage sei; + + gboolean slcparsed = FALSE; + GstH264NalParser *nalparser = h264parse->nalparser; - g_return_if_fail (nal_pos - sc_pos > 0 && nal_pos - sc_pos <= 4); /* nothing to do for broken input */ - if (G_UNLIKELY (nal_size < 2)) { - GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nal_size); + if (G_UNLIKELY (nalu->size < 2)) { + GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nalu->size); return; } - /* lower layer collects params */ - gst_h264_params_parse_nal (h264parse->params, data + nal_pos, nal_size); - /* we have a peek as well */ - nal_type = data[nal_pos] & 0x1f; + nal_type = nalu->type; h264parse->keyframe |= NAL_TYPE_IS_KEY (nal_type); + GST_DEBUG_OBJECT (h264parse, "processing nal of type %u, size %u", + nal_type, nalu->size); + switch (nal_type) { - case NAL_SPS: - case NAL_PPS: + case GST_H264_NAL_SPS: + gst_h264_parser_parse_sps (nalparser, nalu, &sps, TRUE); + + GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); + h264parse->update_caps = TRUE; + /* found in stream, no need to forcibly push at start */ + h264parse->push_codec = FALSE; + + gst_h264_parser_store_nal (h264parse, sps.id, nal_type, nalu); + break; + case GST_H264_NAL_PPS: + gst_h264_parser_parse_pps (nalparser, nalu, &pps); /* parameters might have changed, force caps check */ GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); h264parse->update_caps = TRUE; /* found in stream, no need to forcibly push at start */ h264parse->push_codec = FALSE; + + gst_h264_parser_store_nal (h264parse, pps.id, nal_type, nalu); break; - case NAL_SLICE: - case NAL_SLICE_DPA: - case NAL_SLICE_DPB: - case NAL_SLICE_DPC: + case GST_H264_NAL_SEI: + gst_h264_parser_parse_sei (nalparser, nalu, &sei); + switch (sei.payloadType) { + case GST_H264_SEI_PIC_TIMING: + h264parse->sei_pic_struct_pres_flag = + sei.pic_timing.pic_struct_present_flag; + h264parse->sei_cpb_removal_delay = sei.pic_timing.cpb_removal_delay; + if (h264parse->sei_pic_struct_pres_flag) + h264parse->sei_pic_struct = sei.pic_timing.pic_struct; + break; + case GST_H264_SEI_BUF_PERIOD: + if (h264parse->ts_trn_nb == GST_CLOCK_TIME_NONE || + h264parse->dts == GST_CLOCK_TIME_NONE) + h264parse->ts_trn_nb = 0; + else + h264parse->ts_trn_nb = h264parse->dts; + + GST_LOG_OBJECT (h264parse, + "new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT, + GST_TIME_ARGS (h264parse->ts_trn_nb)); + break; + } + break; + + case GST_H264_NAL_SLICE: + case GST_H264_NAL_SLICE_DPA: + case GST_H264_NAL_SLICE_DPB: + case GST_H264_NAL_SLICE_DPC: + slcparsed = TRUE; + if (gst_h264_parser_parse_slice_hdr (nalparser, nalu, + &slice, FALSE, FALSE) == GST_H264_PARSER_ERROR) + return; + /* real frame data */ - h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0); + h264parse->frame_start |= (slice.first_mb_in_slice == 0); /* if we need to sneak codec NALs into the stream, * this is a good place, so fake it as IDR * (which should be at start anyway) */ + GST_DEBUG ("Frame start: %i first_mb_in_slice %i", h264parse->frame_start, + slice.first_mb_in_slice); if (G_LIKELY (!h264parse->push_codec)) break; /* fall-through */ - case NAL_SLICE_IDR: + case GST_H264_NAL_SLICE_IDR: + if (!slcparsed) { + if (gst_h264_parser_parse_slice_hdr (nalparser, nalu, + &slice, FALSE, FALSE) == GST_H264_PARSER_ERROR) + return; + GST_DEBUG ("Frame start: %i first_mb_in_slice %i", + h264parse->frame_start, slice.first_mb_in_slice); + } /* real frame data */ - h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0); + h264parse->frame_start |= (slice.first_mb_in_slice == 0); + /* mark where config needs to go if interval expired */ /* mind replacement buffer if applicable */ if (h264parse->format == GST_H264_PARSE_FORMAT_AVC) h264parse->idr_pos = gst_adapter_available (h264parse->frame_out); else - h264parse->idr_pos = sc_pos; + h264parse->idr_pos = nalu->offset - 4; GST_DEBUG_OBJECT (h264parse, "marking IDR in frame at offset %d", h264parse->idr_pos); + + GST_DEBUG ("first MB: %u, slice type: %u", slice.first_mb_in_slice, + slice.type); break; + default: + gst_h264_parser_parse_nal (nalparser, nalu); } /* if AVC output needed, collect properly prefixed nal in adapter, @@ -400,7 +515,7 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, GST_LOG_OBJECT (h264parse, "collecting NAL in AVC frame"); buf = gst_h264_parse_wrap_nal (h264parse, h264parse->format, - data + nal_pos, nal_size); + nalu->data + nalu->offset, nalu->size); gst_adapter_push (h264parse->frame_out, buf); } } @@ -408,21 +523,31 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, /* caller guarantees at least 2 bytes of nal payload for each nal * returns TRUE if next_nal indicates that nal terminates an AU */ static inline gboolean -gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal, - guint8 * next_nal) +gst_h264_parse_collect_nal (GstH264Parse * h264parse, const guint8 * data, + guint size, GstH264NalUnit * nalu) { - gint nal_type; gboolean complete; + GstH264ParserResult parse_res; + GstH264NalUnitType nal_type = nalu->type; + GstH264NalUnit nnalu; - if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) + GST_DEBUG ("Parsing collecte nal"); + parse_res = gst_h264_parser_identify_nalu (h264parse->nalparser, data, + nalu->offset + nalu->size, size, &nnalu); + + if (parse_res == GST_H264_PARSER_ERROR) + return FALSE; + + if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) { return TRUE; + } /* determine if AU complete */ - nal_type = nal[0] & 0x1f; GST_LOG_OBJECT (h264parse, "nal type: %d", nal_type); /* coded slice NAL starts a picture, * i.e. other types become aggregated in front of it */ - h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5); + h264parse->picture_start |= (nal_type == GST_H264_NAL_SLICE || + nal_type == GST_H264_NAL_SLICE_DPA || nal_type == GST_H264_NAL_SLICE_IDR); /* consider a coded slices (IDR or not) to start a picture, * (so ending the previous one) if first_mb_in_slice == 0 @@ -431,35 +556,23 @@ gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal, * but in practice it works in sane cases, needs not much parsing, * and also works with broken frame_num in NAL * (where spec-wise would fail) */ - nal_type = next_nal[0] & 0x1f; + nal_type = nnalu.type; + complete = h264parse->picture_start && (nal_type >= GST_H264_NAL_SEI && + nal_type <= GST_H264_NAL_AU_DELIMITER); + GST_LOG_OBJECT (h264parse, "next nal type: %d", nal_type); - complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9); complete |= h264parse->picture_start && - (nal_type == 1 || nal_type == 2 || nal_type == 5) && + (nal_type == GST_H264_NAL_SLICE || + nal_type == GST_H264_NAL_SLICE_DPA || + nal_type == GST_H264_NAL_SLICE_IDR) && /* first_mb_in_slice == 0 considered start of frame */ - (next_nal[1] & 0x80); + (nnalu.data[nnalu.offset + 1] & 0x80); GST_LOG_OBJECT (h264parse, "au complete: %d", complete); return complete; } -/* finds next startcode == 00 00 01, along with a subsequent byte */ -static guint -gst_h264_parse_find_sc (GstBuffer * buffer, guint skip) -{ - GstByteReader br; - guint sc_pos = -1; - - gst_byte_reader_init_from_buffer (&br, buffer); - - /* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */ - sc_pos = gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100, - skip, gst_byte_reader_get_remaining (&br) - skip); - - return sc_pos; -} - /* FIXME move into baseparse, or anything equivalent; * see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */ #define GST_BASE_PARSE_FRAME_FLAG_PARSING 0x10000 @@ -470,10 +583,11 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse, { GstH264Parse *h264parse = GST_H264_PARSE (parse); GstBuffer *buffer = frame->buffer; - gint sc_pos, nal_pos, next_sc_pos, next_nal_pos; guint8 *data; - guint size; + guint size, current_off = 0; gboolean drain; + GstH264NalParser *nalparser = h264parse->nalparser; + GstH264NalUnit nalu = h264parse->nalu; /* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */ if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5)) @@ -495,105 +609,108 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse, data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); - GST_LOG_OBJECT (h264parse, "last_nal_pos: %d, last_scan_pos %d", - h264parse->last_nal_pos, h264parse->next_sc_pos); - - nal_pos = h264parse->last_nal_pos; - next_sc_pos = h264parse->next_sc_pos; - - if (!next_sc_pos) { - sc_pos = gst_h264_parse_find_sc (buffer, 0); - - if (sc_pos == -1) { - /* SC not found, need more data */ - sc_pos = GST_BUFFER_SIZE (buffer) - 3; - /* avoid going < 0 later on */ - nal_pos = next_sc_pos = sc_pos; - goto more; - } - - nal_pos = sc_pos + 3; - next_sc_pos = nal_pos; - /* sc might have 2 or 3 0-bytes */ - if (sc_pos > 0 && data[sc_pos - 1] == 00) - sc_pos--; - GST_LOG_OBJECT (h264parse, "found sc at offset %d", sc_pos); - } else { - /* previous checks already arrange sc at start */ - sc_pos = 0; - } - drain = GST_BASE_PARSE_DRAINING (parse); + current_off = h264parse->current_off; + + GST_DEBUG ("Last parse position %u", current_off); while (TRUE) { - gint prev_sc_pos; + switch (gst_h264_parser_identify_nalu (nalparser, data, current_off, + size, &nalu)) { + case GST_H264_PARSER_OK: + GST_DEBUG ("Complete nal found. %u Off: %u, Size: %u", + current_off, nalu.offset, nalu.size); - next_sc_pos = gst_h264_parse_find_sc (buffer, next_sc_pos); - if (next_sc_pos == -1) { - GST_LOG_OBJECT (h264parse, "no next sc"); - if (drain) { - /* FLUSH/EOS, it's okay if we can't find the next frame */ - next_sc_pos = size; - next_nal_pos = size; - } else { - next_sc_pos = size - 3; - goto more; - } - } else { - next_nal_pos = next_sc_pos + 3; - if (data[next_sc_pos - 1] == 00) - next_sc_pos--; - GST_LOG_OBJECT (h264parse, "found next sc at offset %d", next_sc_pos); - /* need at least 1 more byte of next NAL */ - if (!drain && (next_nal_pos == size - 1)) - goto more; - } - - /* determine nal's sc position */ - prev_sc_pos = nal_pos - 3; - g_assert (prev_sc_pos >= 0); - if (prev_sc_pos > 0 && data[prev_sc_pos - 1] == 0) - prev_sc_pos--; - - /* already consume and gather info from NAL */ - if (G_UNLIKELY (next_sc_pos - nal_pos < 2)) { - GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " - "it contains a NAL unit of length %d", next_sc_pos - nal_pos); - /* broken nal at start -> arrange to skip it, - * otherwise have it terminate current au - * (and so it will be skippd on next frame round) */ - if (prev_sc_pos == sc_pos) { - *skipsize = sc_pos + 2; - return FALSE; - } else { - next_sc_pos = prev_sc_pos; + current_off = nalu.offset + nalu.size; + GST_DEBUG ("CURENT OFF. %u, %u", current_off, nalu.offset + nalu.size); + if (!h264parse->nalu.size && !h264parse->nalu.valid) + h264parse->nalu = nalu; break; - } - } - gst_h264_parse_process_nal (h264parse, data, prev_sc_pos, nal_pos, - next_sc_pos - nal_pos); - if (next_nal_pos >= size - 1 || - gst_h264_parse_collect_nal (h264parse, data + nal_pos, - data + next_nal_pos)) - break; + case GST_H264_PARSER_BROKEN_LINK: + return FALSE; + case GST_H264_PARSER_ERROR: + current_off = size - 3; + goto parsing_error; + case GST_H264_PARSER_NO_NAL: + current_off = size - 3; + goto more; + case GST_H264_PARSER_BROKEN_DATA: + GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " + "it contains a NAL unit of length %d", nalu.size); - /* move along */ - next_sc_pos = nal_pos = next_nal_pos; + /* broken nal at start -> arrange to skip it, + * otherwise have it terminate current au + * (and so it will be skipped on next frame round) */ + if (nalu.sc_offset == h264parse->nalu.sc_offset) { + *skipsize = nalu.offset; + + GST_DEBUG ("Skiping broken nal"); + return FALSE; + } else { + nalu.size = 0; + + goto end; + } + case GST_H264_PARSER_NO_NAL_END: + GST_DEBUG ("Not a complete nal found at offset %u", nalu.offset); + + current_off = nalu.sc_offset; + /* We keep the reference to this nal so we start over the parsing + * here */ + if (!h264parse->nalu.size && !h264parse->nalu.valid) + h264parse->nalu = nalu; + + if (drain) { + GST_DEBUG ("Drainning NAL %u %u %u", size, h264parse->nalu.offset, + h264parse->nalu.size); + /* Can't parse the nalu */ + if (size - h264parse->nalu.offset < 2) { + *skipsize = nalu.offset; + return FALSE; + } + + /* We parse it anyway */ + nalu.size = size - nalu.offset; + break; + } + goto more; + } + + current_off = nalu.offset + nalu.size; + + GST_DEBUG ("%p Complete nal found. Off: %u, Size: %u", data, nalu.offset, + nalu.size); + + gst_h264_parse_process_nal (h264parse, &nalu); + if (gst_h264_parse_collect_nal (h264parse, data, size, &nalu) || drain) + break; } - *skipsize = sc_pos; - *framesize = next_sc_pos - sc_pos; +end: + /* FIXME this shouldnt be needed */ + if (h264parse->nalu.sc_offset > 0 && data[h264parse->nalu.sc_offset - 1] == 0) + h264parse->nalu.sc_offset--; + + *skipsize = h264parse->nalu.sc_offset; + *framesize = nalu.offset + nalu.size - h264parse->nalu.sc_offset; /* CHECKME */ + h264parse->current_off = current_off; return TRUE; +parsing_error: + GST_DEBUG ("Error parsing Nal Unit"); more: + /* ask for best next available */ *framesize = G_MAXUINT; + if (!h264parse->nalu.size) { + /* skip up to initial startcode */ + *skipsize = h264parse->nalu.sc_offset; + } else { + *skipsize = 0; + } - /* skip up to initial startcode */ - *skipsize = sc_pos; - /* resume scanning here next time */ - h264parse->last_nal_pos = nal_pos - sc_pos; - h264parse->next_sc_pos = next_sc_pos - sc_pos; + /* Restart parsing from here next time */ + h264parse->current_off = current_off; return FALSE; } @@ -610,8 +727,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) /* only nal payload in stored nals */ - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((nal = h264parse->sps_nals[i])) { num_sps++; /* size bytes also count */ sps_size += GST_BUFFER_SIZE (nal) + 2; @@ -623,8 +740,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) } } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((nal = h264parse->pps_nals[i])) { num_pps++; /* size bytes also count */ pps_size += GST_BUFFER_SIZE (nal) + 2; @@ -648,8 +765,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) data[5] = 0xe0 | num_sps; /* number of SPSs */ data += 6; - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((nal = h264parse->sps_nals[i])) { GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal)); memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal)); data += 2 + GST_BUFFER_SIZE (nal); @@ -658,8 +775,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) data[0] = num_pps; data++; - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((nal = h264parse->pps_nals[i])) { GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal)); memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal)); data += 2 + GST_BUFFER_SIZE (nal); @@ -672,7 +789,7 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) static void gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) { - GstH264ParamsSPS *sps; + GstH264SPS *sps; GstCaps *sink_caps; gboolean modified = FALSE; GstBuffer *buf = NULL; @@ -695,7 +812,7 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) else sink_caps = gst_caps_new_simple ("video/x-h264", NULL); - sps = h264parse->params->sps; + sps = h264parse->nalparser->last_sps; GST_DEBUG_OBJECT (h264parse, "sps: %p", sps); /* only codec-data for nice-and-clean au aligned packetized avc format */ @@ -764,6 +881,134 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) gst_buffer_unref (buf); } +static void +gst_h264_parse_get_timestamp (GstH264Parse * h264parse, + GstClockTime * out_ts, GstClockTime * out_dur, gboolean frame) +{ + GstH264SPS *sps = h264parse->nalparser->last_sps; + GstClockTime upstream; + gint duration = 1; + + g_return_if_fail (out_dur != NULL); + g_return_if_fail (out_ts != NULL); + + upstream = *out_ts; + + if (!frame) { + GST_LOG_OBJECT (h264parse, "no frame data -> 0 duration"); + *out_dur = 0; + goto exit; + } else { + *out_ts = upstream; + } + + if (!sps) { + GST_DEBUG_OBJECT (h264parse, "referred SPS invalid"); + goto exit; + } else if (!sps->vui_parameters.timing_info_present_flag) { + GST_DEBUG_OBJECT (h264parse, + "unable to compute timestamp: timing info not present"); + goto exit; + } else if (sps->vui_parameters.time_scale == 0) { + GST_DEBUG_OBJECT (h264parse, + "unable to compute timestamp: time_scale = 0 " + "(this is forbidden in spec; bitstream probably contains error)"); + goto exit; + } + + if (h264parse->sei_pic_struct_pres_flag && + h264parse->sei_pic_struct != (guint8) - 1) { + /* Note that when h264parse->sei_pic_struct == -1 (unspecified), there + * are ways to infer its value. This is related to computing the + * TopFieldOrderCnt and BottomFieldOrderCnt, which looks + * complicated and thus not implemented for the time being. Yet + * the value we have here is correct for many applications + */ + switch (h264parse->sei_pic_struct) { + case GST_H264_SEI_PIC_STRUCT_TOP_FIELD: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD: + duration = 1; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME: + case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP: + duration = 2; + break; + case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: + duration = 3; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING: + duration = 4; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING: + duration = 6; + break; + default: + GST_DEBUG_OBJECT (h264parse, + "h264parse->sei_pic_struct of unknown value %d. Not parsed", + h264parse->sei_pic_struct); + break; + } + } else { + duration = h264parse->field_pic_flag ? 1 : 2; + } + + GST_LOG_OBJECT (h264parse, "frame tick duration %d", duration); + + /* + * h264parse.264 C.1.2 Timing of coded picture removal (equivalent to DTS): + * Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000 + * Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n) + * where + * Tc = num_units_in_tick / time_scale + */ + + if (h264parse->ts_trn_nb != GST_CLOCK_TIME_NONE) { + GST_LOG_OBJECT (h264parse, "buffering based ts"); + /* buffering period is present */ + if (upstream != GST_CLOCK_TIME_NONE) { + /* If upstream timestamp is valid, we respect it and adjust current + * reference point */ + h264parse->ts_trn_nb = upstream - + (GstClockTime) gst_util_uint64_scale_int + (h264parse->sei_cpb_removal_delay * GST_SECOND, + sps->vui_parameters.num_units_in_tick, + sps->vui_parameters.time_scale); + } else { + /* If no upstream timestamp is given, we write in new timestamp */ + upstream = h264parse->dts = h264parse->ts_trn_nb + + (GstClockTime) gst_util_uint64_scale_int + (h264parse->sei_cpb_removal_delay * GST_SECOND, + sps->vui_parameters.num_units_in_tick, + sps->vui_parameters.time_scale); + } + } else { + GstClockTime dur; + + GST_LOG_OBJECT (h264parse, "duration based ts"); + /* naive method: no removal delay specified + * track upstream timestamp and provide best guess frame duration */ + dur = gst_util_uint64_scale_int (duration * GST_SECOND, + sps->vui_parameters.num_units_in_tick, sps->vui_parameters.time_scale); + /* sanity check */ + if (dur < GST_MSECOND) { + GST_DEBUG_OBJECT (h264parse, "discarding dur %" GST_TIME_FORMAT, + GST_TIME_ARGS (dur)); + } else { + *out_dur = dur; + } + } + +exit: + if (GST_CLOCK_TIME_IS_VALID (upstream)) + *out_ts = h264parse->dts = upstream; + + if (GST_CLOCK_TIME_IS_VALID (*out_dur) && + GST_CLOCK_TIME_IS_VALID (h264parse->dts)) + h264parse->dts += *out_dur; +} + static GstFlowReturn gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) { @@ -776,7 +1021,7 @@ gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_h264_parse_update_src_caps (h264parse, NULL); - gst_h264_params_get_timestamp (h264parse->params, + gst_h264_parse_get_timestamp (h264parse, &GST_BUFFER_TIMESTAMP (buffer), &GST_BUFFER_DURATION (buffer), h264parse->frame_start); @@ -864,16 +1109,16 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) { /* send separate config NAL buffers */ GST_DEBUG_OBJECT (h264parse, "- sending SPS/PPS"); - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((codec_nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((codec_nal = h264parse->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending SPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); h264parse->last_report = new_ts; } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((codec_nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((codec_nal = h264parse->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending PPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); @@ -890,8 +1135,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer), h264parse->idr_pos); GST_DEBUG_OBJECT (h264parse, "- inserting SPS/PPS"); - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((codec_nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((codec_nal = h264parse->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting SPS nal"); gst_byte_writer_put_uint32_be (&bw, bs ? 1 : GST_BUFFER_SIZE (codec_nal)); @@ -900,8 +1145,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) h264parse->last_report = new_ts; } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((codec_nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((codec_nal = h264parse->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting PPS nal"); gst_byte_writer_put_uint32_be (&bw, bs ? 1 : GST_BUFFER_SIZE (codec_nal)); @@ -937,7 +1182,9 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) GstStructure *str; const GValue *value; GstBuffer *codec_data = NULL; - guint size, format, align; + guint size, format, align, off; + GstH264NalUnit nalu; + GstH264ParserResult parseres; h264parse = GST_H264_PARSE (parse); @@ -959,7 +1206,7 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) if (format != GST_H264_PARSE_FORMAT_BYTE && (value = gst_structure_get_value (str, "codec_data"))) { guint8 *data; - guint num_sps, num_pps, profile, len; + guint num_sps, num_pps, profile; gint i; GST_DEBUG_OBJECT (h264parse, "have packetized h264"); @@ -992,28 +1239,29 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) GST_DEBUG_OBJECT (h264parse, "nal length %u", h264parse->nal_length_size); num_sps = data[5] & 0x1f; - data += 6; - size -= 6; + off = 6; for (i = 0; i < num_sps; i++) { - len = GST_READ_UINT16_BE (data); - if (size < len + 2 || len < 2) + parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser, + data, off, size - off, 2, &nalu); + if (parseres != GST_H264_PARSER_OK) goto avcc_too_small; - /* digest for later reference */ - gst_h264_parse_process_nal (h264parse, data, 0, 2, len); - data += len + 2; - size -= len + 2; + + gst_h264_parse_process_nal (h264parse, &nalu); + off = nalu.offset + nalu.size; } + num_pps = data[0]; data++; size++; for (i = 0; i < num_pps; i++) { - len = GST_READ_UINT16_BE (data); - if (size < len + 2 || len < 2) + parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser, + data, off, size - off, 2, &nalu); + if (parseres != GST_H264_PARSER_OK) { goto avcc_too_small; - /* digest for later reference */ - gst_h264_parse_process_nal (h264parse, data, 0, 2, len); - data += len + 2; - size -= len + 2; + } + + gst_h264_parse_process_nal (h264parse, &nalu); + off = nalu.offset + nalu.size; } h264parse->codec_data = gst_buffer_ref (codec_data); @@ -1089,10 +1337,12 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) GstBuffer *sub; GstFlowReturn ret = GST_FLOW_OK; guint32 len; + GstH264NalUnit nalu; const guint nl = h264parse->nal_length_size; GST_LOG_OBJECT (h264parse, "processing packet buffer of size %d", GST_BUFFER_SIZE (buffer)); + gst_byte_reader_init_from_buffer (&br, buffer); while (ret == GST_FLOW_OK && gst_byte_reader_get_remaining (&br)) { GST_DEBUG_OBJECT (h264parse, "AVC nal offset %d", @@ -1114,9 +1364,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) break; default: goto not_negotiated; - break; } + GST_DEBUG_OBJECT (h264parse, "AVC nal size %d", len); + if (gst_byte_reader_get_remaining (&br) < len) goto parse_failed; if (h264parse->split_packetized) { @@ -1133,9 +1384,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) /* NOTE: so if it is really configured to do so, * pre_push can/will still insert codec-data at intervals, * which is not really pure pass-through, but anyway ... */ - gst_h264_parse_process_nal (h264parse, + gst_h264_parser_identify_nalu (h264parse->nalparser, GST_BUFFER_DATA (buffer), gst_byte_reader_get_pos (&br) - nl, - gst_byte_reader_get_pos (&br), len); + GST_BUFFER_SIZE (buffer), &nalu); + gst_h264_parse_process_nal (h264parse, &nalu); gst_byte_reader_skip_unchecked (&br, len); } } diff --git a/gst/videoparsers/gsth264parse.h b/gst/videoparsers/gsth264parse.h index 1aa1323211..e013a3faca 100644 --- a/gst/videoparsers/gsth264parse.h +++ b/gst/videoparsers/gsth264parse.h @@ -1,7 +1,10 @@ /* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Collabora ltd * Copyright (C) <2010> Nokia Corporation + * Copyright (C) <2011> Intel Corporation + * + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2011> Thibault Saunier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,8 +27,7 @@ #include #include - -#include "h264parse.h" +#include G_BEGIN_DECLS @@ -61,16 +63,34 @@ struct _GstH264Parse gboolean packetized; /* state */ - GstH264Params *params; + GstH264NalParser *nalparser; + GstH264NalUnit nalu; guint align; guint format; + guint current_off; GstClockTime last_report; gboolean push_codec; + /* collected SPS and PPS NALUs */ + GstBuffer *sps_nals[GST_H264_MAX_SPS_COUNT]; + GstBuffer *pps_nals[GST_H264_MAX_PPS_COUNT]; + + /* Infos we need to keep track of */ + guint32 sei_cpb_removal_delay; + guint8 sei_pic_struct; + guint8 sei_pic_struct_pres_flag; + guint field_pic_flag; + + /* cached timestamps */ + /* (trying to) track upstream dts and interpolate */ + GstClockTime dts; + /* dts at start of last buffering period */ + GstClockTime ts_trn_nb; + /* frame parsing */ - guint last_nal_pos; - guint next_sc_pos; + /*guint last_nal_pos;*/ + /*guint next_sc_pos;*/ gint idr_pos; gboolean update_caps; GstAdapter *frame_out; diff --git a/gst/videoparsers/h264parse.c b/gst/videoparsers/h264parse.c deleted file mode 100644 index a556d449cc..0000000000 --- a/gst/videoparsers/h264parse.c +++ /dev/null @@ -1,1046 +0,0 @@ -/* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia - * Copyright (C) <2010> Nokia Corporation - * - * Some bits C-c,C-v'ed and s/4/3 from h264parse: - * (C) 2005 Michal Benes - * (C) 2008 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "h264parse.h" - -#include - -GST_DEBUG_CATEGORY_EXTERN (h264_parse_debug); -#define GST_CAT_DEFAULT h264_parse_debug - -/* simple bitstream parser, automatically skips over - * emulation_prevention_three_bytes. */ -typedef struct -{ - const guint8 *orig_data; - const guint8 *data; - const guint8 *end; - /* bitpos in the cache of next bit */ - gint head; - /* cached bytes */ - guint64 cache; -} GstNalBs; - -static void -gst_nal_bs_init (GstNalBs * bs, const guint8 * data, guint size) -{ - bs->orig_data = data; - bs->data = data; - bs->end = data + size; - bs->head = 0; - /* fill with something other than 0 to detect emulation prevention bytes */ - bs->cache = 0xffffffff; -} - -static inline void -gst_nal_bs_get_data (GstNalBs * bs, const guint8 ** data, guint * size) -{ - *data = bs->orig_data; - *size = bs->end - bs->orig_data; -} - -static guint32 -gst_nal_bs_read (GstNalBs * bs, guint n) -{ - guint32 res = 0; - gint shift; - - if (n == 0) - return res; - - /* fill up the cache if we need to */ - while (bs->head < n) { - guint8 byte; - gboolean check_three_byte; - - check_three_byte = TRUE; - next_byte: - if (bs->data >= bs->end) { - /* we're at the end, can't produce more than head number of bits */ - n = bs->head; - break; - } - /* get the byte, this can be an emulation_prevention_three_byte that we need - * to ignore. */ - byte = *bs->data++; - if (check_three_byte && byte == 0x03 && ((bs->cache & 0xffff) == 0)) { - /* next byte goes unconditionally to the cache, even if it's 0x03 */ - check_three_byte = FALSE; - goto next_byte; - } - /* shift bytes in cache, moving the head bits of the cache left */ - bs->cache = (bs->cache << 8) | byte; - bs->head += 8; - } - - /* bring the required bits down and truncate */ - if ((shift = bs->head - n) > 0) - res = bs->cache >> shift; - else - res = bs->cache; - - /* mask out required bits */ - if (n < 32) - res &= (1 << n) - 1; - - bs->head = shift; - - return res; -} - -static gboolean -gst_nal_bs_eos (GstNalBs * bs) -{ - return (bs->data >= bs->end) && (bs->head == 0); -} - -/* read unsigned Exp-Golomb code */ -static gint -gst_nal_bs_read_ue (GstNalBs * bs) -{ - gint i = 0; - - while (gst_nal_bs_read (bs, 1) == 0 && !gst_nal_bs_eos (bs) && i < 32) - i++; - - return ((1 << i) - 1 + gst_nal_bs_read (bs, i)); -} - -/* read signed Exp-Golomb code */ -static gint -gst_nal_bs_read_se (GstNalBs * bs) -{ - gint i = 0; - - i = gst_nal_bs_read_ue (bs); - /* (-1)^(i+1) Ceil (i / 2) */ - i = (i + 1) / 2 * (i & 1 ? 1 : -1); - - return i; -} - -/* end parser helper */ - -static void -gst_h264_params_store_nal (GstH264Params * params, GstBuffer ** store, - gint store_size, gint id, GstNalBs * bs) -{ - const guint8 *data; - GstBuffer *buf; - guint size; - - if (id >= store_size) { - GST_DEBUG_OBJECT (params->el, - "unable to store nal, id out-of-range %d", id); - return; - } - - gst_nal_bs_get_data (bs, &data, &size); - buf = gst_buffer_new_and_alloc (size); - memcpy (GST_BUFFER_DATA (buf), data, size); - - if (store[id]) - gst_buffer_unref (store[id]); - - store[id] = buf; -} - -static GstH264ParamsSPS * -gst_h264_params_get_sps (GstH264Params * params, guint8 sps_id, gboolean set) -{ - GstH264ParamsSPS *sps; - - g_return_val_if_fail (params != NULL, NULL); - - if (G_UNLIKELY (sps_id >= MAX_SPS_COUNT)) { - GST_WARNING_OBJECT (params->el, - "requested sps_id=%04x out of range", sps_id); - return NULL; - } - - sps = ¶ms->sps_buffers[sps_id]; - if (set) { - if (sps->valid) { - params->sps = sps; - } else { - GST_WARNING_OBJECT (params->el, "invalid sps not selected"); - params->sps = NULL; - sps = NULL; - } - } - - return sps; -} - -static GstH264ParamsPPS * -gst_h264_params_get_pps (GstH264Params * params, guint8 pps_id, gboolean set) -{ - GstH264ParamsPPS *pps; - - g_return_val_if_fail (params != NULL, NULL); - - pps = ¶ms->pps_buffers[pps_id]; - if (set) { - if (pps->valid) { - params->pps = pps; - } else { - GST_WARNING_OBJECT (params->el, "invalid pps not selected"); - params->pps = NULL; - pps = NULL; - } - } - - return pps; -} - -static gboolean -gst_h264_params_decode_sps_vui_hrd (GstH264Params * params, - GstH264ParamsSPS * sps, GstNalBs * bs) -{ - gint sched_sel_idx; - - sps->cpb_cnt_minus1 = gst_nal_bs_read_ue (bs); - if (sps->cpb_cnt_minus1 > 31U) { - GST_WARNING_OBJECT (params->el, "cpb_cnt_minus1 = %d out of range", - sps->cpb_cnt_minus1); - return FALSE; - } - - /* bit_rate_scale */ - gst_nal_bs_read (bs, 4); - /* cpb_size_scale */ - gst_nal_bs_read (bs, 4); - - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; sched_sel_idx++) { - /* bit_rate_value_minus1 */ - gst_nal_bs_read_ue (bs); - /* cpb_size_value_minus1 */ - gst_nal_bs_read_ue (bs); - /* cbr_flag */ - gst_nal_bs_read (bs, 1); - } - - sps->initial_cpb_removal_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->cpb_removal_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->dpb_output_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->time_offset_length_minus1 = gst_nal_bs_read (bs, 5); - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sps_vui (GstH264Params * params, GstH264ParamsSPS * sps, - GstNalBs * bs) -{ - if (G_UNLIKELY (!sps)) - return FALSE; - - /* aspect_ratio_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* aspect_ratio_idc */ - if (gst_nal_bs_read (bs, 8) == 255) { - /* Extended_SAR */ - /* sar_width */ - gst_nal_bs_read (bs, 16); - /* sar_height */ - gst_nal_bs_read (bs, 16); - } - } - - /* overscan_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* overscan_appropriate_flag */ - gst_nal_bs_read (bs, 1); - } - - /* video_signal_type_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* video_format */ - gst_nal_bs_read (bs, 3); - /* video_full_range_flag */ - gst_nal_bs_read (bs, 1); - - /* colour_description_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* colour_primaries */ - gst_nal_bs_read (bs, 8); - /* transfer_characteristics */ - gst_nal_bs_read (bs, 8); - /* matrix_coefficients */ - gst_nal_bs_read (bs, 8); - } - } - - /* chroma_loc_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* chroma_sample_loc_type_top_field */ - gst_nal_bs_read_ue (bs); - /* chroma_sample_loc_type_bottom_field */ - gst_nal_bs_read_ue (bs); - } - - sps->timing_info_present_flag = gst_nal_bs_read (bs, 1); - if (sps->timing_info_present_flag) { - guint32 num_units_in_tick = gst_nal_bs_read (bs, 32); - guint32 time_scale = gst_nal_bs_read (bs, 32); - - /* If any of these parameters = 0, discard all timing_info */ - if (time_scale == 0) { - GST_WARNING_OBJECT (params->el, - "time_scale = 0 detected in stream (incompliant to H.264 E.2.1)." - " Discarding related info."); - } else if (num_units_in_tick == 0) { - GST_WARNING_OBJECT (params->el, - "num_units_in_tick = 0 detected in stream (incompliant to H.264 E.2.1)." - " Discarding related info."); - } else { - sps->num_units_in_tick = num_units_in_tick; - sps->time_scale = time_scale; - sps->fixed_frame_rate_flag = gst_nal_bs_read (bs, 1); - GST_LOG_OBJECT (params->el, "timing info: dur=%d/%d fixed=%d", - num_units_in_tick, time_scale, sps->fixed_frame_rate_flag); - } - } - - sps->nal_hrd_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->nal_hrd_parameters_present_flag) { - gst_h264_params_decode_sps_vui_hrd (params, sps, bs); - } - sps->vcl_hrd_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->vcl_hrd_parameters_present_flag) { - gst_h264_params_decode_sps_vui_hrd (params, sps, bs); - } - if (sps->nal_hrd_parameters_present_flag - || sps->vcl_hrd_parameters_present_flag) { - gst_nal_bs_read (bs, 1); /* low_delay_hrd_flag */ - } - - sps->pic_struct_present_flag = gst_nal_bs_read (bs, 1); - - /* derive framerate */ - /* FIXME verify / also handle other cases */ - if (sps->fixed_frame_rate_flag && sps->frame_mbs_only_flag && - !sps->pic_struct_present_flag) { - sps->fps_num = sps->time_scale; - sps->fps_den = sps->num_units_in_tick; - /* picture is a frame = 2 fields */ - sps->fps_den *= 2; - GST_LOG_OBJECT (params->el, "framerate %d/%d", sps->fps_num, sps->fps_den); - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sps (GstH264Params * params, GstNalBs * bs) -{ - guint8 profile_idc, level_idc; - guint8 sps_id; - GstH264ParamsSPS *sps = NULL; - guint subwc[] = { 1, 2, 2, 1 }; - guint subhc[] = { 1, 2, 1, 1 }; - guint chroma; - guint fc_top, fc_bottom, fc_left, fc_right; - gint width, height; - - profile_idc = gst_nal_bs_read (bs, 8); - /* constraint_set0_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* reserved */ - gst_nal_bs_read (bs, 4); - level_idc = gst_nal_bs_read (bs, 8); - - sps_id = gst_nal_bs_read_ue (bs); - sps = gst_h264_params_get_sps (params, sps_id, FALSE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - gst_h264_params_store_nal (params, params->sps_nals, MAX_SPS_COUNT, sps_id, - bs); - - /* could be redefined mid stream, arrange for clear state */ - memset (sps, 0, sizeof (*sps)); - - GST_LOG_OBJECT (params->el, "sps id %d", sps_id); - sps->valid = TRUE; - /* validate and force activate this one if it is the first SPS we see */ - if (params->sps == NULL) - params->sps = sps; - - sps->profile_idc = profile_idc; - sps->level_idc = level_idc; - - if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 - || profile_idc == 244 || profile_idc == 44 || - profile_idc == 83 || profile_idc == 86) { - gint scp_flag = 0; - - /* chroma_format_idc */ - if ((chroma = gst_nal_bs_read_ue (bs)) == 3) { - /* separate_colour_plane_flag */ - sps->scp_flag = gst_nal_bs_read (bs, 1); - } - /* bit_depth_luma_minus8 */ - gst_nal_bs_read_ue (bs); - /* bit_depth_chroma_minus8 */ - gst_nal_bs_read_ue (bs); - /* qpprime_y_zero_transform_bypass_flag */ - gst_nal_bs_read (bs, 1); - /* seq_scaling_matrix_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - gint i, j, m, d; - - m = (chroma != 3) ? 8 : 12; - for (i = 0; i < m; i++) { - /* seq_scaling_list_present_flag[i] */ - d = gst_nal_bs_read (bs, 1); - if (d) { - gint lastScale = 8, nextScale = 8, deltaScale; - - j = (i < 6) ? 16 : 64; - for (; j > 0; j--) { - if (nextScale != 0) { - deltaScale = gst_nal_bs_read_se (bs); - nextScale = (lastScale + deltaScale + 256) % 256; - } - if (nextScale != 0) - lastScale = nextScale; - } - } - } - } - if (scp_flag) - chroma = 0; - } else { - /* inferred value */ - chroma = 1; - } - - /* between 0 and 12 */ - sps->log2_max_frame_num_minus4 = gst_nal_bs_read_ue (bs); - if (sps->log2_max_frame_num_minus4 > 12) { - GST_WARNING_OBJECT (params->el, - "log2_max_frame_num_minus4 = %d out of range" " [0,12]", - sps->log2_max_frame_num_minus4); - return FALSE; - } - - sps->pic_order_cnt_type = gst_nal_bs_read_ue (bs); - if (sps->pic_order_cnt_type == 0) { - sps->log2_max_pic_order_cnt_lsb_minus4 = gst_nal_bs_read_ue (bs); - } else if (sps->pic_order_cnt_type == 1) { - gint d; - - /* delta_pic_order_always_zero_flag */ - gst_nal_bs_read (bs, 1); - /* offset_for_non_ref_pic */ - gst_nal_bs_read_ue (bs); - /* offset_for_top_to_bottom_field */ - gst_nal_bs_read_ue (bs); - /* num_ref_frames_in_pic_order_cnt_cycle */ - d = gst_nal_bs_read_ue (bs); - for (; d > 0; d--) { - /* offset_for_ref_frame[i] */ - gst_nal_bs_read_ue (bs); - } - } - - /* max_num_ref_frames */ - gst_nal_bs_read_ue (bs); - /* gaps_in_frame_num_value_allowed_flag */ - gst_nal_bs_read (bs, 1); - /* pic_width_in_mbs_minus1 */ - width = gst_nal_bs_read_ue (bs); - /* pic_height_in_map_units_minus1 */ - height = gst_nal_bs_read_ue (bs); - - sps->frame_mbs_only_flag = gst_nal_bs_read (bs, 1); - if (!sps->frame_mbs_only_flag) { - /* mb_adaptive_frame_field_flag */ - gst_nal_bs_read (bs, 1); - } - - width++; - width *= 16; - height++; - height *= 16 * (2 - sps->frame_mbs_only_flag); - - /* direct_8x8_inference_flag */ - gst_nal_bs_read (bs, 1); - /* frame_cropping_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* frame_crop_left_offset */ - fc_left = gst_nal_bs_read_ue (bs); - /* frame_crop_right_offset */ - fc_right = gst_nal_bs_read_ue (bs); - /* frame_crop_top_offset */ - fc_top = gst_nal_bs_read_ue (bs); - /* frame_crop_bottom_offset */ - fc_bottom = gst_nal_bs_read_ue (bs); - } else { - fc_left = fc_right = fc_top = fc_bottom = 0; - } - - GST_LOG_OBJECT (params->el, "decoding SPS: profile_idc = %d, " - "level_idc = %d, sps_id = %d, pic_order_cnt_type = %d, " - "frame_mbs_only_flag = %d", - sps->profile_idc, sps->level_idc, sps_id, sps->pic_order_cnt_type, - sps->frame_mbs_only_flag); - - /* calculate width and height */ - GST_LOG_OBJECT (params->el, "initial width=%d, height=%d", width, height); - GST_LOG_OBJECT (params->el, "crop (%d,%d)(%d,%d)", - fc_left, fc_top, fc_right, fc_bottom); - if (chroma > 3) { - GST_LOG_OBJECT (params->el, "chroma=%d in SPS is out of range", chroma); - return FALSE; - } - width -= (fc_left + fc_right) * subwc[chroma]; - height -= - (fc_top + fc_bottom) * subhc[chroma] * (2 - sps->frame_mbs_only_flag); - if (width < 0 || height < 0) { - GST_WARNING_OBJECT (params->el, "invalid width/height in SPS"); - return FALSE; - } - GST_LOG_OBJECT (params->el, "final width=%u, height=%u", width, height); - sps->width = width; - sps->height = height; - - sps->vui_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->vui_parameters_present_flag) { - /* discard parsing problem */ - gst_h264_params_decode_sps_vui (params, sps, bs); - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_pps (GstH264Params * params, GstNalBs * bs) -{ - gint pps_id; - GstH264ParamsPPS *pps = NULL; - - pps_id = gst_nal_bs_read_ue (bs); - if (G_UNLIKELY (pps_id >= MAX_PPS_COUNT)) { - GST_WARNING_OBJECT (params->el, - "requested pps_id=%04x out of range", pps_id); - return FALSE; - } - - - pps = gst_h264_params_get_pps (params, pps_id, FALSE); - if (G_UNLIKELY (pps == NULL)) - return FALSE; - - /* validate and set */ - pps->valid = TRUE; - params->pps = pps; - - gst_h264_params_store_nal (params, params->pps_nals, MAX_PPS_COUNT, pps_id, - bs); - - pps->sps_id = gst_nal_bs_read_ue (bs); - GST_LOG_OBJECT (params->el, "pps %d referencing sps %d", pps_id, pps->sps_id); - - /* activate referenced sps */ - if (!gst_h264_params_get_sps (params, pps->sps_id, TRUE)) - return FALSE; - - /* not parsing the rest for the time being */ - return TRUE; -} - -static gboolean -gst_h264_params_decode_sei_buffering_period (GstH264Params * params, - GstNalBs * bs) -{ -#ifdef EXTRA_PARSE - guint8 sps_id; - gint sched_sel_idx; - GstH264ParamsSPS *sps; - - sps_id = gst_nal_bs_read_ue (bs); - sps = gst_h264_params_get_sps (params, sps_id, TRUE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - if (sps->nal_hrd_parameters_present_flag) { - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; - sched_sel_idx++) { - params->initial_cpb_removal_delay[sched_sel_idx] - = gst_nal_bs_read (bs, - sps->initial_cpb_removal_delay_length_minus1 + 1); - /* initial_cpb_removal_delay_offset */ - gst_nal_bs_read (bs, sps->initial_cpb_removal_delay_length_minus1 + 1); - } - } - - if (sps->vcl_hrd_parameters_present_flag) { - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; - sched_sel_idx++) { - params->initial_cpb_removal_delay[sched_sel_idx] - = gst_nal_bs_read (bs, - sps->initial_cpb_removal_delay_length_minus1 + 1); - /* initial_cpb_removal_delay_offset */ - gst_nal_bs_read (bs, sps->initial_cpb_removal_delay_length_minus1 + 1); - } - } -#endif - - if (params->ts_trn_nb == GST_CLOCK_TIME_NONE || - params->dts == GST_CLOCK_TIME_NONE) - params->ts_trn_nb = 0; - else - params->ts_trn_nb = params->dts; - - GST_LOG_OBJECT (params->el, - "new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT, - GST_TIME_ARGS (params->ts_trn_nb)); - - return 0; -} - -static gboolean -gst_h264_params_decode_sei_picture_timing (GstH264Params * params, - GstNalBs * bs) -{ - GstH264ParamsSPS *sps = params->sps; - - if (sps == NULL) { - GST_WARNING_OBJECT (params->el, - "SPS == NULL; delayed decoding of picture timing info not implemented "); - return FALSE; - } - - if (sps->nal_hrd_parameters_present_flag - || sps->vcl_hrd_parameters_present_flag) { - params->sei_cpb_removal_delay = - gst_nal_bs_read (bs, sps->cpb_removal_delay_length_minus1 + 1); - /* sei_dpb_output_delay */ - gst_nal_bs_read (bs, sps->dpb_output_delay_length_minus1 + 1); - } - - if (sps->pic_struct_present_flag) { -#ifdef EXTRA_PARSE - /* pic_struct to NumClockTS lookup table */ - static const guint8 sei_num_clock_ts_table[9] = - { 1, 1, 1, 2, 2, 3, 3, 2, 3 }; - guint i, num_clock_ts; - guint sei_ct_type = 0; -#endif - - params->sei_pic_struct = gst_nal_bs_read (bs, 4); - GST_LOG_OBJECT (params, "pic_struct:%d", params->sei_pic_struct); - if (params->sei_pic_struct > SEI_PIC_STRUCT_FRAME_TRIPLING) - return FALSE; - -#ifdef EXTRA_PARSE - num_clock_ts = sei_num_clock_ts_table[params->sei_pic_struct]; - - for (i = 0; i < num_clock_ts; i++) { - /* clock_timestamp_flag */ - if (gst_nal_bs_read (bs, 1)) { - guint full_timestamp_flag; - - sei_ct_type |= 1 << gst_nal_bs_read (bs, 2); - /* nuit_field_based_flag */ - gst_nal_bs_read (bs, 1); - /* counting_type */ - gst_nal_bs_read (bs, 5); - full_timestamp_flag = gst_nal_bs_read (bs, 1); - /* discontinuity_flag */ - gst_nal_bs_read (bs, 1); - /* cnt_dropped_flag */ - gst_nal_bs_read (bs, 1); - /* n_frames */ - gst_nal_bs_read (bs, 8); - if (full_timestamp_flag) { - /* seconds_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* minutes_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* hours_value 0..23 */ - gst_nal_bs_read (bs, 5); - } else { - /* seconds_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* seconds_value range 0..59 */ - gst_nal_bs_read (bs, 6); - /* minutes_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* minutes_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* hours_flag */ - if (gst_nal_bs_read (bs, 1)) - /* hours_value 0..23 */ - gst_nal_bs_read (bs, 5); - } - } - } - if (sps->time_offset_length_minus1 >= 0) { - /* time_offset */ - gst_nal_bs_read (bs, sps->time_offset_length_minus1 + 1); - } - } - } - - GST_LOG_OBJECT (params, "ct_type:%X", sei_ct_type); -#endif - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sei (GstH264Params * params, GstNalBs * bs) -{ - guint8 tmp; - GstH264ParamsSEIPayloadType payloadType = 0; - gint8 payloadSize = 0; - - do { - tmp = gst_nal_bs_read (bs, 8); - payloadType += tmp; - } while (tmp == 255); - do { - tmp = gst_nal_bs_read (bs, 8); - payloadSize += tmp; - } while (tmp == 255); - - GST_LOG_OBJECT (params->el, - "SEI message received: payloadType = %d, payloadSize = %d bytes", - payloadType, payloadSize); - - switch (payloadType) { - case SEI_BUF_PERIOD: - if (!gst_h264_params_decode_sei_buffering_period (params, bs)) - return FALSE; - break; - case SEI_PIC_TIMING: - /* TODO: According to H264 D2.2 Note1, it might be the case that the - * picture timing SEI message is encountered before the corresponding SPS - * is specified. Need to hold down the message and decode it later. */ - if (!gst_h264_params_decode_sei_picture_timing (params, bs)) - return FALSE; - break; - default: - GST_LOG_OBJECT (params->el, - "SEI message of payloadType = %d is received but not parsed", - payloadType); - break; - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_slice_header (GstH264Params * params, GstNalBs * bs) -{ - GstH264ParamsSPS *sps; - GstH264ParamsPPS *pps; - guint8 pps_id; - - params->first_mb_in_slice = gst_nal_bs_read_ue (bs); - params->slice_type = gst_nal_bs_read_ue (bs); - - pps_id = gst_nal_bs_read_ue (bs); - GST_LOG_OBJECT (params->el, "slice header references pps id %d", pps_id); - pps = gst_h264_params_get_pps (params, pps_id, TRUE); - if (G_UNLIKELY (pps == NULL)) - return FALSE; - sps = gst_h264_params_get_sps (params, pps->sps_id, TRUE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - if (sps->scp_flag) { - /* colour_plane_id */ - gst_nal_bs_read (bs, 2); - } - - /* frame num */ - gst_nal_bs_read (bs, sps->log2_max_pic_order_cnt_lsb_minus4 + 4); - - if (!sps->frame_mbs_only_flag) { - params->field_pic_flag = gst_nal_bs_read (bs, 1); - if (params->field_pic_flag) - params->bottom_field_flag = gst_nal_bs_read (bs, 1); - } - - /* not parsing the rest for the time being */ - return TRUE; -} - -/* only payload in @data */ -gboolean -gst_h264_params_parse_nal (GstH264Params * params, guint8 * data, gint size) -{ - GstH264ParamsNalUnitType nal_type; - GstNalBs bs; - gint nal_ref_idc; - gboolean res = TRUE; - - g_return_val_if_fail (params != NULL, FALSE); - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (size != 0, FALSE); - - nal_type = (data[0] & 0x1f); - nal_ref_idc = (data[0] & 0x60) >> 5; - - GST_LOG_OBJECT (params->el, "NAL type: %d, ref_idc: %d", nal_type, - nal_ref_idc); - - gst_nal_bs_init (&bs, data + 1, size - 1); - /* optimality HACK */ - bs.orig_data = data; - - /* first parse some things needed to get to the frame type */ - switch (nal_type) { - case NAL_SLICE: - case NAL_SLICE_DPA: - case NAL_SLICE_DPB: - case NAL_SLICE_DPC: - case NAL_SLICE_IDR: - { - gint first_mb_in_slice, slice_type; - - gst_h264_params_decode_slice_header (params, &bs); - first_mb_in_slice = params->first_mb_in_slice; - slice_type = params->slice_type; - - GST_LOG_OBJECT (params->el, "first MB: %d, slice type: %d", - first_mb_in_slice, slice_type); - - switch (slice_type) { - case 0: - case 5: - case 3: - case 8: /* SP */ - /* P frames */ - GST_LOG_OBJECT (params->el, "we have a P slice"); - break; - case 1: - case 6: - /* B frames */ - GST_LOG_OBJECT (params->el, "we have a B slice"); - break; - case 2: - case 7: - case 4: - case 9: - /* I frames */ - GST_LOG_OBJECT (params->el, "we have an I slice"); - break; - } - break; - } - case NAL_SEI: - GST_LOG_OBJECT (params->el, "SEI NAL"); - res = gst_h264_params_decode_sei (params, &bs); - break; - case NAL_SPS: - GST_LOG_OBJECT (params->el, "SPS NAL"); - res = gst_h264_params_decode_sps (params, &bs); - break; - case NAL_PPS: - GST_LOG_OBJECT (params->el, "PPS NAL"); - res = gst_h264_params_decode_pps (params, &bs); - break; - case NAL_AU_DELIMITER: - GST_LOG_OBJECT (params->el, "AU delimiter NAL"); - break; - default: - GST_LOG_OBJECT (params->el, "unparsed NAL"); - break; - } - - return res; -} - -void -gst_h264_params_get_timestamp (GstH264Params * params, - GstClockTime * out_ts, GstClockTime * out_dur, gboolean frame) -{ - GstH264ParamsSPS *sps = params->sps; - GstClockTime upstream; - gint duration = 1; - - g_return_if_fail (out_dur != NULL); - g_return_if_fail (out_ts != NULL); - - upstream = *out_ts; - - if (!frame) { - GST_LOG_OBJECT (params->el, "no frame data -> 0 duration"); - *out_dur = 0; - goto exit; - } else { - *out_ts = upstream; - } - - if (!sps) { - GST_DEBUG_OBJECT (params->el, "referred SPS invalid"); - goto exit; - } else if (!sps->timing_info_present_flag) { - GST_DEBUG_OBJECT (params->el, - "unable to compute timestamp: timing info not present"); - goto exit; - } else if (sps->time_scale == 0) { - GST_DEBUG_OBJECT (params->el, - "unable to compute timestamp: time_scale = 0 " - "(this is forbidden in spec; bitstream probably contains error)"); - goto exit; - } - - if (sps->pic_struct_present_flag && params->sei_pic_struct != (guint8) - 1) { - /* Note that when h264parse->sei_pic_struct == -1 (unspecified), there - * are ways to infer its value. This is related to computing the - * TopFieldOrderCnt and BottomFieldOrderCnt, which looks - * complicated and thus not implemented for the time being. Yet - * the value we have here is correct for many applications - */ - switch (params->sei_pic_struct) { - case SEI_PIC_STRUCT_TOP_FIELD: - case SEI_PIC_STRUCT_BOTTOM_FIELD: - duration = 1; - break; - case SEI_PIC_STRUCT_FRAME: - case SEI_PIC_STRUCT_TOP_BOTTOM: - case SEI_PIC_STRUCT_BOTTOM_TOP: - duration = 2; - break; - case SEI_PIC_STRUCT_TOP_BOTTOM_TOP: - case SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: - duration = 3; - break; - case SEI_PIC_STRUCT_FRAME_DOUBLING: - duration = 4; - break; - case SEI_PIC_STRUCT_FRAME_TRIPLING: - duration = 6; - break; - default: - GST_DEBUG_OBJECT (params, - "h264parse->sei_pic_struct of unknown value %d. Not parsed", - params->sei_pic_struct); - break; - } - } else { - duration = params->field_pic_flag ? 1 : 2; - } - - GST_LOG_OBJECT (params->el, "frame tick duration %d", duration); - - /* - * h264parse.264 C.1.2 Timing of coded picture removal (equivalent to DTS): - * Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000 - * Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n) - * where - * Tc = num_units_in_tick / time_scale - */ - - if (params->ts_trn_nb != GST_CLOCK_TIME_NONE) { - GST_LOG_OBJECT (params->el, "buffering based ts"); - /* buffering period is present */ - if (upstream != GST_CLOCK_TIME_NONE) { - /* If upstream timestamp is valid, we respect it and adjust current - * reference point */ - params->ts_trn_nb = upstream - - (GstClockTime) gst_util_uint64_scale_int - (params->sei_cpb_removal_delay * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - } else { - /* If no upstream timestamp is given, we write in new timestamp */ - upstream = params->dts = params->ts_trn_nb + - (GstClockTime) gst_util_uint64_scale_int - (params->sei_cpb_removal_delay * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - } - } else { - GstClockTime dur; - - GST_LOG_OBJECT (params->el, "duration based ts"); - /* naive method: no removal delay specified - * track upstream timestamp and provide best guess frame duration */ - dur = gst_util_uint64_scale_int (duration * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - /* sanity check */ - if (dur < GST_MSECOND) { - GST_DEBUG_OBJECT (params->el, "discarding dur %" GST_TIME_FORMAT, - GST_TIME_ARGS (dur)); - } else { - *out_dur = dur; - } - } - -exit: - if (GST_CLOCK_TIME_IS_VALID (upstream)) - *out_ts = params->dts = upstream; - - if (GST_CLOCK_TIME_IS_VALID (*out_dur) && - GST_CLOCK_TIME_IS_VALID (params->dts)) - params->dts += *out_dur; -} - -void -gst_h264_params_create (GstH264Params ** _params, GstElement * element) -{ - GstH264Params *params; - - g_return_if_fail (_params != NULL); - - params = g_new0 (GstH264Params, 1); - params->el = element; - - params->dts = GST_CLOCK_TIME_NONE; - params->ts_trn_nb = GST_CLOCK_TIME_NONE; - - *_params = params; -} - -void -gst_h264_params_free (GstH264Params * params) -{ - gint i; - - g_return_if_fail (params != NULL); - - for (i = 0; i < MAX_SPS_COUNT; i++) - gst_buffer_replace (¶ms->sps_nals[i], NULL); - for (i = 0; i < MAX_PPS_COUNT; i++) - gst_buffer_replace (¶ms->pps_nals[i], NULL); - - g_free (params); -} diff --git a/gst/videoparsers/h264parse.h b/gst/videoparsers/h264parse.h deleted file mode 100644 index 517c8542b2..0000000000 --- a/gst/videoparsers/h264parse.h +++ /dev/null @@ -1,186 +0,0 @@ -/* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia - * Copyright (C) <2010> Nokia Corporation - * - * Some bits C-c,C-v'ed and s/4/3 from h264parse: - * (C) 2005 Michal Benes - * (C) 2008 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_H264_PARAMS_H__ -#define __GST_H264_PARAMS_H__ - -#include - -G_BEGIN_DECLS - -typedef enum -{ - NAL_UNKNOWN = 0, - NAL_SLICE = 1, - NAL_SLICE_DPA = 2, - NAL_SLICE_DPB = 3, - NAL_SLICE_DPC = 4, - NAL_SLICE_IDR = 5, - NAL_SEI = 6, - NAL_SPS = 7, - NAL_PPS = 8, - NAL_AU_DELIMITER = 9, - NAL_SEQ_END = 10, - NAL_STREAM_END = 11, - NAL_FILTER_DATA = 12 -} GstH264ParamsNalUnitType; - -/* SEI type */ -typedef enum -{ - SEI_BUF_PERIOD = 0, - SEI_PIC_TIMING = 1 - /* and more... */ -} GstH264ParamsSEIPayloadType; - -/* SEI pic_struct type */ -typedef enum -{ - SEI_PIC_STRUCT_FRAME = 0, - SEI_PIC_STRUCT_TOP_FIELD = 1, - SEI_PIC_STRUCT_BOTTOM_FIELD = 2, - SEI_PIC_STRUCT_TOP_BOTTOM = 3, - SEI_PIC_STRUCT_BOTTOM_TOP = 4, - SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5, - SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, - SEI_PIC_STRUCT_FRAME_DOUBLING = 7, - SEI_PIC_STRUCT_FRAME_TRIPLING = 8 -} GstH264ParamsSEIPicStructType; - -typedef struct _GstH264Params GstH264Params; -typedef struct _GstH264ParamsSPS GstH264ParamsSPS; -typedef struct _GstH264ParamsPPS GstH264ParamsPPS; - -#define MAX_SPS_COUNT 32 -#define MAX_PPS_COUNT 256 - -/* SPS: sequential parameter sets */ -struct _GstH264ParamsSPS -{ - gboolean valid; - - /* raw values */ - guint8 profile_idc; - guint8 level_idc; - - guint8 sps_id; - - guint8 pic_order_cnt_type; - - guint8 log2_max_frame_num_minus4; - gboolean frame_mbs_only_flag; - guint8 log2_max_pic_order_cnt_lsb_minus4; - - gboolean frame_cropping_flag; - gboolean scp_flag; - - /* VUI parameters */ - gboolean vui_parameters_present_flag; - - gboolean timing_info_present_flag; - guint32 num_units_in_tick; - guint32 time_scale; - gboolean fixed_frame_rate_flag; - - gboolean nal_hrd_parameters_present_flag; - gboolean vcl_hrd_parameters_present_flag; - - /* hrd parameters */ - guint8 cpb_cnt_minus1; - gint initial_cpb_removal_delay_length_minus1; - gint cpb_removal_delay_length_minus1; - gint dpb_output_delay_length_minus1; - gboolean time_offset_length_minus1; - - gboolean pic_struct_present_flag; - - /* ... and probably more ... */ - - /* derived values */ - gint width, height; - gint fps_num, fps_den; -}; - -/* PPS: pic parameter sets */ -struct _GstH264ParamsPPS -{ - gboolean valid; - - /* raw values */ - guint8 pps_id; - guint8 sps_id; -}; - -struct _GstH264Params -{ - /* debug purposes */ - GstElement *el; - - /* SPS: sequential parameter set */ - GstH264ParamsSPS sps_buffers[MAX_SPS_COUNT]; - /* current SPS; most recent one in stream or referenced by PPS */ - GstH264ParamsSPS *sps; - /* PPS: sequential parameter set */ - GstH264ParamsPPS pps_buffers[MAX_PPS_COUNT]; - /* current PPS; most recent one in stream */ - GstH264ParamsPPS *pps; - - /* extracted from slice header or otherwise relevant nal */ - guint8 first_mb_in_slice; - guint8 slice_type; - gboolean field_pic_flag; - gboolean bottom_field_flag; - - /* SEI: supplemental enhancement messages */ -#ifdef EXTRA_PARSE - /* buffering period */ - guint32 initial_cpb_removal_delay[32]; -#endif - /* picture timing */ - guint32 sei_cpb_removal_delay; - guint8 sei_pic_struct; - /* And more... */ - - /* cached timestamps */ - /* (trying to) track upstream dts and interpolate */ - GstClockTime dts; - /* dts at start of last buffering period */ - GstClockTime ts_trn_nb; - - /* collected SPS and PPS NALUs */ - GstBuffer *sps_nals[MAX_SPS_COUNT]; - GstBuffer *pps_nals[MAX_PPS_COUNT]; -}; - -gboolean gst_h264_params_parse_nal (GstH264Params * params, guint8 * nal, gint size); -void gst_h264_params_get_timestamp (GstH264Params * params, - GstClockTime * out_ts, GstClockTime * out_dur, - gboolean frame); -void gst_h264_params_create (GstH264Params ** _params, GstElement * element); -void gst_h264_params_free (GstH264Params * params); - - -G_END_DECLS -#endif From 888e8144a0ec043ed9f0f2c1594d653d0be69371 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Wed, 3 Aug 2011 17:31:55 +0200 Subject: [PATCH 61/85] codecparsers: mpeg: fix Picture_Coding_Extension parser. --- gst-libs/gst/codecparsers/gstmpegvideoparser.c | 6 ++++++ gst-libs/gst/codecparsers/gstmpegvideoparser.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.c b/gst-libs/gst/codecparsers/gstmpegvideoparser.c index be34e5f99b..44df76ad5e 100644 --- a/gst-libs/gst/codecparsers/gstmpegvideoparser.c +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.c @@ -560,6 +560,12 @@ gst_mpeg_video_parse_picture_extension (GstMpegVideoPictureExt * ext, gst_bit_reader_init (&br, &data[offset], size); + if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != + GST_MPEG_VIDEO_PACKET_EXT_PICTURE) { + GST_DEBUG ("Not parsing a picture extension"); + return FALSE; + } + /* f_code */ READ_UINT8 (&br, ext->f_code[0][0], 4); READ_UINT8 (&br, ext->f_code[0][1], 4); diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.h b/gst-libs/gst/codecparsers/gstmpegvideoparser.h index 8d034affcd..fbcf93ebb7 100644 --- a/gst-libs/gst/codecparsers/gstmpegvideoparser.h +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.h @@ -69,7 +69,8 @@ typedef enum { GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE = 0x01, GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY = 0x02, GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX = 0x03, - GST_MPEG_VIDEO_PACKET_EXT_GOP = 0x04 + GST_MPEG_VIDEO_PACKET_EXT_GOP = 0x04, + GST_MPEG_VIDEO_PACKET_EXT_PICTURE = 0x08 } GstMpegVideoPacketExtensionCode; /** From 10ee3593f25eb9c2744987452c6a1cf8fdcd54aa Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Fri, 5 Aug 2011 11:21:05 +0200 Subject: [PATCH 62/85] codecparsers: mpeg: add user-data packet start code definition. --- gst-libs/gst/codecparsers/gstmpegvideoparser.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.h b/gst-libs/gst/codecparsers/gstmpegvideoparser.h index fbcf93ebb7..2fed93be8a 100644 --- a/gst-libs/gst/codecparsers/gstmpegvideoparser.h +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.h @@ -35,6 +35,7 @@ G_BEGIN_DECLS * @GST_MPEG_VIDEO_PACKET_PICTURE: Picture packet starting code * @GST_MPEG_VIDEO_PACKET_SLICE_MIN: Picture packet starting code * @GST_MPEG_VIDEO_PACKET_SLICE_MAX: Slice max packet starting code + * @GST_MPEG_VIDEO_PACKET_USER_DATA: User data packet starting code * @GST_MPEG_VIDEO_PACKET_SEQUENCE : Sequence packet starting code * @GST_MPEG_VIDEO_PACKET_EXTENSION: Extension packet starting code * @GST_MPEG_VIDEO_PACKET_SEQUENCE_END: Sequence end packet code @@ -47,6 +48,7 @@ typedef enum { GST_MPEG_VIDEO_PACKET_PICTURE = 0x00, GST_MPEG_VIDEO_PACKET_SLICE_MIN = 0x01, GST_MPEG_VIDEO_PACKET_SLICE_MAX = 0xaf, + GST_MPEG_VIDEO_PACKET_USER_DATA = 0xb2, GST_MPEG_VIDEO_PACKET_SEQUENCE = 0xb3, GST_MPEG_VIDEO_PACKET_EXTENSION = 0xb5, GST_MPEG_VIDEO_PACKET_SEQUENCE_END = 0xb7, From e09dba934c2738dd0e61a2f005487dbfa0817568 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Fri, 12 Aug 2011 10:17:47 +0200 Subject: [PATCH 63/85] codecparsers: add pkgconfig file. Adds a new pkgconfig file for codecparsers. They don't have any specific dependency on gst-plugins-bad and they could quite be independent bitstream parsers. --- configure.ac | 2 ++ pkgconfig/Makefile.am | 9 ++++++--- pkgconfig/gstreamer-codecparsers-uninstalled.pc.in | 12 ++++++++++++ pkgconfig/gstreamer-codecparsers.pc.in | 12 ++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 pkgconfig/gstreamer-codecparsers-uninstalled.pc.in create mode 100644 pkgconfig/gstreamer-codecparsers.pc.in diff --git a/configure.ac b/configure.ac index 17ea3b96b1..b41d54b888 100644 --- a/configure.ac +++ b/configure.ac @@ -2047,6 +2047,8 @@ docs/version.entities pkgconfig/Makefile pkgconfig/gstreamer-plugins-bad.pc pkgconfig/gstreamer-plugins-bad-uninstalled.pc +pkgconfig/gstreamer-codecparsers.pc +pkgconfig/gstreamer-codecparsers-uninstalled.pc tools/Makefile m4/Makefile win32/common/config.h diff --git a/pkgconfig/Makefile.am b/pkgconfig/Makefile.am index 38cd20bb71..99e1f1c1d8 100644 --- a/pkgconfig/Makefile.am +++ b/pkgconfig/Makefile.am @@ -1,9 +1,11 @@ ### all of the standard pc files we need to generate pcverfiles = \ - gstreamer-plugins-bad-@GST_MAJORMINOR@.pc + gstreamer-plugins-bad-@GST_MAJORMINOR@.pc \ + gstreamer-codecparsers-@GST_MAJORMINOR@.pc pcverfiles_uninstalled = \ - gstreamer-plugins-bad-@GST_MAJORMINOR@-uninstalled.pc + gstreamer-plugins-bad-@GST_MAJORMINOR@-uninstalled.pc \ + gstreamer-codecparsers-@GST_MAJORMINOR@-uninstalled.pc all-local: $(pcverfiles) $(pcverfiles_uninstalled) @@ -22,7 +24,8 @@ pkgconfig_DATA = $(pcverfiles) CLEANFILES = $(pcverfiles) $(pcverfiles_uninstalled) pcinfiles = \ - gstreamer-plugins-bad.pc.in gstreamer-plugins-bad-uninstalled.pc.in + gstreamer-plugins-bad.pc.in gstreamer-plugins-bad-uninstalled.pc.in \ + gstreamer-codecparsers.pc.in gstreamer-codecparsers-uninstalled.pc.in DISTCLEANFILES = $(pcinfiles:.in=) EXTRA_DIST = $(pcinfiles) diff --git a/pkgconfig/gstreamer-codecparsers-uninstalled.pc.in b/pkgconfig/gstreamer-codecparsers-uninstalled.pc.in new file mode 100644 index 0000000000..5a01ba8d09 --- /dev/null +++ b/pkgconfig/gstreamer-codecparsers-uninstalled.pc.in @@ -0,0 +1,12 @@ +prefix= +exec_prefix= +libdir=${pcfiledir}/../gst-libs/gst/codecparsers +includedir=${pcfiledir}/../gst-libs + +Name: GStreamer codec parsers, Uninstalled +Description: Bitstream parsers for GStreamer elements, uninstalled +Requires: gstreamer-@GST_MAJORMINOR@ gstreamer-base-@GST_MAJORMINOR@ +Version: @VERSION@ +Libs: -L${libdir} ${libdir}/libgstcodecparsers-@GST_MAJORMINOR@.la +Cflags: -I${includedir} + diff --git a/pkgconfig/gstreamer-codecparsers.pc.in b/pkgconfig/gstreamer-codecparsers.pc.in new file mode 100644 index 0000000000..e2fc6d1ad8 --- /dev/null +++ b/pkgconfig/gstreamer-codecparsers.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ + +Name: GStreamer codec parsers +Description: Bitstream parsers for GStreamer elements +Requires: gstreamer-@GST_MAJORMINOR@ gstreamer-base-@GST_MAJORMINOR@ +Version: @VERSION@ +Libs: -L${libdir} -lgstcodecparsers-@GST_MAJORMINOR@ +Cflags: -I${includedir} + From 43e47009d5da6eaf09be8ebdd4ab9392c9baa82c Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Fri, 12 Aug 2011 13:19:29 +0200 Subject: [PATCH 64/85] codecparsers: h264: fix filler-data NAL unit type name. --- gst-libs/gst/codecparsers/gsth264parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h index c7fae9584d..5d38db9f2d 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.h +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -57,7 +57,7 @@ G_BEGIN_DECLS * @GST_H264_NAL_AU_DELIMITER: Access unit delimiter nal unit * @GST_H264_NAL_SEQ_END: End of sequence nal unit * @GST_H264_NAL_STREAM_END: End of stream nal unit - * @GST_H264_NAL_FILTER_DATA: Filler data na lunit + * @GST_H264_NAL_FILLER_DATA: Filler data na lunit * * Indicates the type of H264 Nal Units */ From 0a0c73169fedbe4abd0bed1e844a748b4c7c4ede Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Tue, 16 Aug 2011 15:14:03 +0200 Subject: [PATCH 65/85] codecparsers: h264: fix slice_header() parsing. We were not parsing the slice headers until the actual end, we are now parsing until the end. --- gst-libs/gst/codecparsers/gsth264parser.c | 24 +++++++++++++++++++++++ gst-libs/gst/codecparsers/gsth264parser.h | 10 ++++++++++ 2 files changed, 34 insertions(+) diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c index bdd2899bd0..a89dc1cfd7 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.c +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -109,6 +109,30 @@ const guint8 zigzag_4x4[16] = { 7, 11, 14, 15, }; +/* Compute Ceil(Log2(v)) */ +/* Derived from branchless code for integer log2(v) from: + */ +static guint +ceil_log2 (guint32 v) +{ + guint r, shift; + + v--; + r = (v > 0xFFFF) << 4; + v >>= r; + shift = (v > 0xFF) << 3; + v >>= shift; + r |= shift; + shift = (v > 0xF) << 2; + v >>= shift; + r |= shift; + shift = (v > 0x3) << 1; + v >>= shift; + r |= shift; + r |= (v >> 1); + return r + 1; +} + /****** Nal parser ******/ typedef struct diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h index 5d38db9f2d..ed34feb128 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.h +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -546,6 +546,16 @@ struct _GstH264SliceHdr /* if nal_unit.ref_idc != 0 */ GstH264DecRefPicMarking dec_ref_pic_marking; + guint8 cabac_init_idc; + gint8 slice_qp_delta; + gint8 slice_qs_delta; + + guint8 disable_deblocking_filter_idc; + gint8 slice_alpha_c0_offset_div2; + gint8 slice_beta_offset_div2; + + guint16 slice_group_change_cycle; + /* calculated values */ guint32 max_pic_num; gboolean valid; From 4bacba620f3180f730399bdaac0fa3c31404d4c0 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Tue, 16 Aug 2011 15:21:18 +0200 Subject: [PATCH 66/85] codecparsers: h264: record slice_header() size in bits. --- gst-libs/gst/codecparsers/gsth264parser.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h index ed34feb128..b8de5759f4 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.h +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -559,6 +559,9 @@ struct _GstH264SliceHdr /* calculated values */ guint32 max_pic_num; gboolean valid; + + /* Size of the slice_header() in bits */ + guint header_size; }; From 621fd01281efa920abc06a3696aeae35a2a32d08 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Tue, 16 Aug 2011 16:19:02 +0200 Subject: [PATCH 67/85] codecparsers: h264: fix pred_weight_table() parsing. Use 16-bit signed integer values for weight values because valid ranges are -128 to +128 inclusive. --- gst-libs/gst/codecparsers/gsth264parser.c | 1 + gst-libs/gst/codecparsers/gsth264parser.h | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c index a89dc1cfd7..ad7a067fb0 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.c +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -827,6 +827,7 @@ gst_h264_slice_parse_pred_weight_table (GstH264SliceHdr * slice, NalReader * nr, guint8 chroma_array_type) { GstH264PredWeightTable *p; + gint16 default_luma_weight, default_chroma_weight; gint i; GST_DEBUG ("parsing \"Prediction weight table\""); diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h index b8de5759f4..56c9883ed4 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.h +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -474,19 +474,20 @@ struct _GstH264PredWeightTable guint8 luma_log2_weight_denom; guint8 chroma_log2_weight_denom; - guint8 luma_weight_l0[32]; - guint8 luma_offset_l0[32]; + gint16 luma_weight_l0[32]; + gint8 luma_offset_l0[32]; /* if seq->ChromaArrayType != 0 */ - guint8 chroma_weight_l0[32][2]; - guint8 chroma_offset_l0[32][2]; + gint16 chroma_weight_l0[32][2]; + gint8 chroma_offset_l0[32][2]; /* if slice->slice_type % 5 == 1 */ - guint8 luma_weight_l1[32]; - guint8 luma_offset_l1[32]; + gint16 luma_weight_l1[32]; + gint8 luma_offset_l1[32]; + /* and if seq->ChromaArrayType != 0 */ - guint8 chroma_weight_l1[32][2]; - guint8 chroma_offset_l1[32][2]; + gint16 chroma_weight_l1[32][2]; + gint8 chroma_offset_l1[32][2]; }; struct _GstH264RefPicMarking From ccfd7616596d4d4ae21d0e33b39e4f35d1cb9492 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Fri, 26 Aug 2011 16:05:58 +0200 Subject: [PATCH 68/85] codecparsers: h264: fix ref_pic_list_reordering(). --- gst-libs/gst/codecparsers/gsth264parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c index ad7a067fb0..8e4e212537 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.c +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -720,7 +720,7 @@ slice_parse_ref_pic_list_reordering (GstH264SliceHdr * slice, NalReader * nr) { GST_DEBUG ("parsing \"Reference picture list reordering\""); - if (!(slice->type == GST_H264_I_SLICE) && !(slice->type == GST_H264_SI_SLICE)) { + if (!GST_H264_IS_I_SLICE (slice) && !GST_H264_IS_SI_SLICE (slice)) { guint8 ref_pic_list_reordering_flag_l0; guint32 reordering_of_pic_nums_idc; @@ -741,7 +741,7 @@ slice_parse_ref_pic_list_reordering (GstH264SliceHdr * slice, NalReader * nr) } while (reordering_of_pic_nums_idc != 3); } - if (slice->type == GST_H264_B_SLICE) { + if (GST_H264_IS_B_SLICE (slice)) { guint8 ref_pic_list_reordering_flag_l1; guint32 reordering_of_pic_nums_idc; From d37f842aaa2c7535cdcb8edb6d033e987f82ff90 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Fri, 26 Aug 2011 15:54:51 +0200 Subject: [PATCH 69/85] codecparsers: h264: handle end-of-seq and end-of-stream NAL gracefully. --- gst-libs/gst/codecparsers/gsth264parser.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c index 8e4e212537..a4c09c0acf 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.c +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -1124,8 +1124,8 @@ gst_h264_parser_identify_nalu (GstH264NalParser * nalparser, { gint off1, off2; - if (size - 4 <= offset) { - GST_DEBUG ("Can't parse, buffer is to small size %u, offset %u", size, + if (size - offset < 4) { + GST_DEBUG ("Can't parse, buffer has too small size %u, offset %u", size, offset); return GST_H264_PARSER_ERROR; } @@ -1153,6 +1153,13 @@ gst_h264_parser_identify_nalu (GstH264NalParser * nalparser, nalu->data = (guint8 *) data; set_nalu_datas (nalu); + if (nalu->type == GST_H264_NAL_SEQ_END || + nalu->type == GST_H264_NAL_STREAM_END) { + GST_DEBUG ("end-of-seq or end-of-stream nal found"); + nalu->size = 0; + return GST_H264_PARSER_OK; + } + off2 = scan_for_start_codes (data + nalu->offset, size - nalu->offset); if (off2 < 0) { GST_DEBUG ("Nal start %d, No end found", nalu->offset); From d7fafc551a96b912c8eea77cdfb43c641903df26 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Mon, 29 Aug 2011 13:52:16 +0200 Subject: [PATCH 70/85] codecparsers: h264: fix NAL reader initialization. --- gst-libs/gst/codecparsers/gsth264parser.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c index a4c09c0acf..964d937167 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.c +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -1289,7 +1289,7 @@ gst_h264_parse_sps (GstH264NalUnit * nalu, GstH264SPS * sps, GstH264VUIParams *vui = NULL; GST_DEBUG ("parsing SPS"); - nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); /* set default values for fields that might not be present in the bitstream and have valid defaults */ @@ -1462,7 +1462,7 @@ gst_h264_parse_pps (GstH264NalParser * nalparser, GstH264NalUnit * nalu, GST_DEBUG ("parsing PPS"); - nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); READ_UE_ALLOWED (&nr, pps->id, 0, GST_H264_MAX_PPS_COUNT); READ_UE_ALLOWED (&nr, sps_id, 0, GST_H264_MAX_SPS_COUNT); @@ -1627,7 +1627,7 @@ gst_h264_parser_parse_slice_hdr (GstH264NalParser * nalparser, } - nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); READ_UE (&nr, slice->first_mb_in_slice); READ_UE (&nr, slice->type); @@ -1799,7 +1799,7 @@ gst_h264_parser_parse_sei (GstH264NalParser * nalparser, GstH264NalUnit * nalu, GST_DEBUG ("parsing \"Sei message\""); - nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size); + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); sei->payloadType = 0; do { From 3952c0dd3b544360c5bc3cfba047dfccd0201c60 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Mon, 29 Aug 2011 13:52:17 +0200 Subject: [PATCH 71/85] codecparsers: h264: fix PPS parser Fix transform_8x8_mode_flag and second_chroma_qp_index_offset --- gst-libs/gst/codecparsers/gsth264parser.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c index 964d937167..869aa6122b 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.c +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -1558,13 +1558,10 @@ gst_h264_parse_pps (GstH264NalParser * nalparser, GstH264NalUnit * nalu, } } - /* FIXME For some reson second_chroma_qp_index_offset is not always present */ - if (G_UNLIKELY (nr.byte * 8 + (8 - nr.bits_in_cache) > nr.size * 8)) - READ_SE_ALLOWED (&nr, pps->second_chroma_qp_index_offset, -12, 12); - - pps->valid = TRUE; + READ_SE_ALLOWED (&nr, pps->second_chroma_qp_index_offset, -12, 12); done: + pps->valid = TRUE; return GST_H264_PARSER_OK; error: From 5655bdc75d28e3435edb313c684d769699144399 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 31 Aug 2011 10:10:21 +0200 Subject: [PATCH 72/85] docs: Add more sections to the gst-libs API docs --- docs/libs/Makefile.am | 7 +- docs/libs/gst-plugins-bad-libs-docs.sgml | 8 + docs/libs/gst-plugins-bad-libs-sections.txt | 289 ++++++++++++++++++++ 3 files changed, 303 insertions(+), 1 deletion(-) diff --git a/docs/libs/Makefile.am b/docs/libs/Makefile.am index e8e43a8c20..e93bbda17b 100644 --- a/docs/libs/Makefile.am +++ b/docs/libs/Makefile.am @@ -66,7 +66,12 @@ CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.c # this is useful ;) SCANOBJ_DEPS = \ - $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/basecamerabinsrc/libgstbasecamerabinsrc-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/signalprocessor/libgstsignalprocessor-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/video/libgstbasevideo-@GST_MAJORMINOR@.la + # Header files to ignore when scanning. IGNORE_HFILES = diff --git a/docs/libs/gst-plugins-bad-libs-docs.sgml b/docs/libs/gst-plugins-bad-libs-docs.sgml index b31d6414e9..f137ca3c2f 100644 --- a/docs/libs/gst-plugins-bad-libs-docs.sgml +++ b/docs/libs/gst-plugins-bad-libs-docs.sgml @@ -31,6 +31,14 @@ + + + Base video element classes + + + + + diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt index 251d453cce..f5825626b0 100644 --- a/docs/libs/gst-plugins-bad-libs-sections.txt +++ b/docs/libs/gst-plugins-bad-libs-sections.txt @@ -37,6 +37,7 @@ gst_h264_parser_parse_sps gst_h264_parser_parse_pps gst_h264_parser_parse_sei gst_h264_nal_parser_new +gst_h264_nal_parser_free gst_h264_parse_sps gst_h264_parse_pps @@ -70,3 +71,291 @@ gst_mpeg_video_parse_quant_matrix_extension
+ +
+photography +GST_PHOTOGRAPHY_AUTOFOCUS_DONE +GST_PHOTOGRAPHY_SHAKE_RISK +GST_PHOTOGRAPHY_PROP_WB_MODE +GST_PHOTOGRAPHY_PROP_COLOUR_TONE +GST_PHOTOGRAPHY_PROP_SCENE_MODE +GST_PHOTOGRAPHY_PROP_FLASH_MODE +GST_PHOTOGRAPHY_PROP_NOISE_REDUCTION +GST_PHOTOGRAPHY_PROP_FOCUS_STATUS +GST_PHOTOGRAPHY_PROP_CAPABILITIES +GST_PHOTOGRAPHY_PROP_SHAKE_RISK +GST_PHOTOGRAPHY_PROP_EV_COMP +GST_PHOTOGRAPHY_PROP_ISO_SPEED +GST_PHOTOGRAPHY_PROP_APERTURE +GST_PHOTOGRAPHY_PROP_EXPOSURE +GST_PHOTOGRAPHY_PROP_IMAGE_CAPTURE_SUPPORTED_CAPS +GST_PHOTOGRAPHY_PROP_IMAGE_PREVIEW_SUPPORTED_CAPS +GST_PHOTOGRAPHY_PROP_FLICKER_MODE +GST_PHOTOGRAPHY_PROP_FOCUS_MODE +GST_PHOTOGRAPHY_PROP_ZOOM +GstPhotographyNoiseReduction +GstWhiteBalanceMode +GstColourToneMode +GstSceneMode +GstFlashMode +GstFocusStatus +GstPhotoCaps +GstPhotoShakeRisk +GstFlickerReductionMode +GstFocusMode +GstPhotoCapturePrepared +get_ev_compensation +get_iso_speed +get_aperture +get_exposure +get_white_balance_mode +get_colour_tone_mode +get_scene_mode +get_flash_mode +get_zoom +get_flicker_mode +get_focus_mode +set_ev_compensation +set_iso_speed +set_aperture +set_exposure +set_white_balance_mode +set_colour_tone_mode +set_scene_mode +set_flash_mode +set_zoom +set_flicker_mode +set_focus_mode +get_capabilities +prepare_for_capture +set_autofocus +set_config +get_config +get_noise_reduction +set_noise_reduction +gst_photography_get_ev_compensation +gst_photography_get_iso_speed +gst_photography_get_aperture +gst_photography_get_exposure +gst_photography_get_white_balance_mode +gst_photography_get_colour_tone_mode +gst_photography_get_scene_mode +gst_photography_get_flash_mode +gst_photography_get_noise_reduction +gst_photography_get_zoom +gst_photography_get_flicker_mode +gst_photography_get_focus_mode +gst_photography_set_ev_compensation +gst_photography_set_iso_speed +gst_photography_set_aperture +gst_photography_set_exposure +gst_photography_set_white_balance_mode +gst_photography_set_colour_tone_mode +gst_photography_set_scene_mode +gst_photography_set_flash_mode +gst_photography_set_noise_reduction +gst_photography_set_zoom +gst_photography_set_flicker_mode +gst_photography_set_focus_mode +gst_photography_get_capabilities +gst_photography_prepare_for_capture +gst_photography_set_autofocus +gst_photography_set_config +gst_photography_get_config +GstPhotography + +GST_PHOTOGRAPHY +GST_IS_PHOTOGRAPHY +GST_TYPE_PHOTOGRAPHY +gst_photography_get_type +GST_PHOTOGRAPHY_GET_IFACE +
+ +
+gstbasecamerasrc +GstBaseCameraSrc +GST_BASE_CAMERA_SRC_CAST +GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME +GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME +GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME +GST_BASE_CAMERA_SRC_PREVIEW_MESSAGE_NAME +GstBaseCameraSrc +GstBaseCameraSrcClass +MIN_ZOOM +MAX_ZOOM +ZOOM_1X +gst_base_camera_src_get_photography +gst_base_camera_src_get_color_balance +gst_base_camera_src_set_mode +gst_base_camera_src_setup_zoom +gst_base_camera_src_setup_preview +gst_base_camera_src_finish_capture +gst_base_camera_src_post_preview + +GST_BASE_CAMERA_SRC +GST_IS_BASE_CAMERA_SRC +GST_TYPE_BASE_CAMERA_SRC +gst_base_camera_src_get_type +GST_BASE_CAMERA_SRC_CLASS +GST_IS_BASE_CAMERA_SRC_CLASS +GST_BASE_CAMERA_SRC_GET_CLASS +
+ +
+gstbasevideoencoder +GstBaseVideoEncoder +GST_BASE_VIDEO_ENCODER_SINK_NAME +GST_BASE_VIDEO_ENCODER_SRC_NAME +GST_BASE_VIDEO_ENCODER_FLOW_DROPPED +GstBaseVideoEncoder +GstBaseVideoEncoderClass +gst_base_video_encoder_get_state +gst_base_video_encoder_get_oldest_frame +gst_base_video_encoder_finish_frame +gst_base_video_encoder_set_latency +gst_base_video_encoder_set_latency_fields + +GST_BASE_VIDEO_ENCODER +GST_IS_BASE_VIDEO_ENCODER +GST_TYPE_BASE_VIDEO_ENCODER +gst_base_video_encoder_get_type +GST_BASE_VIDEO_ENCODER_CLASS +GST_IS_BASE_VIDEO_ENCODER_CLASS +GST_BASE_VIDEO_ENCODER_GET_CLASS +
+ +
+gstbasevideodecoder +GstBaseVideoDecoder +GST_BASE_VIDEO_DECODER_SINK_NAME +GST_BASE_VIDEO_DECODER_SRC_NAME +GST_BASE_VIDEO_DECODER_FLOW_NEED_DATA +GST_BASE_VIDEO_DECODER_FLOW_DROPPED +GST_BASE_AUDIO_DECODER_ERROR +GstBaseVideoDecoder +GstBaseVideoDecoderClass +gst_base_video_decoder_class_set_capture_pattern +gst_base_video_decoder_get_frame +gst_base_video_decoder_get_oldest_frame +gst_base_video_decoder_add_to_frame +gst_base_video_decoder_lost_sync +gst_base_video_decoder_have_frame +gst_base_video_decoder_set_sync_point +gst_base_video_decoder_set_src_caps +gst_base_video_decoder_alloc_src_buffer +gst_base_video_decoder_alloc_src_frame +gst_base_video_decoder_get_state +gst_base_video_decoder_get_max_decode_time +gst_base_video_decoder_finish_frame + +GST_BASE_VIDEO_DECODER +GST_IS_BASE_VIDEO_DECODER +GST_TYPE_BASE_VIDEO_DECODER +gst_base_video_decoder_get_type +GST_BASE_VIDEO_DECODER_CLASS +GST_IS_BASE_VIDEO_DECODER_CLASS +GST_BASE_VIDEO_DECODER_GET_CLASS +
+ +
+gstbasevideocodec +GstBaseVideoCodec +GST_BASE_VIDEO_CODEC_SINK_NAME +GST_BASE_VIDEO_CODEC_SRC_NAME +GST_BASE_VIDEO_CODEC_SRC_PAD +GST_BASE_VIDEO_CODEC_SINK_PAD +GST_BASE_VIDEO_CODEC_FLOW_NEED_DATA +GST_BASE_VIDEO_CODEC_STREAM_LOCK +GST_BASE_VIDEO_CODEC_STREAM_UNLOCK +GstVideoState +GstVideoFrame +GstBaseVideoCodec +GstBaseVideoCodecClass +gst_base_video_codec_new_frame +gst_base_video_codec_free_frame + +GST_BASE_VIDEO_CODEC +GST_IS_BASE_VIDEO_CODEC +GST_TYPE_BASE_VIDEO_CODEC +gst_base_video_codec_get_type +GST_BASE_VIDEO_CODEC_CLASS +GST_IS_BASE_VIDEO_CODEC_CLASS +GST_BASE_VIDEO_CODEC_GET_CLASS +
+ +
+gstsignalprocessor +GstSignalProcessor +GstSignalProcessorClassFlags +GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE +GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE +GstSignalProcessorState +GST_SIGNAL_PROCESSOR_IS_INITIALIZED +GST_SIGNAL_PROCESSOR_IS_RUNNING +GstSignalProcessorGroup +GstSignalProcessor +GstSignalProcessorClass +gst_signal_processor_class_add_pad_template + +GST_SIGNAL_PROCESSOR +GST_IS_SIGNAL_PROCESSOR +GST_TYPE_SIGNAL_PROCESSOR +gst_signal_processor_get_type +GST_SIGNAL_PROCESSOR_CLASS +GST_IS_SIGNAL_PROCESSOR_CLASS +GST_SIGNAL_PROCESSOR_GET_CLASS +
+ + +
+photography-enumtypes +gst_photography_noise_reduction_get_type +GST_TYPE_PHOTOGRAPHY_NOISE_REDUCTION +gst_white_balance_mode_get_type +GST_TYPE_WHITE_BALANCE_MODE +gst_colour_tone_mode_get_type +GST_TYPE_COLOUR_TONE_MODE +gst_scene_mode_get_type +GST_TYPE_SCENE_MODE +gst_flash_mode_get_type +GST_TYPE_FLASH_MODE +gst_focus_status_get_type +GST_TYPE_FOCUS_STATUS +gst_photo_caps_get_type +GST_TYPE_PHOTO_CAPS +gst_photo_shake_risk_get_type +GST_TYPE_PHOTO_SHAKE_RISK +gst_flicker_reduction_mode_get_type +GST_TYPE_FLICKER_REDUCTION_MODE +gst_focus_mode_get_type +GST_TYPE_FOCUS_MODE +
+ +
+gstcamerabin-enum +DEFAULT_WIDTH +DEFAULT_HEIGHT +DEFAULT_CAPTURE_WIDTH +DEFAULT_CAPTURE_HEIGHT +DEFAULT_FPS_N +DEFAULT_FPS_D +DEFAULT_ZOOM +GstCameraBinMode +GST_TYPE_CAMERABIN_MODE +gst_camerabin_mode_get_type +
+ +
+gstcamerabinpreview +gst_camerabin_create_preview_pipeline +gst_camerabin_destroy_preview_pipeline +gst_camerabin_preview_pipeline_post +gst_camerabin_preview_set_caps +
+ +
+gstbasevideoutils +gst_base_video_rawvideo_convert +gst_base_video_encoded_video_convert +gst_video_state_get_timestamp +
From 53fcc53d22b0835db2fa2fddf5bdaaad3b5c1624 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 31 Aug 2011 10:12:44 +0200 Subject: [PATCH 73/85] gst-libs: Remove play directory. Was unported since 0.8 --- gst-libs/gst/play/.gitignore | 3 - gst-libs/gst/play/play.h | 104 ------------------------ gst-libs/gst/play/play.vcproj | 144 ---------------------------------- 3 files changed, 251 deletions(-) delete mode 100644 gst-libs/gst/play/.gitignore delete mode 100644 gst-libs/gst/play/play.h delete mode 100644 gst-libs/gst/play/play.vcproj diff --git a/gst-libs/gst/play/.gitignore b/gst-libs/gst/play/.gitignore deleted file mode 100644 index b7f5a7eb66..0000000000 --- a/gst-libs/gst/play/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -play-enumtypes.[ch] -play-marshal.[ch] -play-marshal.list diff --git a/gst-libs/gst/play/play.h b/gst-libs/gst/play/play.h deleted file mode 100644 index 6cbedce379..0000000000 --- a/gst-libs/gst/play/play.h +++ /dev/null @@ -1,104 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Julien Moutte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_PLAY_H__ -#define __GST_PLAY_H__ - -#include -#include -#include - -G_BEGIN_DECLS - -/* GError stuff */ - -#define GST_PLAY_ERROR gst_play_error_quark () -/* GObject stuff */ - -#define GST_TYPE_PLAY (gst_play_get_type()) -#define GST_PLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY, GstPlay)) -#define GST_PLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY, GstPlayClass)) -#define GST_IS_PLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY)) -#define GST_IS_PLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY)) -#define GST_PLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PLAY, GstPlayClass)) - -typedef enum -{ - GST_PLAY_SINK_TYPE_AUDIO, - GST_PLAY_SINK_TYPE_VIDEO, - GST_PLAY_SINK_TYPE_ANY, -} GstPlaySinkType; - -typedef struct _GstPlay GstPlay; -typedef struct _GstPlayClass GstPlayClass; -typedef struct _GstPlayPrivate GstPlayPrivate; - -struct _GstPlay -{ - GstPipeline pipeline; - - GstPlayPrivate *priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstPlayClass -{ - GstPipelineClass parent_class; - - void (*time_tick) (GstPlay *play, gint64 time_nanos); - void (*stream_length) (GstPlay *play, gint64 length_nanos); - void (*have_video_size) (GstPlay *play, gint width, gint height); - - gpointer _gst_reserved[GST_PADDING]; -}; - -GType gst_play_get_type (void); -GstPlay * gst_play_new (GError **error); - -gboolean gst_play_set_data_src (GstPlay *play, - GstElement *data_src); -gboolean gst_play_set_video_sink (GstPlay *play, - GstElement *video_sink); -gboolean gst_play_set_audio_sink (GstPlay *play, - GstElement *audio_sink); - -gboolean gst_play_set_visualization (GstPlay *play, - GstElement *element); -gboolean gst_play_connect_visualization (GstPlay *play, - gboolean connect); - -gboolean gst_play_set_location (GstPlay *play, - const char *location); -char * gst_play_get_location (GstPlay *play); - -gboolean gst_play_seek_to_time (GstPlay *play, - gint64 time_nanos); - -GstElement * gst_play_get_sink_element (GstPlay *play, - GstElement *element, - GstPlaySinkType sink_type); -GList * gst_play_get_all_by_interface (GstPlay *play, - GType interface); - -gdouble gst_play_get_framerate (GstPlay *play); - -G_END_DECLS - -#endif /* __GST_PLAY_H__ */ diff --git a/gst-libs/gst/play/play.vcproj b/gst-libs/gst/play/play.vcproj deleted file mode 100644 index d55cb3a579..0000000000 --- a/gst-libs/gst/play/play.vcproj +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 52a6db220616ff33125e8b033ffe7b3115f3e37d Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Fri, 2 Sep 2011 16:08:59 +0200 Subject: [PATCH 74/85] docs: minor fixes --- docs/libs/gst-plugins-bad-libs-sections.txt | 2 +- gst-libs/gst/codecparsers/gsth264parser.h | 13 ++++++++++++- gst-libs/gst/codecparsers/gstmpegvideoparser.h | 8 ++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt index f5825626b0..73cc720197 100644 --- a/docs/libs/gst-plugins-bad-libs-sections.txt +++ b/docs/libs/gst-plugins-bad-libs-sections.txt @@ -73,7 +73,7 @@ gst_mpeg_video_parse_quant_matrix_extension
-photography +gstphotography GST_PHOTOGRAPHY_AUTOFOCUS_DONE GST_PHOTOGRAPHY_SHAKE_RISK GST_PHOTOGRAPHY_PROP_WB_MODE diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h index 56c9883ed4..84577f686b 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.h +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -143,6 +143,12 @@ typedef enum GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING = 8 } GstH264SEIPicStructType; +/** + * GstH264SliceType: + * + * Type of Picture slice + */ + typedef enum { GST_H264_P_SLICE = 0, @@ -624,9 +630,14 @@ struct _GstH264SEIMessage }; }; -/* Opaque structure */ +/** + * GstH264NalParser: + * + * H264 NAL Parser (opaque structure). + */ struct _GstH264NalParser { + /*< private >*/ GstH264SPS sps[GST_H264_MAX_SPS_COUNT]; GstH264PPS pps[GST_H264_MAX_PPS_COUNT]; GstH264SPS *last_sps; diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.h b/gst-libs/gst/codecparsers/gstmpegvideoparser.h index 2fed93be8a..a42792b81b 100644 --- a/gst-libs/gst/codecparsers/gstmpegvideoparser.h +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.h @@ -77,10 +77,10 @@ typedef enum { /** * GstMpegVideoLevel: - * @GST_MPEG_VIDEO_LEVEL_LOW - * @GST_MPEG_VIDEO_LEVEL_MAIN - * @GST_MPEG_VIDEO_LEVEL_HIGH_1440 - * @GST_MPEG_VIDEO_LEVEL_HIGH + * @GST_MPEG_VIDEO_LEVEL_LOW: Level Low + * @GST_MPEG_VIDEO_LEVEL_MAIN: Level Main + * @GST_MPEG_VIDEO_LEVEL_HIGH_1440: Level High 1440 + * @GST_MPEG_VIDEO_LEVEL_HIGH: Level High * * Indicates the level in use **/ From a82f92aed2ff3cfb1be571c6437632760088189a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 2 Sep 2011 19:44:31 +0200 Subject: [PATCH 75/85] hlsdemux: Reset in PAUSED->READY after chaining up to the parent class Otherwise there are possible race conditions with the streaming thread. --- gst/hls/gsthlsdemux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 4f30bc50ad..aaff1065dd 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -287,7 +287,6 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - case GST_STATE_CHANGE_PAUSED_TO_READY: gst_hls_demux_reset (demux, FALSE); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: @@ -311,6 +310,7 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_PAUSED_TO_READY: demux->cancelled = TRUE; gst_hls_demux_stop (demux); + gst_hls_demux_reset (demux, FALSE); break; default: break; From a252794a039d09a9e2701c900b6211fe9c0054f6 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 2 Sep 2011 23:48:03 +0000 Subject: [PATCH 76/85] hlsdemux: Make the m3u8 client thread-safe --- gst/hls/m3u8.c | 52 ++++++++++++++++++++++++++++++++++++++++---------- gst/hls/m3u8.h | 4 ++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/gst/hls/m3u8.c b/gst/hls/m3u8.c index 82e7df8d2a..a17193fb7a 100644 --- a/gst/hls/m3u8.c +++ b/gst/hls/m3u8.c @@ -389,6 +389,7 @@ gst_m3u8_client_new (const gchar * uri) client->current = NULL; client->sequence = -1; client->update_failed_count = 0; + client->lock = g_mutex_new (); gst_m3u8_set_uri (client->main, g_strdup (uri)); return client; @@ -400,6 +401,7 @@ gst_m3u8_client_free (GstM3U8Client * self) g_return_if_fail (self != NULL); gst_m3u8_free (self->main); + g_mutex_free (self->lock); g_free (self); } @@ -408,10 +410,12 @@ gst_m3u8_client_set_current (GstM3U8Client * self, GstM3U8 * m3u8) { g_return_if_fail (self != NULL); + GST_M3U8_CLIENT_LOCK (self); if (m3u8 != self->current) { self->current = m3u8; self->update_failed_count = 0; } + GST_M3U8_CLIENT_UNLOCK (self); } gboolean @@ -419,17 +423,19 @@ gst_m3u8_client_update (GstM3U8Client * self, gchar * data) { GstM3U8 *m3u8; gboolean updated = FALSE; + gboolean ret = FALSE; g_return_val_if_fail (self != NULL, FALSE); + GST_M3U8_CLIENT_LOCK (self); m3u8 = self->current ? self->current : self->main; if (!gst_m3u8_update (m3u8, data, &updated)) - return FALSE; + goto out; if (!updated) { self->update_failed_count++; - return FALSE; + goto out; } /* select the first playlist, for now */ @@ -447,7 +453,10 @@ gst_m3u8_client_update (GstM3U8Client * self, gchar * data) GST_DEBUG ("Setting first sequence at %d", self->sequence); } - return TRUE; + ret = TRUE; +out: + GST_M3U8_CLIENT_UNLOCK (self); + return ret; } static gboolean @@ -472,11 +481,14 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client, g_return_val_if_fail (client->current != NULL, FALSE); g_return_val_if_fail (discontinuity != NULL, FALSE); + GST_M3U8_CLIENT_LOCK (client); GST_DEBUG ("Looking for fragment %d", client->sequence); l = g_list_find_custom (client->current->files, client, (GCompareFunc) _find_next); - if (l == NULL) + if (l == NULL) { + GST_M3U8_CLIENT_UNLOCK (client); return FALSE; + } file = GST_M3U8_MEDIA_FILE (l->data); @@ -494,6 +506,7 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client, } *timestamp *= GST_SECOND; + GST_M3U8_CLIENT_UNLOCK (client); return TRUE; } @@ -510,37 +523,56 @@ gst_m3u8_client_get_duration (GstM3U8Client * client) g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE); + GST_M3U8_CLIENT_LOCK (client); /* We can only get the duration for on-demand streams */ - if (!client->current->endlist) + if (!client->current->endlist) { + GST_M3U8_CLIENT_UNLOCK (client); return GST_CLOCK_TIME_NONE; + } g_list_foreach (client->current->files, (GFunc) _sum_duration, &duration); + GST_M3U8_CLIENT_UNLOCK (client); return duration * GST_SECOND; } const gchar * gst_m3u8_client_get_uri (GstM3U8Client * client) { + const gchar *uri; + g_return_val_if_fail (client != NULL, NULL); - return client->main->uri; + GST_M3U8_CLIENT_LOCK (client); + uri = client->main->uri; + GST_M3U8_CLIENT_UNLOCK (client); + return uri; } gboolean gst_m3u8_client_has_variant_playlist (GstM3U8Client * client) { + gboolean ret; + g_return_val_if_fail (client != NULL, FALSE); - return client->main->lists != NULL; + GST_M3U8_CLIENT_LOCK (client); + ret = (client->main->lists != NULL); + GST_M3U8_CLIENT_UNLOCK (client); + return ret; } gboolean gst_m3u8_client_is_live (GstM3U8Client * client) { + gboolean ret; + g_return_val_if_fail (client != NULL, FALSE); + GST_M3U8_CLIENT_LOCK (client); if (!client->current || client->current->endlist) - return FALSE; - - return TRUE; + ret = FALSE; + else + ret = TRUE; + GST_M3U8_CLIENT_UNLOCK (client); + return ret; } diff --git a/gst/hls/m3u8.h b/gst/hls/m3u8.h index aefe8667d1..5223b5cd93 100644 --- a/gst/hls/m3u8.h +++ b/gst/hls/m3u8.h @@ -32,6 +32,9 @@ typedef struct _GstM3U8Client GstM3U8Client; #define GST_M3U8(m) ((GstM3U8*)m) #define GST_M3U8_MEDIA_FILE(f) ((GstM3U8MediaFile*)f) +#define GST_M3U8_CLIENT_LOCK(c) g_mutex_lock (c->lock); +#define GST_M3U8_CLIENT_UNLOCK(c) g_mutex_unlock (c->lock); + struct _GstM3U8 { gchar *uri; @@ -70,6 +73,7 @@ struct _GstM3U8Client GstM3U8 *current; guint update_failed_count; gint sequence; /* the next sequence for this client */ + GMutex *lock; }; From 613360878936e59232fe1b258f9b7109361610b7 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 2 Sep 2011 23:48:22 +0000 Subject: [PATCH 77/85] hlsdemux: Add a get_target_duration and get_current_uri api to m3u8_client --- gst/hls/m3u8.c | 26 ++++++++++++++++++++++++++ gst/hls/m3u8.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/gst/hls/m3u8.c b/gst/hls/m3u8.c index a17193fb7a..c407d72d83 100644 --- a/gst/hls/m3u8.c +++ b/gst/hls/m3u8.c @@ -535,6 +535,19 @@ gst_m3u8_client_get_duration (GstM3U8Client * client) return duration * GST_SECOND; } +GstClockTime +gst_m3u8_client_get_target_duration (GstM3U8Client * client) +{ + GstClockTime duration = 0; + + g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE); + + GST_M3U8_CLIENT_LOCK (client); + duration = client->current->targetduration; + GST_M3U8_CLIENT_UNLOCK (client); + return duration * GST_SECOND; +} + const gchar * gst_m3u8_client_get_uri (GstM3U8Client * client) { @@ -548,6 +561,19 @@ gst_m3u8_client_get_uri (GstM3U8Client * client) return uri; } +const gchar * +gst_m3u8_client_get_current_uri (GstM3U8Client * client) +{ + const gchar *uri; + + g_return_val_if_fail (client != NULL, NULL); + + GST_M3U8_CLIENT_LOCK (client); + uri = client->current->uri; + GST_M3U8_CLIENT_UNLOCK (client); + return uri; +} + gboolean gst_m3u8_client_has_variant_playlist (GstM3U8Client * client) { diff --git a/gst/hls/m3u8.h b/gst/hls/m3u8.h index 5223b5cd93..8c83990f87 100644 --- a/gst/hls/m3u8.h +++ b/gst/hls/m3u8.h @@ -85,7 +85,9 @@ gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client, gboolean * discontinuity, const gchar ** uri, GstClockTime * duration, GstClockTime * timestamp); GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client); +GstClockTime gst_m3u8_client_get_target_duration (GstM3U8Client * client); const gchar *gst_m3u8_client_get_uri(GstM3U8Client * client); +const gchar *gst_m3u8_client_get_current_uri(GstM3U8Client * client); gboolean gst_m3u8_client_has_variant_playlist(GstM3U8Client * client); gboolean gst_m3u8_client_is_live(GstM3U8Client * client); From 5aa972eab2216e2a0ebf34ddf22693d69146fa12 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Fri, 2 Sep 2011 23:49:38 +0000 Subject: [PATCH 78/85] hlsdemux: make hlsdemux lock/unlock the m3u8 client when it accesses it --- gst/hls/gsthlsdemux.c | 55 ++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index aaff1065dd..e7d54ca711 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -356,6 +356,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + GST_M3U8_CLIENT_LOCK (demux->client); file = GST_M3U8_MEDIA_FILE (demux->client->current->files->data); current_sequence = file->sequence; current_pos = 0; @@ -371,6 +372,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) } current_pos += file->duration; } + GST_M3U8_CLIENT_UNLOCK (demux->client); if (walk == NULL) { GST_WARNING_OBJECT (demux, "Could not find seeked fragment"); @@ -401,10 +403,13 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) g_queue_clear (demux->queue); gst_adapter_clear (demux->download); + GST_M3U8_CLIENT_LOCK (demux->client); GST_DEBUG_OBJECT (demux, "seeking to sequence %d", current_sequence); demux->client->sequence = current_sequence; demux->position = start; demux->need_segment = TRUE; + GST_M3U8_CLIENT_UNLOCK (demux->client); + if (flags & GST_SEEK_FLAG_FLUSH) { GST_DEBUG_OBJECT (demux, "sending flush stop"); @@ -522,7 +527,7 @@ gst_hls_demux_src_query (GstPad * pad, GstQuery * query) if (hlsdemux->client) { /* FIXME: Do we answer with the variant playlist, with the current * playlist or the the uri of the least downlowaded fragment? */ - gst_query_set_uri (query, hlsdemux->client->current->uri); + gst_query_set_uri (query, gst_m3u8_client_get_uri (hlsdemux->client)); ret = TRUE; } break; @@ -1007,7 +1012,11 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) /* If this playlist is a variant playlist, select the first one * and update it */ if (gst_m3u8_client_has_variant_playlist (demux->client)) { - GstM3U8 *child = demux->client->main->current_variant->data; + GstM3U8 *child = NULL; + + GST_M3U8_CLIENT_LOCK (demux->client); + child = demux->client->main->current_variant->data; + GST_M3U8_CLIENT_UNLOCK (demux->client); gst_m3u8_client_set_current (demux->client, child); if (!gst_hls_demux_update_playlist (demux)) { GST_ERROR_OBJECT (demux, "Could not fetch the child playlist %s", @@ -1019,11 +1028,13 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) /* If it's a live source, set the sequence number to the end of the list * and substract the 'fragmets_cache' to start from the last fragment*/ if (gst_m3u8_client_is_live (demux->client)) { + GST_M3U8_CLIENT_LOCK (demux->client); demux->client->sequence += g_list_length (demux->client->current->files); if (demux->client->sequence >= demux->fragments_cache) demux->client->sequence -= demux->fragments_cache; else demux->client->sequence = 0; + GST_M3U8_CLIENT_UNLOCK (demux->client); } else { GstClockTime duration = gst_m3u8_client_get_duration (demux->client); @@ -1042,7 +1053,8 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) 100 * i / demux->fragments_cache)); g_get_current_time (&demux->next_update); g_time_val_add (&demux->next_update, - demux->client->current->targetduration * 1000000); + gst_m3u8_client_get_target_duration (demux->client) + / GST_SECOND * G_USEC_PER_SEC); if (!gst_hls_demux_get_next_fragment (demux)) { if (!demux->cancelled) GST_ERROR_OBJECT (demux, "Error caching the first fragments"); @@ -1142,10 +1154,10 @@ gst_hls_demux_update_playlist (GstHLSDemux * demux) const guint8 *data; gchar *playlist; guint avail; + const gchar *uri = gst_m3u8_client_get_current_uri (demux->client); - GST_INFO_OBJECT (demux, "Updating the playlist %s", - demux->client->current->uri); - if (!gst_hls_demux_fetch_location (demux, demux->client->current->uri)) + GST_INFO_OBJECT (demux, "Updating the playlist %s", uri); + if (!gst_hls_demux_fetch_location (demux, uri)) return FALSE; avail = gst_adapter_available (demux->download); @@ -1165,26 +1177,36 @@ gst_hls_demux_change_playlist (GstHLSDemux * demux, gboolean is_fast) { GList *list; GstStructure *s; + gint new_bandwidth; + GST_M3U8_CLIENT_LOCK (demux->client); if (is_fast) list = g_list_next (demux->client->main->current_variant); else list = g_list_previous (demux->client->main->current_variant); /* Don't do anything else if the playlist is the same */ - if (!list || list->data == demux->client->current) + if (!list || list->data == demux->client->current) { + GST_M3U8_CLIENT_UNLOCK (demux->client); return TRUE; + } demux->client->main->current_variant = list; + GST_M3U8_CLIENT_UNLOCK (demux->client); gst_m3u8_client_set_current (demux->client, list->data); + + GST_M3U8_CLIENT_LOCK (demux->client); + new_bandwidth = demux->client->current->bandwidth; + GST_M3U8_CLIENT_UNLOCK (demux->client); + gst_hls_demux_update_playlist (demux); GST_INFO_OBJECT (demux, "Client is %s, switching to bitrate %d", - is_fast ? "fast" : "slow", demux->client->current->bandwidth); + is_fast ? "fast" : "slow", new_bandwidth); s = gst_structure_new ("playlist", - "uri", G_TYPE_STRING, demux->client->current->uri, - "bitrate", G_TYPE_INT, demux->client->current->bandwidth, NULL); + "uri", G_TYPE_STRING, gst_m3u8_client_get_current_uri (demux->client), + "bitrate", G_TYPE_INT, new_bandwidth, NULL); gst_element_post_message (GST_ELEMENT_CAST (demux), gst_message_new_element (GST_OBJECT_CAST (demux), s)); @@ -1215,7 +1237,8 @@ gst_hls_demux_schedule (GstHLSDemux * demux) /* schedule the next update using the target duration field of the * playlist */ g_time_val_add (&demux->next_update, - demux->client->current->targetduration * update_factor * 1000000); + gst_m3u8_client_get_target_duration (demux->client) + / GST_SECOND * G_USEC_PER_SEC * update_factor); GST_DEBUG_OBJECT (demux, "Next update scheduled at %s", g_time_val_to_iso8601 (&demux->next_update)); @@ -1229,14 +1252,18 @@ gst_hls_demux_switch_playlist (GstHLSDemux * demux) gint64 diff, limit; g_get_current_time (&now); - if (!demux->client->main->lists) + GST_M3U8_CLIENT_LOCK (demux->client); + if (!demux->client->main->lists) { + GST_M3U8_CLIENT_UNLOCK (demux->client); return TRUE; + } + GST_M3U8_CLIENT_UNLOCK (demux->client); /* compare the time when the fragment was downloaded with the time when it was * scheduled */ diff = (GST_TIMEVAL_TO_TIME (demux->next_update) - GST_TIMEVAL_TO_TIME (now)); - limit = demux->client->current->targetduration * GST_SECOND * - demux->bitrate_switch_tol; + limit = gst_m3u8_client_get_target_duration (demux->client) + * demux->bitrate_switch_tol; GST_DEBUG ("diff:%s%" GST_TIME_FORMAT ", limit:%" GST_TIME_FORMAT, diff < 0 ? "-" : " ", GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (limit)); From 473e71e1ca325c71fabee5c7b1c501865b2edb7a Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Sat, 3 Sep 2011 00:36:34 +0000 Subject: [PATCH 79/85] hlsdemux: If a fetch fails, decrement the sequence number --- gst/hls/gsthlsdemux.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index e7d54ca711..143f36387d 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -1310,8 +1310,15 @@ gst_hls_demux_get_next_fragment (GstHLSDemux * demux) GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri); - if (!gst_hls_demux_fetch_location (demux, next_fragment_uri)) + if (!gst_hls_demux_fetch_location (demux, next_fragment_uri)) { + /* FIXME: The gst_m3u8_get_next_fragment increments the sequence number + but another thread might call get_next_fragment and this decrement + will not redownload the failed fragment, but might duplicate the + download of a succeeded fragment + */ + g_atomic_int_add (&demux->client->sequence, -1); return FALSE; + } avail = gst_adapter_available (demux->download); buf = gst_adapter_take_buffer (demux->download, avail); From 8a044de701ea28ef77803c72c9899588a755ee44 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 26 Aug 2011 10:50:38 +0100 Subject: [PATCH 80/85] basevideoenc: do not try to calculate latency from an unknown framerate It'll divide by zero, and latency is unknown for an unknown framerate. Fixes an assert in the schroenc test. https://bugzilla.gnome.org/show_bug.cgi?id=657419 --- gst-libs/gst/video/gstbasevideoencoder.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gst-libs/gst/video/gstbasevideoencoder.c b/gst-libs/gst/video/gstbasevideoencoder.c index 5baa4b0d22..b04cf7710b 100644 --- a/gst-libs/gst/video/gstbasevideoencoder.c +++ b/gst-libs/gst/video/gstbasevideoencoder.c @@ -1025,6 +1025,10 @@ gst_base_video_encoder_set_latency_fields (GstBaseVideoEncoder * { gint64 latency; + /* 0 numerator is used for "don't know" */ + if (GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_n == 0) + return; + latency = gst_util_uint64_scale (n_fields, GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_d * GST_SECOND, 2 * GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_n); From 0bdde84f3b8f76f0ac3e89af820a748088810a3a Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 1 Sep 2011 20:01:09 -0300 Subject: [PATCH 81/85] camerabin2: preview: Disable appsrc signals emission Camerabin2 doesn't check appsrc's signals, so disable them --- gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c index 6dd07b0609..597aeb96fa 100644 --- a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c +++ b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c @@ -148,6 +148,8 @@ gst_camerabin_create_preview_pipeline (GstElement * element, goto error; } + g_object_set (data->appsrc, "emit-signals", FALSE, NULL); + gst_bin_add_many (GST_BIN (data->pipeline), data->appsrc, data->capsfilter, data->appsink, csp, csp2, vscale, NULL); if (filter) From d41af4e2f83be8b7cc1460b273a8182cd45325ff Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 5 Sep 2011 09:55:53 -0300 Subject: [PATCH 82/85] camerabin2: preview: Remove one of the two colorspace converters The preview pipeline doesn't need 2 colorspace converters, remove one to speed up caps negotiation and reduce the delay on getting the first preview buffer out of the preview pipeline. It shouldn't cause problems as videoscale and ffmpegcolorspace seems to handle the same caps, so no conversion should be needed for videoscale. Additionally, camerabin1 has been working with a similar pipeline with a single ffmpegcolorspace and no bugs have been open about it so far. --- .../basecamerabinsrc/gstcamerabinpreview.c | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c index 597aeb96fa..9dfc1876a8 100644 --- a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c +++ b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c @@ -125,7 +125,6 @@ gst_camerabin_create_preview_pipeline (GstElement * element, { GstCameraBinPreviewPipelineData *data; GstElement *csp; - GstElement *csp2; GstElement *vscale; gboolean added = FALSE; gboolean linkfail = FALSE; @@ -139,19 +138,17 @@ gst_camerabin_create_preview_pipeline (GstElement * element, data->capsfilter = gst_element_factory_make ("capsfilter", "preview-capsfilter"); data->appsink = gst_element_factory_make ("appsink", "preview-appsink"); - csp = gst_element_factory_make ("ffmpegcolorspace", "preview-csp0"); - csp2 = gst_element_factory_make ("ffmpegcolorspace", "preview-csp1"); + csp = gst_element_factory_make ("ffmpegcolorspace", "preview-csp"); vscale = gst_element_factory_make ("videoscale", "preview-vscale"); - if (!data->appsrc || !data->capsfilter || !data->appsink || !csp || - !csp2 || !vscale) { + if (!data->appsrc || !data->capsfilter || !data->appsink || !csp || !vscale) { goto error; } g_object_set (data->appsrc, "emit-signals", FALSE, NULL); gst_bin_add_many (GST_BIN (data->pipeline), data->appsrc, data->capsfilter, - data->appsink, csp, csp2, vscale, NULL); + data->appsink, csp, vscale, NULL); if (filter) gst_bin_add (GST_BIN (data->pipeline), gst_object_ref (filter)); added = TRUE; @@ -162,21 +159,18 @@ gst_camerabin_create_preview_pipeline (GstElement * element, filter, NULL, GST_PAD_LINK_CHECK_NOTHING)); linkfail |= GST_PAD_LINK_FAILED (gst_element_link_pads_full (filter, NULL, - csp, "sink", GST_PAD_LINK_CHECK_CAPS)); + vscale, "sink", GST_PAD_LINK_CHECK_CAPS)); } else { linkfail |= GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->appsrc, "src", - csp, "sink", GST_PAD_LINK_CHECK_NOTHING)); + vscale, "sink", GST_PAD_LINK_CHECK_NOTHING)); } linkfail |= - GST_PAD_LINK_FAILED (gst_element_link_pads_full (csp, "src", vscale, + GST_PAD_LINK_FAILED (gst_element_link_pads_full (vscale, "src", csp, "sink", GST_PAD_LINK_CHECK_NOTHING)); linkfail |= - GST_PAD_LINK_FAILED (gst_element_link_pads_full (vscale, "src", csp2, - "sink", GST_PAD_LINK_CHECK_NOTHING)); - linkfail |= - GST_PAD_LINK_FAILED (gst_element_link_pads_full (csp2, "src", + GST_PAD_LINK_FAILED (gst_element_link_pads_full (csp, "src", data->capsfilter, "sink", GST_PAD_LINK_CHECK_NOTHING)); linkfail |= GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->capsfilter, "src", @@ -212,8 +206,6 @@ error: if (!added) { if (csp) gst_object_unref (csp); - if (csp2) - gst_object_unref (csp2); if (vscale) gst_object_unref (vscale); if (data->appsrc) From 8a56a7de6d5f2cf70dbe6efbd7c8ed74600c8565 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 5 Sep 2011 14:39:20 -0300 Subject: [PATCH 83/85] camerabin2: preview: Appsink doesn't need to sync Set sync to false on preview's pipeline appsink --- gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c index 9dfc1876a8..b916d7f6b3 100644 --- a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c +++ b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c @@ -146,6 +146,7 @@ gst_camerabin_create_preview_pipeline (GstElement * element, } g_object_set (data->appsrc, "emit-signals", FALSE, NULL); + g_object_set (data->appsink, "sync", FALSE, NULL); gst_bin_add_many (GST_BIN (data->pipeline), data->appsrc, data->capsfilter, data->appsink, csp, vscale, NULL); From cfb986b6bf2658fd7716e3333ecf0a8c78cf63d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 6 Sep 2011 13:20:43 +0200 Subject: [PATCH 84/85] h264parse: Add Converter to classification This parser can convert between different alignments and stream-formats. --- gst/videoparsers/gsth264parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c index 875f04f96b..3821f0031f 100644 --- a/gst/videoparsers/gsth264parse.c +++ b/gst/videoparsers/gsth264parse.c @@ -102,7 +102,7 @@ gst_h264_parse_base_init (gpointer g_class) gst_static_pad_template_get (&sinktemplate)); gst_element_class_set_details_simple (gstelement_class, "H.264 parser", - "Codec/Parser/Video", + "Codec/Parser/Converter/Video", "Parses H.264 streams", "Mark Nauwelaerts "); From b9befbe11b1ac1cebf640565467f58e8dba02b40 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Tue, 6 Sep 2011 15:40:46 +0200 Subject: [PATCH 85/85] Automatic update of common submodule From 605cd9a to a39eb83 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 605cd9a65e..a39eb835fb 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 605cd9a65ed61505f24b840d3fe8e252be72b151 +Subproject commit a39eb835fb3be2a4c5a6a89b5ca5cc064e79b2e2