diff options
Diffstat (limited to 'gst')
38 files changed, 848 insertions, 385 deletions
diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index 22d3dbb8..c84ffce8 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -196,6 +196,7 @@ gst_avi_demux_init (GstAviDemux * avi) gst_element_add_pad (GST_ELEMENT_CAST (avi), avi->sinkpad); avi->adapter = gst_adapter_new (); + avi->flowcombiner = gst_flow_combiner_new (); gst_avi_demux_reset (avi); @@ -210,6 +211,7 @@ gst_avi_demux_finalize (GObject * object) GST_DEBUG ("AVI: finalize"); g_object_unref (avi->adapter); + gst_flow_combiner_free (avi->flowcombiner); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -232,6 +234,7 @@ gst_avi_demux_reset_stream (GstAviDemux * avi, GstAviStream * stream) if (stream->exposed) { gst_pad_set_active (stream->pad, FALSE); gst_element_remove_pad (GST_ELEMENT_CAST (avi), stream->pad); + gst_flow_combiner_remove_pad (avi->flowcombiner, stream->pad); } else gst_object_unref (stream->pad); } @@ -898,7 +901,6 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstObject * parent, gst_adapter_clear (avi->adapter); avi->have_eos = FALSE; for (i = 0; i < avi->num_streams; i++) { - avi->stream[i].last_flow = GST_FLOW_OK; avi->stream[i].discont = TRUE; } /* fall through to default case so that the event gets passed downstream */ @@ -1886,6 +1888,7 @@ gst_avi_demux_expose_streams (GstAviDemux * avi, gboolean force) if (force || stream->idx_n != 0) { GST_LOG_OBJECT (avi, "Adding pad %s", GST_PAD_NAME (stream->pad)); gst_element_add_pad ((GstElement *) avi, stream->pad); + gst_flow_combiner_add_pad (avi->flowcombiner, stream->pad); #if 0 if (avi->element_index) @@ -2404,7 +2407,6 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) stream->current_entry = -1; stream->current_total = 0; - stream->last_flow = GST_FLOW_OK; stream->discont = TRUE; stream->total_bytes = 0; @@ -4638,7 +4640,6 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event) /* reset the last flow and mark discont, seek is always DISCONT */ for (i = 0; i < avi->num_streams; i++) { GST_DEBUG_OBJECT (avi, "marking DISCONT"); - avi->stream[i].last_flow = GST_FLOW_OK; avi->stream[i].discont = TRUE; } GST_PAD_STREAM_UNLOCK (avi->sinkpad); @@ -5003,37 +5004,11 @@ static GstFlowReturn gst_avi_demux_combine_flows (GstAviDemux * avi, GstAviStream * stream, GstFlowReturn ret) { - guint i; - gboolean unexpected = FALSE, not_linked = TRUE; - - /* store the value */ - stream->last_flow = ret; - - /* any other error that is not-linked or eos can be returned right away */ - if (G_LIKELY (ret != GST_FLOW_EOS && ret != GST_FLOW_NOT_LINKED)) - goto done; - - /* only return NOT_LINKED if all other pads returned NOT_LINKED */ - for (i = 0; i < avi->num_streams; i++) { - GstAviStream *ostream = &avi->stream[i]; - - ret = ostream->last_flow; - /* no unexpected or unlinked, return */ - if (G_LIKELY (ret != GST_FLOW_EOS && ret != GST_FLOW_NOT_LINKED)) - goto done; + GST_LOG_OBJECT (avi, "Stream %s:%s flow return: %s", + GST_DEBUG_PAD_NAME (stream->pad), gst_flow_get_name (ret)); + ret = gst_flow_combiner_update_flow (avi->flowcombiner, ret); + GST_LOG_OBJECT (avi, "combined to return %s", gst_flow_get_name (ret)); - /* we check to see if we have at least 1 unexpected or all unlinked */ - unexpected |= (ret == GST_FLOW_EOS); - not_linked &= (ret == GST_FLOW_NOT_LINKED); - } - /* when we get here, we all have unlinked or unexpected */ - if (not_linked) - ret = GST_FLOW_NOT_LINKED; - else if (unexpected) - ret = GST_FLOW_EOS; -done: - GST_LOG_OBJECT (avi, "combined %s to return %s", - gst_flow_get_name (stream->last_flow), gst_flow_get_name (ret)); return ret; } @@ -5092,7 +5067,6 @@ gst_avi_demux_advance (GstAviDemux * avi, GstAviStream * stream, &stream->current_timestamp, &stream->current_ts_end, &stream->current_offset, &stream->current_offset_end); /* and MARK discont for this stream */ - stream->last_flow = GST_FLOW_OK; stream->discont = TRUE; GST_DEBUG_OBJECT (avi, "Moved from %u to %u, ts %" GST_TIME_FORMAT ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT @@ -5134,7 +5108,7 @@ gst_avi_demux_find_next (GstAviDemux * avi, gfloat rate) stream = &avi->stream[i]; /* ignore streams that finished */ - if (stream->last_flow == GST_FLOW_EOS) + if (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS) continue; position = stream->current_timestamp; diff --git a/gst/avi/gstavidemux.h b/gst/avi/gstavidemux.h index 0e8786fb..fd2af507 100644 --- a/gst/avi/gstavidemux.h +++ b/gst/avi/gstavidemux.h @@ -27,6 +27,7 @@ #include "gst/riff/riff-ids.h" #include "gst/riff/riff-read.h" #include <gst/base/gstadapter.h> +#include <gst/base/gstflowcombiner.h> G_BEGIN_DECLS @@ -90,7 +91,6 @@ typedef struct { guint64 current_offset; guint64 current_offset_end; - GstFlowReturn last_flow; gboolean discont; /* stream length */ @@ -165,6 +165,8 @@ typedef struct _GstAviDemux { guint main_stream; /* used for seeking */ + GstFlowCombiner *flowcombiner; + gboolean have_group_id; guint group_id; diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c index 212d18a3..0af779ca 100644 --- a/gst/flv/gstflvdemux.c +++ b/gst/flv/gstflvdemux.c @@ -885,10 +885,11 @@ gst_flv_demux_push_tags (GstFlvDemux * demux) } } -static void +static gboolean gst_flv_demux_update_resync (GstFlvDemux * demux, guint32 pts, gboolean discont, guint32 * last, GstClockTime * offset) { + gboolean ret = FALSE; gint32 dpts = pts - *last; if (!discont && ABS (dpts) >= RESYNC_THRESHOLD) { /* Theoretically, we should use substract the duration of the last buffer, @@ -898,8 +899,12 @@ gst_flv_demux_update_resync (GstFlvDemux * demux, guint32 pts, gboolean discont, GST_WARNING_OBJECT (demux, "Large pts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %" GST_TIME_FORMAT "", dpts, GST_TIME_ARGS (*offset)); + + ret = TRUE; } *last = pts; + + return ret; } static GstFlowReturn @@ -1031,6 +1036,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) /* We need to set caps before adding */ gst_element_add_pad (GST_ELEMENT (demux), gst_object_ref (demux->audio_pad)); + gst_flow_combiner_add_pad (demux->flowcombiner, demux->audio_pad); /* We only emit no more pads when we have audio and video. Indeed we can * not trust the FLV header to tell us if there will be only audio or @@ -1098,8 +1104,10 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) } /* detect (and deem to be resyncs) large pts gaps */ - gst_flv_demux_update_resync (demux, pts, demux->audio_need_discont, - &demux->last_audio_pts, &demux->audio_time_offset); + if (gst_flv_demux_update_resync (demux, pts, demux->audio_need_discont, + &demux->last_audio_pts, &demux->audio_time_offset)) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } /* Fill buffer with data */ GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->audio_time_offset; @@ -1170,27 +1178,20 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) /* Push downstream */ ret = gst_pad_push (demux->audio_pad, outbuf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - if (demux->segment.rate < 0.0 && ret == GST_FLOW_EOS && - demux->segment.position > demux->segment.stop) { - /* In reverse playback we can get a GST_FLOW_EOS when - * we are at the end of the segment, so we just need to jump - * back to the previous section. */ - GST_DEBUG_OBJECT (demux, "downstream has reached end of segment"); - demux->audio_done = TRUE; - ret = GST_FLOW_OK; - } else { - if (ret == GST_FLOW_NOT_LINKED) { - demux->audio_linked = FALSE; - } else - GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT - " bytes audio buffer: %s", demux->tag_data_size, - gst_flow_get_name (ret)); - goto beach; - } + + if (G_UNLIKELY (ret != GST_FLOW_OK) && + demux->segment.rate < 0.0 && ret == GST_FLOW_EOS && + demux->segment.position > demux->segment.stop) { + /* In reverse playback we can get a GST_FLOW_EOS when + * we are at the end of the segment, so we just need to jump + * back to the previous section. */ + GST_DEBUG_OBJECT (demux, "downstream has reached end of segment"); + demux->audio_done = TRUE; + ret = GST_FLOW_OK; + goto beach; } - demux->audio_linked = TRUE; + ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); beach: gst_buffer_unmap (buffer, &map); @@ -1420,6 +1421,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) /* We need to set caps before adding */ gst_element_add_pad (GST_ELEMENT (demux), gst_object_ref (demux->video_pad)); + gst_flow_combiner_add_pad (demux->flowcombiner, demux->video_pad); /* We only emit no more pads when we have audio and video. Indeed we can * not trust the FLV header to tell us if there will be only audio or @@ -1489,8 +1491,10 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) } /* detect (and deem to be resyncs) large pts gaps */ - gst_flv_demux_update_resync (demux, pts, demux->video_need_discont, - &demux->last_video_pts, &demux->video_time_offset); + if (gst_flv_demux_update_resync (demux, pts, demux->video_need_discont, + &demux->last_video_pts, &demux->video_time_offset)) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } /* Fill buffer with data */ GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->video_time_offset; @@ -1564,27 +1568,19 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) /* Push downstream */ ret = gst_pad_push (demux->video_pad, outbuf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - if (demux->segment.rate < 0.0 && ret == GST_FLOW_EOS && - demux->segment.position > demux->segment.stop) { - /* In reverse playback we can get a GST_FLOW_EOS when - * we are at the end of the segment, so we just need to jump - * back to the previous section. */ - GST_DEBUG_OBJECT (demux, "downstream has reached end of segment"); - demux->video_done = TRUE; - ret = GST_FLOW_OK; - } else { - if (ret == GST_FLOW_NOT_LINKED) - demux->video_linked = FALSE; - else - GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT - " bytes video buffer: %s", demux->tag_data_size, - gst_flow_get_name (ret)); - goto beach; - } + if (G_UNLIKELY (ret != GST_FLOW_OK) && + demux->segment.rate < 0.0 && ret == GST_FLOW_EOS && + demux->segment.position > demux->segment.stop) { + /* In reverse playback we can get a GST_FLOW_EOS when + * we are at the end of the segment, so we just need to jump + * back to the previous section. */ + GST_DEBUG_OBJECT (demux, "downstream has reached end of segment"); + demux->video_done = TRUE; + ret = GST_FLOW_OK; + goto beach; } - demux->video_linked = TRUE; + ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); beach: gst_buffer_unmap (buffer, &map); @@ -1811,10 +1807,6 @@ gst_flv_demux_cleanup (GstFlvDemux * demux) demux->audio_need_discont = TRUE; demux->video_need_discont = TRUE; - /* By default we consider them as linked */ - demux->audio_linked = TRUE; - demux->video_linked = TRUE; - demux->has_audio = FALSE; demux->has_video = FALSE; demux->push_tags = FALSE; @@ -1862,12 +1854,14 @@ gst_flv_demux_cleanup (GstFlvDemux * demux) } if (demux->audio_pad) { + gst_flow_combiner_remove_pad (demux->flowcombiner, demux->audio_pad); gst_element_remove_pad (GST_ELEMENT (demux), demux->audio_pad); gst_object_unref (demux->audio_pad); demux->audio_pad = NULL; } if (demux->video_pad) { + gst_flow_combiner_remove_pad (demux->flowcombiner, demux->video_pad); gst_element_remove_pad (GST_ELEMENT (demux), demux->video_pad); gst_object_unref (demux->video_pad); demux->video_pad = NULL; @@ -1947,13 +1941,8 @@ gst_flv_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) parse: if (G_UNLIKELY (ret != GST_FLOW_OK)) { - if (ret == GST_FLOW_NOT_LINKED && (demux->audio_linked - || demux->video_linked)) { - ret = GST_FLOW_OK; - } else { - GST_DEBUG_OBJECT (demux, "got flow return %s", gst_flow_get_name (ret)); - goto beach; - } + GST_DEBUG_OBJECT (demux, "got flow return %s", gst_flow_get_name (ret)); + goto beach; } if (G_UNLIKELY (demux->flushing)) { @@ -2127,13 +2116,6 @@ parse: } beach: - if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) { - /* If either audio or video is linked we return GST_FLOW_OK */ - if (demux->audio_linked || demux->video_linked) { - ret = GST_FLOW_OK; - } - } - return ret; /* ERRORS */ @@ -2242,13 +2224,8 @@ gst_flv_demux_pull_tag (GstPad * pad, GstFlvDemux * demux) demux->state = FLV_STATE_TAG_TYPE; if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) { - /* If either audio or video is linked we return GST_FLOW_OK */ - if (demux->audio_linked || demux->video_linked) { - ret = GST_FLOW_OK; - } else { - GST_WARNING_OBJECT (demux, "parsing this tag returned not-linked and " - "neither video nor audio are linked"); - } + GST_WARNING_OBJECT (demux, "parsing this tag returned not-linked and " + "neither video nor audio are linked"); } beach: @@ -3132,6 +3109,10 @@ gst_flv_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: + /* Try to push upstream first */ + ret = gst_pad_push_event (demux->sinkpad, event); + if (ret) + break; if (demux->random_access) { ret = gst_flv_demux_handle_seek_pull (demux, event, TRUE); } else { @@ -3159,6 +3140,11 @@ gst_flv_demux_query (GstPad * pad, GstObject * parent, GstQuery * query) { GstFormat format; + /* Try to push upstream first */ + res = gst_pad_peer_query (demux->sinkpad, query); + if (res) + goto beach; + gst_query_parse_duration (query, &format, NULL); /* duration is time only */ @@ -3382,6 +3368,11 @@ gst_flv_demux_dispose (GObject * object) demux->taglist = NULL; } + if (demux->flowcombiner) { + gst_flow_combiner_free (demux->flowcombiner); + demux->flowcombiner = NULL; + } + if (demux->new_seg_event) { gst_event_unref (demux->new_seg_event); demux->new_seg_event = NULL; @@ -3472,6 +3463,7 @@ gst_flv_demux_init (GstFlvDemux * demux) demux->adapter = gst_adapter_new (); demux->taglist = gst_tag_list_new_empty (); + demux->flowcombiner = gst_flow_combiner_new (); gst_segment_init (&demux->segment, GST_FORMAT_TIME); demux->own_index = FALSE; diff --git a/gst/flv/gstflvdemux.h b/gst/flv/gstflvdemux.h index 79a8902e..611482c3 100644 --- a/gst/flv/gstflvdemux.h +++ b/gst/flv/gstflvdemux.h @@ -22,6 +22,7 @@ #include <gst/gst.h> #include <gst/base/gstadapter.h> +#include <gst/base/gstflowcombiner.h> #include "gstindex.h" G_BEGIN_DECLS @@ -74,6 +75,8 @@ struct _GstFlvDemux GstAdapter *adapter; + GstFlowCombiner *flowcombiner; + GstSegment segment; GstEvent *new_seg_event; diff --git a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h index 64322a7a..4edf900b 100644 --- a/gst/isomp4/fourcc.h +++ b/gst/isomp4/fourcc.h @@ -304,6 +304,11 @@ G_BEGIN_DECLS /* ilst metatags */ #define FOURCC__cmt GST_MAKE_FOURCC(0xa9, 'c','m','t') +/* apple tags */ +#define FOURCC__mak GST_MAKE_FOURCC(0xa9, 'm','a','k') +#define FOURCC__mod GST_MAKE_FOURCC(0xa9, 'm','o','d') +#define FOURCC__swr GST_MAKE_FOURCC(0xa9, 's','w','r') + /* Chapters reference */ #define FOURCC_chap GST_MAKE_FOURCC('c','h','a','p') diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 53847c00..7df62597 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -294,9 +294,6 @@ struct _QtDemuxStream GstSegment segment; guint32 segment_seqnum; /* segment event seqnum obtained from seek */ - /* last GstFlowReturn */ - GstFlowReturn last_ret; - /* quicktime segments */ guint32 n_segments; QtDemuxSegment *segments; @@ -467,7 +464,8 @@ static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux, static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux); static void gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream); -static void gst_qtdemux_stream_clear (QtDemuxStream * stream); +static void gst_qtdemux_stream_clear (GstQTDemux * qtdemux, + QtDemuxStream * stream); static void gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int index); static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux); static void qtdemux_do_allocation (GstQTDemux * qtdemux, @@ -546,6 +544,7 @@ gst_qtdemux_init (GstQTDemux * qtdemux) qtdemux->have_group_id = FALSE; qtdemux->group_id = G_MAXUINT; gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); + qtdemux->flowcombiner = gst_flow_combiner_new (); GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE); } @@ -559,6 +558,7 @@ gst_qtdemux_dispose (GObject * object) g_object_unref (G_OBJECT (qtdemux->adapter)); qtdemux->adapter = NULL; } + gst_flow_combiner_free (qtdemux->flowcombiner); G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -1397,7 +1397,6 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment, stream->sample_index = -1; stream->offset_in_sample = 0; stream->segment_index = -1; - stream->last_ret = GST_FLOW_OK; stream->sent_eos = FALSE; stream->segment_seqnum = seqnum; @@ -1427,7 +1426,6 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) gboolean flush; gboolean update; GstSegment seeksegment; - int i; guint32 seqnum = 0; GstEvent *flush_event; @@ -1504,10 +1502,6 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg); } - /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */ - for (i = 0; i < qtdemux->n_streams; i++) - qtdemux->streams[i]->last_ret = GST_FLOW_OK; - gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop, qtdemux->sinkpad, NULL); @@ -1562,10 +1556,10 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent, GstClockTime ts = gst_util_get_timestamp (); #endif - if (qtdemux->upstream_newsegment || qtdemux->fragmented) { + if (qtdemux->upstream_newsegment && qtdemux->fragmented) { /* seek should be handled by upstream, we might need to re-download fragments */ GST_DEBUG_OBJECT (qtdemux, - "leting upstream handle seek for smoothstreaming"); + "let upstream handle seek for fragmented playback"); goto upstream; } @@ -1715,7 +1709,6 @@ _create_stream (void) stream->time_position = 0; stream->sample_index = -1; stream->offset_in_sample = 0; - stream->last_ret = GST_FLOW_OK; stream->new_stream = TRUE; return stream; } @@ -1879,10 +1872,9 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) qtdemux->got_moov = FALSE; } else if (qtdemux->mss_mode) { for (n = 0; n < qtdemux->n_streams; n++) - gst_qtdemux_stream_clear (qtdemux->streams[n]); + gst_qtdemux_stream_clear (qtdemux, qtdemux->streams[n]); } else { for (n = 0; n < qtdemux->n_streams; n++) { - qtdemux->streams[n]->last_ret = GST_FLOW_OK; qtdemux->streams[n]->sent_eos = FALSE; qtdemux->streams[n]->segment_seqnum = 0; qtdemux->streams[n]->time_position = 0; @@ -2130,7 +2122,7 @@ gst_qtdemux_stbl_free (QtDemuxStream * stream) } static void -gst_qtdemux_stream_clear (QtDemuxStream * stream) +gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream) { if (stream->allocator) gst_object_unref (stream->allocator); @@ -2154,7 +2146,6 @@ gst_qtdemux_stream_clear (QtDemuxStream * stream) /* free stbl sub-atoms */ gst_qtdemux_stbl_free (stream); - stream->last_ret = GST_FLOW_OK; stream->sent_eos = FALSE; stream->segment_index = -1; stream->time_position = 0; @@ -2167,12 +2158,14 @@ gst_qtdemux_stream_clear (QtDemuxStream * stream) static void gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream) { - gst_qtdemux_stream_clear (stream); + gst_qtdemux_stream_clear (qtdemux, stream); if (stream->caps) gst_caps_unref (stream->caps); stream->caps = NULL; - if (stream->pad) + if (stream->pad) { gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); + gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad); + } g_free (stream); } @@ -3511,8 +3504,6 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream, stream->segment_seqnum = 0; } gst_pad_push_event (stream->pad, event); - /* assume we can send more data now */ - stream->last_ret = GST_FLOW_OK; /* clear to send tags on this pad now */ gst_qtdemux_push_tags (qtdemux, stream); } @@ -3817,42 +3808,12 @@ gst_qtdemux_sync_streams (GstQTDemux * demux) * GST_FLOW_EOS: when all pads EOS or NOT_LINKED. */ static GstFlowReturn -gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream, - GstFlowReturn ret) +gst_qtdemux_combine_flows (GstQTDemux * demux, GstFlowReturn ret) { - gint i; - gboolean unexpected = FALSE, not_linked = TRUE; - GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret)); - /* store the value */ - stream->last_ret = ret; - - /* any other error that is not-linked or eos can be returned right away */ - if (G_LIKELY (ret != GST_FLOW_EOS && ret != GST_FLOW_NOT_LINKED)) - goto done; + ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); - /* only return NOT_LINKED if all other pads returned NOT_LINKED */ - for (i = 0; i < demux->n_streams; i++) { - QtDemuxStream *ostream = demux->streams[i]; - - ret = ostream->last_ret; - - /* no unexpected or unlinked, return */ - if (G_LIKELY (ret != GST_FLOW_EOS && ret != GST_FLOW_NOT_LINKED)) - goto done; - - /* we check to see if we have at least 1 unexpected or all unlinked */ - unexpected |= (ret == GST_FLOW_EOS); - not_linked &= (ret == GST_FLOW_NOT_LINKED); - } - - /* when we get here, we all have unlinked or unexpected */ - if (not_linked) - ret = GST_FLOW_NOT_LINKED; - else if (unexpected) - ret = GST_FLOW_EOS; -done: GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret)); return ret; } @@ -4287,7 +4248,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) goto next; /* last pushed sample was out of boundary, goto next sample */ - if (G_UNLIKELY (stream->last_ret == GST_FLOW_EOS)) + if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS)) goto next; if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) { @@ -4346,7 +4307,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) } /* combine flows */ - ret = gst_qtdemux_combine_flows (qtdemux, stream, ret); + ret = gst_qtdemux_combine_flows (qtdemux, ret); /* ignore unlinked, we will not push on the pad anymore and we will EOS when * we have no more data for the pad to push */ if (ret == GST_FLOW_EOS) @@ -5121,7 +5082,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) } /* combine flows */ - ret = gst_qtdemux_combine_flows (demux, stream, ret); + ret = gst_qtdemux_combine_flows (demux, ret); } else { /* skip this data, stream is EOS */ gst_adapter_flush (demux->adapter, demux->neededbytes); @@ -6020,6 +5981,7 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p", GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux); gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); + gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad); if (stream->pending_tags) gst_tag_list_unref (stream->pending_tags); @@ -6952,6 +6914,12 @@ qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd, fourcc = QT_FOURCC (stsd_data + 4); data = stsd_data + 8; + if (size == 0) { + GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting " + "svq3 atom parsing"); + goto end; + } + switch (fourcc) { case FOURCC_gama:{ if (size == 12) { @@ -7170,6 +7138,93 @@ bad_data: return 0; } +static gboolean +qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux, + GstByteReader * reader, guint32 * matrix, const gchar * atom) +{ + /* + * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30) + * [0 1 2] + * [3 4 5] + * [6 7 8] + */ + + if (gst_byte_reader_get_remaining (reader) < 36) + return FALSE; + + matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader); + matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader); + + GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom); + GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16, + matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16, + matrix[2] & 0xFF); + GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16, + matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16, + matrix[5] & 0xFF); + GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16, + matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16, + matrix[8] & 0xFF); + + return TRUE; +} + +static void +qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux, + QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist) +{ + +/* [a b c] + * [d e f] + * [g h i] + * + * This macro will only compare value abdegh, it expects cfi to have already + * been checked + */ +#define QTCHECK_MATRIX(m,a,b,d,e,g,h) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \ + (m)[3] == (d << 16) && (m)[4] == (e << 16) && \ + (m)[6] == (g << 16) && (m)[7] == (h << 16)) + + /* only handle the cases where the last column has standard values */ + if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) { + const gchar *rotation_tag = NULL; + + /* no rotation needed */ + if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1, 0, 0)) { + /* NOP */ + } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0, + stream->display_height, 0)) { + rotation_tag = "rotate-90"; + } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16, + stream->display_width, stream->display_height)) { + rotation_tag = "rotate-180"; + } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0, 0, + stream->display_width)) { + rotation_tag = "rotate-270"; + } else { + GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values"); + } + + GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s", + rotation_tag); + if (rotation_tag != NULL) { + if (*taglist == NULL) + *taglist = gst_tag_list_new_empty (); + gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL); + } + } else { + GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values"); + } +} + /* parse the traks. * With each track we associate a new QtDemuxStream that contains all the info * about the trak. @@ -7380,19 +7435,27 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) guint32 w = 0, h = 0; gboolean gray; gint depth, palette_size, palette_count; + guint32 matrix[9]; guint32 *palette_data = NULL; stream->sampled = TRUE; /* version 1 uses some 64-bit ints */ - if (!gst_byte_reader_skip (&tkhd, 56 + value_size) - || !gst_byte_reader_get_uint32_be (&tkhd, &w) + if (!gst_byte_reader_skip (&tkhd, 20 + value_size)) + goto corrupt_file; + + if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd")) + goto corrupt_file; + + if (!gst_byte_reader_get_uint32_be (&tkhd, &w) || !gst_byte_reader_get_uint32_be (&tkhd, &h)) goto corrupt_file; stream->display_width = w >> 16; stream->display_height = h >> 16; + qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix, &list); + offset = 16; if (len < 86) goto corrupt_file; @@ -7496,7 +7559,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) } if (codec) { - list = gst_tag_list_new_empty (); + if (list == NULL) + list = gst_tag_list_new_empty (); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_VIDEO_CODEC, codec, NULL); g_free (codec); @@ -8276,7 +8340,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) GstStructure *s; gint bitrate = 0; - list = gst_tag_list_new_empty (); + if (list == NULL) + list = gst_tag_list_new_empty (); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_AUDIO_CODEC, codec, NULL); g_free (codec); @@ -8886,6 +8951,7 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) gst_pad_push_event (oldpad, gst_event_new_eos ()); gst_pad_set_active (oldpad, FALSE); gst_element_remove_pad (GST_ELEMENT (qtdemux), oldpad); + gst_flow_combiner_remove_pad (qtdemux->flowcombiner, oldpad); gst_object_unref (oldpad); } @@ -9728,6 +9794,9 @@ static const struct FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, { FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL, qtdemux_tag_add_classification}, { + FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, { + FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, { + FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, { /* This is a special case, some tags are stored in this * 'reverse dns naming', according to: diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h index 3fe01954..47c82029 100644 --- a/gst/isomp4/qtdemux.h +++ b/gst/isomp4/qtdemux.h @@ -23,6 +23,7 @@ #include <gst/gst.h> #include <gst/base/gstadapter.h> +#include <gst/base/gstflowcombiner.h> G_BEGIN_DECLS @@ -64,6 +65,8 @@ struct _GstQTDemux { gint n_audio_streams; gint n_sub_streams; + GstFlowCombiner *flowcombiner; + gboolean have_group_id; guint group_id; diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 7fa71590..63ecfe1b 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -190,6 +190,7 @@ gst_matroska_demux_finalize (GObject * object) GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (object); gst_matroska_read_common_finalize (&demux->common); + gst_flow_combiner_free (demux->flowcombiner); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -266,44 +267,7 @@ gst_matroska_demux_init (GstMatroskaDemux * demux) /* finish off */ gst_matroska_demux_reset (GST_ELEMENT (demux)); -} - -/* - * Returns the aggregated GstFlowReturn. - */ -static GstFlowReturn -gst_matroska_demux_combine_flows (GstMatroskaDemux * demux, - GstMatroskaTrackContext * track, GstFlowReturn ret) -{ - guint i; - - /* store the value */ - track->last_flow = ret; - - /* any other error that is not-linked can be returned right away */ - if (ret != GST_FLOW_NOT_LINKED) - goto done; - - /* only return NOT_LINKED if all other pads returned NOT_LINKED */ - g_assert (demux->common.src->len == demux->common.num_streams); - for (i = 0; i < demux->common.src->len; i++) { - GstMatroskaTrackContext *ostream = g_ptr_array_index (demux->common.src, - i); - - if (ostream == NULL) - continue; - - ret = ostream->last_flow; - /* some other return value (must be SUCCESS but we can return - * other values as well) */ - if (ret != GST_FLOW_NOT_LINKED) - goto done; - } - /* if we get here, all other pads were unlinked and we return - * NOT_LINKED then */ -done: - GST_LOG_OBJECT (demux, "combined return %s", gst_flow_get_name (ret)); - return ret; + demux->flowcombiner = gst_flow_combiner_new (); } static void @@ -459,7 +423,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT | GST_MATROSKA_TRACK_LACING; - context->last_flow = GST_FLOW_OK; context->from_time = GST_CLOCK_TIME_NONE; context->from_offset = -1; context->to_offset = G_MAXINT64; @@ -1271,6 +1234,7 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) gst_pad_set_caps (context->pad, context->caps); gst_element_add_pad (GST_ELEMENT (demux), context->pad); + gst_flow_combiner_add_pad (demux->flowcombiner, context->pad); g_free (padname); @@ -2555,7 +2519,7 @@ gst_matroska_demux_push_stream_headers (GstMatroskaDemux * demux, stream->stream_headers = NULL; /* combine flows */ - ret = gst_matroska_demux_combine_flows (demux, stream, ret); + ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); return ret; } @@ -3657,7 +3621,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, } } /* combine flows */ - ret = gst_matroska_demux_combine_flows (demux, stream, ret); + ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); next_lace: size -= lace_size[n]; @@ -3683,7 +3647,7 @@ eos: stream->eos = TRUE; ret = GST_FLOW_OK; /* combine flows */ - ret = gst_matroska_demux_combine_flows (demux, stream, ret); + ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); goto done; } invalid_lacing: diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index 4453fe8f..36a686da 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -24,6 +24,7 @@ #define __GST_MATROSKA_DEMUX_H__ #include <gst/gst.h> +#include <gst/base/gstflowcombiner.h> #include "ebml-read.h" #include "matroska-ids.h" @@ -58,6 +59,8 @@ typedef struct _GstMatroskaDemux { guint group_id; gboolean have_group_id; + GstFlowCombiner *flowcombiner; + /* state */ gboolean streaming; guint level_up; diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index f7eea2b5..68a68d40 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -497,7 +497,6 @@ struct _GstMatroskaTrackContext { GstPad *pad; GstCaps *caps; guint index; - GstFlowReturn last_flow; /* reverse playback */ GstClockTime from_time; gint64 from_offset; diff --git a/gst/matroska/matroska-parse.c b/gst/matroska/matroska-parse.c index c53bcf3f..4d3cb421 100644 --- a/gst/matroska/matroska-parse.c +++ b/gst/matroska/matroska-parse.c @@ -299,7 +299,6 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml) context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT | GST_MATROSKA_TRACK_LACING; - context->last_flow = GST_FLOW_OK; context->to_offset = G_MAXINT64; context->alignment = 1; parse->common.num_streams++; diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c index ae5ea463..73381baf 100644 --- a/gst/matroska/matroska-read-common.c +++ b/gst/matroska/matroska-read-common.c @@ -1213,7 +1213,7 @@ gst_matroska_read_common_parse_header (GstMatroskaReadCommon * common, if (num != GST_EBML_VERSION) { GST_ERROR_OBJECT (ebml, "Unsupported EBML version %" G_GUINT64_FORMAT, num); - return GST_FLOW_ERROR; + goto exit_error; } GST_DEBUG_OBJECT (ebml, "EbmlReadVersion: %" G_GUINT64_FORMAT, num); @@ -2959,8 +2959,6 @@ gst_matroska_read_common_reset_streams (GstMatroskaReadCommon * common, context->set_discont = TRUE; context->eos = FALSE; context->from_time = GST_CLOCK_TIME_NONE; - if (full) - context->last_flow = GST_FLOW_OK; if (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO) { GstMatroskaTrackVideoContext *videocontext = (GstMatroskaTrackVideoContext *) context; diff --git a/gst/multipart/multipartdemux.c b/gst/multipart/multipartdemux.c index 7ba880c6..a62c2a06 100644 --- a/gst/multipart/multipartdemux.c +++ b/gst/multipart/multipartdemux.c @@ -188,6 +188,8 @@ gst_multipart_demux_init (GstMultipartDemux * multipart) multipart->header_completed = FALSE; multipart->scanpos = 0; multipart->singleStream = DEFAULT_SINGLE_STREAM; + multipart->have_group_id = FALSE; + multipart->group_id = G_MAXUINT; } static void @@ -296,6 +298,8 @@ gst_multipart_find_pad_by_mime (GstMultipartDemux * demux, gchar * mime, gchar *name; const gchar *capsname; GstCaps *caps; + gchar *stream_id; + GstEvent *event; mppad = g_new0 (GstMultipartPad, 1); @@ -316,17 +320,42 @@ gst_multipart_find_pad_by_mime (GstMultipartDemux * demux, gchar * mime, demux->srcpads = g_slist_prepend (demux->srcpads, mppad); demux->numpads++; + gst_pad_use_fixed_caps (pad); + gst_pad_set_active (pad, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (demux), pad); + + /* prepare and send stream-start */ + if (!demux->have_group_id) { + event = gst_pad_get_sticky_event (demux->sinkpad, + GST_EVENT_STREAM_START, 0); + + if (event) { + demux->have_group_id = + gst_event_parse_group_id (event, &demux->group_id); + gst_event_unref (event); + } else if (!demux->have_group_id) { + demux->have_group_id = TRUE; + demux->group_id = gst_util_group_id_next (); + } + } + + stream_id = gst_pad_create_stream_id (pad, + GST_ELEMENT_CAST (demux), demux->mime_type); + + event = gst_event_new_stream_start (stream_id); + if (demux->have_group_id) + gst_event_set_group_id (event, demux->group_id); + + gst_pad_push_event (pad, event); + g_free (stream_id); + /* take the mime type, convert it to the caps name */ capsname = gst_multipart_demux_get_gstname (demux, mime); caps = gst_caps_from_string (capsname); GST_DEBUG_OBJECT (demux, "caps for pad: %s", capsname); - gst_pad_use_fixed_caps (pad); - gst_pad_set_active (pad, TRUE); gst_pad_set_caps (pad, caps); gst_caps_unref (caps); - gst_element_add_pad (GST_ELEMENT_CAST (demux), pad); - if (created) { *created = TRUE; } @@ -592,6 +621,9 @@ gst_multipart_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) if (G_UNLIKELY (datalen <= 0)) { GST_DEBUG_OBJECT (multipart, "skipping empty content."); gst_adapter_flush (adapter, size - datalen); + } else if (G_UNLIKELY (!multipart->mime_type)) { + GST_DEBUG_OBJECT (multipart, "content has no MIME type."); + gst_adapter_flush (adapter, size - datalen); } else { GstClockTime ts; @@ -677,6 +709,8 @@ gst_multipart_demux_change_state (GstElement * element, multipart->content_length = -1; multipart->scanpos = 0; gst_multipart_demux_remove_src_pads (multipart); + multipart->have_group_id = FALSE; + multipart->group_id = G_MAXUINT; break; case GST_STATE_CHANGE_READY_TO_NULL: break; diff --git a/gst/multipart/multipartdemux.h b/gst/multipart/multipartdemux.h index 4db33c26..cb81a83d 100644 --- a/gst/multipart/multipartdemux.h +++ b/gst/multipart/multipartdemux.h @@ -87,6 +87,10 @@ struct _GstMultipartDemux gint scanpos; gboolean singleStream; + + /* to handle stream-start */ + gboolean have_group_id; + guint group_id; }; struct _GstMultipartDemuxClass diff --git a/gst/rtp/gstrtpdvpay.c b/gst/rtp/gstrtpdvpay.c index d779f123..db75cf65 100644 --- a/gst/rtp/gstrtpdvpay.c +++ b/gst/rtp/gstrtpdvpay.c @@ -300,7 +300,12 @@ gst_rtp_dv_pay_handle_buffer (GstRTPBasePayload * basepayload, max_payload_size = ((GST_RTP_BASE_PAYLOAD_MTU (rtpdvpay) - hdrlen) / 80) * 80; /* The length of the buffer to transmit. */ - gst_buffer_map (buffer, &map, GST_MAP_READ); + if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) { + GST_ELEMENT_ERROR (rtpdvpay, CORE, FAILED, + (NULL), ("Failed to map buffer")); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } data = map.data; size = map.size; @@ -327,7 +332,13 @@ gst_rtp_dv_pay_handle_buffer (GstRTPBasePayload * basepayload, outbuf = gst_rtp_buffer_new_allocate (max_payload_size, 0, 0); GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer); - gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + if (!gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp)) { + gst_buffer_unref (outbuf); + GST_ELEMENT_ERROR (rtpdvpay, CORE, FAILED, + (NULL), ("Failed to map RTP buffer")); + ret = GST_FLOW_ERROR; + goto beach; + } dest = gst_rtp_buffer_get_payload (&rtp); filled = 0; } @@ -368,6 +379,8 @@ gst_rtp_dv_pay_handle_buffer (GstRTPBasePayload * basepayload, outbuf = NULL; } } + +beach: gst_buffer_unmap (buffer, &map); gst_buffer_unref (buffer); diff --git a/gst/rtp/gstrtpg729pay.c b/gst/rtp/gstrtpg729pay.c index 898bcd12..306e1757 100644 --- a/gst/rtp/gstrtpg729pay.c +++ b/gst/rtp/gstrtpg729pay.c @@ -204,6 +204,17 @@ gst_rtp_g729_pay_push (GstRTPG729Pay * rtpg729pay, return ret; } +static GstFlowReturn +gst_rtp_g729_pay_push_and_free (GstRTPG729Pay * rtpg729pay, + guint8 * data, guint payload_len) +{ + GstFlowReturn ret; + + ret = gst_rtp_g729_pay_push (rtpg729pay, data, payload_len); + g_free (data); + return ret; +} + static void gst_rtp_g729_pay_recalc_rtp_time (GstRTPG729Pay * rtpg729pay, GstClockTime time) { @@ -309,7 +320,7 @@ gst_rtp_g729_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf) if (GST_BUFFER_IS_DISCONT (buf)) { /* flush remainder */ if (available > 0) { - gst_rtp_g729_pay_push (rtpg729pay, + gst_rtp_g729_pay_push_and_free (rtpg729pay, gst_adapter_take (adapter, available), available); available = 0; } @@ -354,7 +365,7 @@ gst_rtp_g729_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf) (available / G729_FRAME_SIZE) * G729_FRAME_SIZE); } - ret = gst_rtp_g729_pay_push (rtpg729pay, + ret = gst_rtp_g729_pay_push_and_free (rtpg729pay, gst_adapter_take (adapter, payload_len), payload_len); available -= payload_len; } diff --git a/gst/rtp/gstrtpgstpay.c b/gst/rtp/gstrtpgstpay.c index 016cefe9..c0a9d94d 100644 --- a/gst/rtp/gstrtpgstpay.c +++ b/gst/rtp/gstrtpgstpay.c @@ -257,12 +257,13 @@ gst_rtp_gst_pay_change_state (GstElement * element, GstStateChange transition) return ret; } +#define RTP_HEADER_LEN 12 static gboolean gst_rtp_gst_pay_create_from_adapter (GstRtpGSTPay * rtpgstpay, GstClockTime timestamp) { - guint avail; + guint avail, mtu; guint frag_offset; GstBufferList *list; @@ -270,7 +271,9 @@ gst_rtp_gst_pay_create_from_adapter (GstRtpGSTPay * rtpgstpay, if (avail == 0) return FALSE; - list = gst_buffer_list_new (); + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpgstpay); + + list = gst_buffer_list_new_sized ((avail / (mtu - (RTP_HEADER_LEN + 8))) + 1); frag_offset = 0; while (avail) { @@ -287,7 +290,7 @@ gst_rtp_gst_pay_create_from_adapter (GstRtpGSTPay * rtpgstpay, packet_len = gst_rtp_buffer_calc_packet_len (8 + avail, 0, 0); /* fill one MTU or all available bytes */ - towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpgstpay)); + towrite = MIN (packet_len, mtu); /* this is the payload length */ payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); diff --git a/gst/rtp/gstrtph263pay.c b/gst/rtp/gstrtph263pay.c index a9672ec1..4de0fabd 100644 --- a/gst/rtp/gstrtph263pay.c +++ b/gst/rtp/gstrtph263pay.c @@ -958,7 +958,7 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, GST_DEBUG ("MCBPC index: %d", mb_type_index); if (mb_type_index == -1) { GST_ERROR ("MB index shouldn't be -1 in window: %08x", context->window); - return NULL; + goto beach; } mac->ebit = @@ -977,7 +977,7 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, GST_DEBUG ("CBPY index: %d", cbpy_type_index); if (cbpy_type_index == -1) { GST_ERROR ("CBPY index shouldn't be -1 in window: %08x", context->window); - return NULL; + goto beach; } mac->ebit = @@ -1018,7 +1018,7 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, if (tcoef_type_index == -1) { GST_ERROR ("TCOEF index shouldn't be -1 in window: %08x", context->window); - return NULL; + goto beach; } mac->ebit = gst_rtp_h263_pay_move_window_right (context, @@ -1069,7 +1069,7 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, GST_DEBUG ("MCBPC index: %d", mb_type_index); if (mb_type_index == -1) { GST_ERROR ("MB index shouldn't be -1 in window: %08x", context->window); - return NULL; + goto beach; } mac->ebit = gst_rtp_h263_pay_move_window_right (context, mcbpc_P[mb_type_index][2], @@ -1087,7 +1087,7 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, GST_DEBUG ("CBPY index: %d", cbpy_type_index); if (cbpy_type_index == -1) { GST_ERROR ("CBPY index shouldn't be -1 in window: %08x", context->window); - return NULL; + goto beach; } mac->ebit = gst_rtp_h263_pay_move_window_right (context, cbpy_P[cbpy_type_index][2], @@ -1121,7 +1121,7 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, if (mvd_type == -1) { GST_ERROR ("MVD1-4 index shouldn't be -1 in window: %08x", context->window); - return NULL; + goto beach; } //set the MB mvd values mac->mvd[j] = mvd[mvd_type][3]; @@ -1166,7 +1166,7 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, if (tcoef_type_index == -1) { GST_ERROR ("TCOEF index shouldn't be -1 in window: %08x", context->window); - return NULL; + goto beach; } mac->ebit = @@ -1193,6 +1193,10 @@ gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, mac->length = mac->end - mac->start + 1; return mac; + +beach: + gst_rtp_h263_pay_mb_destroy (mac); + return NULL; } static GstRtpH263PayMB * @@ -1709,7 +1713,8 @@ gst_rtp_h263_pay_flush (GstRtpH263Pay * rtph263pay) if (!gst_rtp_h263_pay_mode_B_fragment (rtph263pay, context, context->gobs[i])) { GST_ERROR ("There was an error fragmenting in mode B"); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto end; } } else { //IMPLEMENT C mode diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c index a7b52bda..586cf734 100644 --- a/gst/rtp/gstrtph264depay.c +++ b/gst/rtp/gstrtph264depay.c @@ -944,7 +944,11 @@ gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) memcpy (map.data + sizeof (sync_bytes), payload, nalu_size); gst_buffer_unmap (outbuf, &map); - gst_adapter_push (rtph264depay->adapter, outbuf); + outbuf = + gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, + marker); + if (outbuf) + gst_adapter_push (rtph264depay->adapter, outbuf); payload += nalu_size; payload_len -= nalu_size; diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c index 5fb15ceb..3d835aeb 100644 --- a/gst/rtp/gstrtph264pay.c +++ b/gst/rtp/gstrtph264pay.c @@ -166,6 +166,8 @@ gst_rtp_h264_pay_init (GstRtpH264Pay * rtph264pay) (GDestroyNotify) gst_buffer_unref); rtph264pay->last_spspps = -1; rtph264pay->spspps_interval = DEFAULT_CONFIG_INTERVAL; + rtph264pay->delta_unit = FALSE; + rtph264pay->discont = FALSE; rtph264pay->adapter = gst_adapter_new (); } @@ -704,7 +706,8 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader, static GstFlowReturn gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, - GstBuffer * paybuf, GstClockTime dts, GstClockTime pts, gboolean end_of_au); + GstBuffer * paybuf, GstClockTime dts, GstClockTime pts, gboolean end_of_au, + gboolean delta_unit, gboolean discont); static GstFlowReturn gst_rtp_h264_pay_send_sps_pps (GstRTPBasePayload * basepayload, @@ -721,7 +724,7 @@ gst_rtp_h264_pay_send_sps_pps (GstRTPBasePayload * basepayload, GST_DEBUG_OBJECT (rtph264pay, "inserting SPS in the stream"); /* resend SPS */ ret = gst_rtp_h264_pay_payload_nal (basepayload, gst_buffer_ref (sps_buf), - dts, pts, FALSE); + dts, pts, FALSE, FALSE, FALSE); /* Not critical here; but throw a warning */ if (ret != GST_FLOW_OK) { sent_all_sps_pps = FALSE; @@ -735,7 +738,7 @@ gst_rtp_h264_pay_send_sps_pps (GstRTPBasePayload * basepayload, GST_DEBUG_OBJECT (rtph264pay, "inserting PPS in the stream"); /* resend PPS */ ret = gst_rtp_h264_pay_payload_nal (basepayload, gst_buffer_ref (pps_buf), - dts, pts, FALSE); + dts, pts, FALSE, FALSE, FALSE); /* Not critical here; but throw a warning */ if (ret != GST_FLOW_OK) { sent_all_sps_pps = FALSE; @@ -749,9 +752,15 @@ gst_rtp_h264_pay_send_sps_pps (GstRTPBasePayload * basepayload, return ret; } +/* @delta_unit: if %FALSE the first packet sent won't have the + * GST_BUFFER_FLAG_DELTA_UNIT flag. + * @discont: if %TRUE the first packet sent will have the + * GST_BUFFER_FLAG_DISCONT flag. + */ static GstFlowReturn gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, - GstBuffer * paybuf, GstClockTime dts, GstClockTime pts, gboolean end_of_au) + GstBuffer * paybuf, GstClockTime dts, GstClockTime pts, gboolean end_of_au, + gboolean delta_unit, gboolean discont) { GstRtpH264Pay *rtph264pay; GstFlowReturn ret; @@ -824,12 +833,11 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, packet_len = gst_rtp_buffer_calc_packet_len (size, 0, 0); if (packet_len < mtu) { + /* will fit in one packet */ GST_DEBUG_OBJECT (basepayload, "NAL Unit fit in one packet datasize=%d mtu=%d", size, mtu); - /* will fit in one packet */ - /* use buffer lists - * create buffer without payload containing only the RTP header + /* create buffer without payload containing only the RTP header * (memory block at index 0) */ outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); @@ -844,18 +852,25 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, GST_BUFFER_PTS (outbuf) = pts; GST_BUFFER_DTS (outbuf) = dts; - /* insert payload memory block */ - outbuf = gst_buffer_append (outbuf, paybuf); - - list = gst_buffer_list_new (); + if (!delta_unit) + /* Only the first packet sent should not have the flag */ + delta_unit = TRUE; + else + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); - /* add the buffer to the buffer list */ - gst_buffer_list_add (list, outbuf); + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first packet sent should have the flag */ + discont = FALSE; + } gst_rtp_buffer_unmap (&rtp); - /* push the list to the next element in the pipe */ - ret = gst_rtp_base_payload_push_list (basepayload, list); + /* insert payload memory block */ + outbuf = gst_buffer_append (outbuf, paybuf); + + /* push the buffer to the next element */ + ret = gst_rtp_base_payload_push (basepayload, outbuf); } else { /* fragmentation Units FU-A */ guint limitedSize; @@ -875,7 +890,7 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, /* We keep 2 bytes for FU indicator and FU Header */ payload_len = gst_rtp_buffer_calc_payload_len (mtu - 2, 0, 0); - list = gst_buffer_list_new (); + list = gst_buffer_list_new_sized ((size / payload_len) + 1); while (end == 0) { limitedSize = size < payload_len ? size : payload_len; @@ -915,6 +930,18 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, gst_buffer_copy_region (paybuf, GST_BUFFER_COPY_MEMORY, pos, limitedSize)); + if (!delta_unit) + /* Only the first packet sent should not have the flag */ + delta_unit = TRUE; + else + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first packet sent should have the flag */ + discont = FALSE; + } + /* add the buffer to the buffer list */ gst_buffer_list_add (list, outbuf); @@ -946,6 +973,8 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, gboolean avc; GstBuffer *paybuf = NULL; gsize skip; + gboolean delayed_not_delta_unit = FALSE; + gboolean delayed_discont = FALSE; rtph264pay = GST_RTP_H264_PAY (basepayload); @@ -962,11 +991,34 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, size = map.size; pts = GST_BUFFER_PTS (buffer); dts = GST_BUFFER_DTS (buffer); + rtph264pay->delta_unit = GST_BUFFER_FLAG_IS_SET (buffer, + GST_BUFFER_FLAG_DELTA_UNIT); + rtph264pay->discont = GST_BUFFER_IS_DISCONT (buffer); GST_DEBUG_OBJECT (basepayload, "got %" G_GSIZE_FORMAT " bytes", size); } else { dts = gst_adapter_prev_dts (rtph264pay->adapter, NULL); pts = gst_adapter_prev_pts (rtph264pay->adapter, NULL); if (buffer) { + if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { + if (gst_adapter_available (rtph264pay->adapter) == 0) + rtph264pay->delta_unit = FALSE; + else + /* This buffer contains a key frame but the adapter isn't empty. So + * we'll purge it first by sending a first packet and then the second + * one won't have the DELTA_UNIT flag. */ + delayed_not_delta_unit = TRUE; + } + + if (GST_BUFFER_IS_DISCONT (buffer)) { + if (gst_adapter_available (rtph264pay->adapter) == 0) + rtph264pay->discont = TRUE; + else + /* This buffer has the DISCONT flag but the adapter isn't empty. So + * we'll purge it first by sending a first packet and then the second + * one will have the DISCONT flag set. */ + delayed_discont = TRUE; + } + if (!GST_CLOCK_TIME_IS_VALID (dts)) dts = GST_BUFFER_DTS (buffer); if (!GST_CLOCK_TIME_IS_VALID (pts)) @@ -1030,7 +1082,16 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, nal_len); ret = gst_rtp_h264_pay_payload_nal (basepayload, paybuf, dts, pts, - end_of_au); + end_of_au, rtph264pay->delta_unit, rtph264pay->discont); + + if (!rtph264pay->delta_unit) + /* Only the first outgoing packet doesn't have the DELTA_UNIT flag */ + rtph264pay->delta_unit = TRUE; + + if (rtph264pay->discont) + /* Only the first outgoing packet have the DISCONT flag */ + rtph264pay->discont = FALSE; + if (ret != GST_FLOW_OK) break; @@ -1161,7 +1222,24 @@ gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, /* put the data in one or more RTP packets */ ret = gst_rtp_h264_pay_payload_nal (basepayload, paybuf, dts, pts, - end_of_au); + end_of_au, rtph264pay->delta_unit, rtph264pay->discont); + + if (delayed_not_delta_unit) { + rtph264pay->delta_unit = FALSE; + delayed_not_delta_unit = FALSE; + } else { + /* Only the first outgoing packet doesn't have the DELTA_UNIT flag */ + rtph264pay->delta_unit = TRUE; + } + + if (delayed_discont) { + rtph264pay->discont = TRUE; + delayed_discont = FALSE; + } else { + /* Only the first outgoing packet have the DISCONT flag */ + rtph264pay->discont = FALSE; + } + if (ret != GST_FLOW_OK) { break; } diff --git a/gst/rtp/gstrtph264pay.h b/gst/rtp/gstrtph264pay.h index e1f6c7f1..44f7af44 100644 --- a/gst/rtp/gstrtph264pay.h +++ b/gst/rtp/gstrtph264pay.h @@ -74,6 +74,11 @@ struct _GstRtpH264Pay guint spspps_interval; gboolean send_spspps; GstClockTime last_spspps; + + /* TRUE if the next NALU processed should have the DELTA_UNIT flag */ + gboolean delta_unit; + /* TRUE if the next NALU processed should have the DISCONT flag */ + gboolean discont; }; struct _GstRtpH264PayClass diff --git a/gst/rtp/gstrtpj2kpay.c b/gst/rtp/gstrtpj2kpay.c index 6cefc314..98880b29 100644 --- a/gst/rtp/gstrtpj2kpay.c +++ b/gst/rtp/gstrtpj2kpay.c @@ -353,11 +353,11 @@ gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * basepayload, state.next_sot = 0; state.force_packet = FALSE; - list = gst_buffer_list_new (); - /* get max packet length */ max_size = gst_rtp_buffer_calc_payload_len (mtu - HEADER_SIZE, 0, 0); + list = gst_buffer_list_new_sized ((mtu / max_size) + 1); + do { GstBuffer *outbuf; guint8 *header; diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c index 227d8ddb..3e820a08 100644 --- a/gst/rtp/gstrtpjpegpay.c +++ b/gst/rtp/gstrtpjpegpay.c @@ -662,6 +662,8 @@ gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset) } } +#define RTP_HEADER_LEN 12 + static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buffer) @@ -678,7 +680,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, GstMapInfo map; guint8 *data; gsize size; - guint mtu; + guint mtu, max_payload_size; guint bytes_left; guint jpeg_header_size = 0; guint offset; @@ -686,6 +688,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, gboolean sos_found, sof_found, dqt_found, dri_found; gint i; GstBufferList *list = NULL; + gboolean discont; pay = GST_RTP_JPEG_PAY (basepayload); mtu = GST_RTP_BASE_PAYLOAD_MTU (pay); @@ -695,6 +698,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, size = map.size; timestamp = GST_BUFFER_TIMESTAMP (buffer); offset = 0; + discont = GST_BUFFER_IS_DISCONT (buffer); GST_LOG_OBJECT (pay, "got buffer size %" G_GSIZE_FORMAT " , timestamp %" GST_TIME_FORMAT, size, GST_TIME_ARGS (timestamp)); @@ -802,13 +806,14 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size); - list = gst_buffer_list_new (); - bytes_left = sizeof (jpeg_header) + quant_data_size + size; if (dri_found) bytes_left += sizeof (restart_marker_header); + max_payload_size = mtu - (RTP_HEADER_LEN + sizeof (jpeg_header)); + list = gst_buffer_list_new_sized ((bytes_left / max_payload_size) + 1); + frame_done = FALSE; do { GstBuffer *outbuf; @@ -892,6 +897,12 @@ gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first outputted buffer has the DISCONT flag */ + discont = FALSE; + } + /* and add to list */ gst_buffer_list_insert (list, -1, outbuf); diff --git a/gst/rtp/gstrtpmp4gpay.c b/gst/rtp/gstrtpmp4gpay.c index 7913d9ab..e374e5cd 100644 --- a/gst/rtp/gstrtpmp4gpay.c +++ b/gst/rtp/gstrtpmp4gpay.c @@ -543,6 +543,12 @@ gst_rtp_mp4g_pay_flush (GstRtpMP4GPay * rtpmp4gpay) rtpmp4gpay->offset += rtpmp4gpay->frame_len; } + if (rtpmp4gpay->discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first outputted buffer has the DISCONT flag */ + rtpmp4gpay->discont = FALSE; + } + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), outbuf); avail -= payload_len; @@ -563,6 +569,7 @@ gst_rtp_mp4g_pay_handle_buffer (GstRTPBasePayload * basepayload, rtpmp4gpay->first_timestamp = GST_BUFFER_TIMESTAMP (buffer); rtpmp4gpay->first_duration = GST_BUFFER_DURATION (buffer); + rtpmp4gpay->discont = GST_BUFFER_IS_DISCONT (buffer); /* we always encode and flush a full AU */ gst_adapter_push (rtpmp4gpay->adapter, buffer); diff --git a/gst/rtp/gstrtpmp4gpay.h b/gst/rtp/gstrtpmp4gpay.h index fed9c930..7506fad9 100644 --- a/gst/rtp/gstrtpmp4gpay.h +++ b/gst/rtp/gstrtpmp4gpay.h @@ -47,6 +47,7 @@ struct _GstRtpMP4GPay GstAdapter *adapter; GstClockTime first_timestamp; GstClockTime first_duration; + gboolean discont; GstClockTime duration; guint64 offset; diff --git a/gst/rtp/gstrtpmp4vpay.c b/gst/rtp/gstrtpmp4vpay.c index 8ac6d061..56cf9f73 100644 --- a/gst/rtp/gstrtpmp4vpay.c +++ b/gst/rtp/gstrtpmp4vpay.c @@ -223,10 +223,12 @@ gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay) gst_adapter_clear (rtpmp4vpay->adapter); } +#define RTP_HEADER_LEN 12 + static GstFlowReturn gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay) { - guint avail; + guint avail, mtu; GstBuffer *outbuf; GstBuffer *outbuf_data = NULL; GstFlowReturn ret; @@ -248,12 +250,12 @@ gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay) if (!avail) return GST_FLOW_OK; - ret = GST_FLOW_OK; + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp4vpay); /* Use buffer lists. Each frame will be put into a list * of buffers and the whole list will be pushed downstream * at once */ - list = gst_buffer_list_new (); + list = gst_buffer_list_new_sized ((avail / (mtu - RTP_HEADER_LEN)) + 1); while (avail > 0) { guint towrite; @@ -265,7 +267,7 @@ gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay) packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0); /* fill one MTU or all available bytes */ - towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpmp4vpay)); + towrite = MIN (packet_len, mtu); /* this is the payload length */ payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); diff --git a/gst/rtp/gstrtptheoradepay.c b/gst/rtp/gstrtptheoradepay.c index 39c8d2ac..ccfe12db 100644 --- a/gst/rtp/gstrtptheoradepay.c +++ b/gst/rtp/gstrtptheoradepay.c @@ -570,19 +570,16 @@ gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) break; } - g_free (to_free); - if (rtptheoradepay->needs_keyframe) goto request_keyframe; +out: +no_output: + gst_rtp_buffer_unmap (&rtp); + g_free (to_free); return NULL; -no_output: - { - gst_rtp_buffer_unmap (&rtp); - return NULL; - } /* ERORRS */ switch_failed: { @@ -599,8 +596,7 @@ packet_short: ignore_reserved: { GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored"); - gst_rtp_buffer_unmap (&rtp); - return NULL; + goto out; } length_short: { @@ -621,8 +617,7 @@ request_config: gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_structure_new ("GstForceKeyUnit", "all-headers", G_TYPE_BOOLEAN, TRUE, NULL))); - gst_rtp_buffer_unmap (&rtp); - return NULL; + goto out; } request_keyframe: { @@ -630,8 +625,7 @@ request_keyframe: gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload), gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_structure_new_empty ("GstForceKeyUnit"))); - gst_rtp_buffer_unmap (&rtp); - return NULL; + goto out; } } diff --git a/gst/rtp/gstrtpvp8depay.c b/gst/rtp/gstrtpvp8depay.c index 7cc4504a..40f3375e 100644 --- a/gst/rtp/gstrtpvp8depay.c +++ b/gst/rtp/gstrtpvp8depay.c @@ -106,7 +106,7 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstBuffer * buf) GstRtpVP8Depay *self = GST_RTP_VP8_DEPAY (depay); GstBuffer *payload; guint8 *data; - guint offset; + guint hdrsize; guint size; GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT; @@ -134,31 +134,32 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstBuffer * buf) self->started = TRUE; } - offset = 1; + hdrsize = 1; /* Check X optional header */ if ((data[0] & 0x80) != 0) { - offset++; + hdrsize++; /* Check I optional header */ if ((data[1] & 0x80) != 0) { - offset++; - if (G_UNLIKELY (offset + 2 >= size)) + if (G_UNLIKELY (size < 3)) goto too_small; + hdrsize++; /* Check for 16 bits PictureID */ if ((data[2] & 0x80) != 0) - offset++; + hdrsize++; } /* Check L optional header */ if ((data[1] & 0x40) != 0) - offset++; + hdrsize++; /* Check T or K optional headers */ if ((data[1] & 0x20) != 0 || (data[1] & 0x10) != 0) - offset++; + hdrsize++; } + GST_DEBUG_OBJECT (depay, "hdrsize %u, size %u", hdrsize, size); - if (G_UNLIKELY (offset >= size)) + if (G_UNLIKELY (hdrsize >= size)) goto too_small; - payload = gst_rtp_buffer_get_payload_subbuffer (&rtpbuffer, offset, -1); + payload = gst_rtp_buffer_get_payload_subbuffer (&rtpbuffer, hdrsize, -1); gst_adapter_push (self->adapter, payload); /* Marker indicates that it was the last rtp packet for this frame */ diff --git a/gst/rtp/gstrtpvp8pay.c b/gst/rtp/gstrtpvp8pay.c index 47837819..f3ad8455 100644 --- a/gst/rtp/gstrtpvp8pay.c +++ b/gst/rtp/gstrtpvp8pay.c @@ -171,12 +171,13 @@ gst_rtp_vp8_pay_get_property (GObject * object, } static gboolean -gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer) +gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer, + gsize buffer_size) { - GstBitReader *reader = NULL; + GstMapInfo map = GST_MAP_INFO_INIT; + GstBitReader reader; guint8 *data; gsize size; - GstMapInfo map; int i; gboolean keyframe; guint32 partition0_size; @@ -187,7 +188,7 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer) BOOL_DECODER bc; guint8 *pdata; - if (G_UNLIKELY (gst_buffer_get_size (buffer) < 3)) + if (G_UNLIKELY (buffer_size < 3)) goto error; if (!gst_buffer_map (buffer, &map, GST_MAP_READ) || !map.data) @@ -195,7 +196,8 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer) data = map.data; size = map.size; - reader = gst_bit_reader_new (data, size); + + gst_bit_reader_init (&reader, data, size); self->is_keyframe = keyframe = ((data[0] & 0x1) == 0); version = (data[0] >> 1) & 0x7; @@ -212,22 +214,22 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer) offset = keyframe ? 10 : 3; partition0_size += offset; - if (!gst_bit_reader_skip (reader, 24)) + if (!gst_bit_reader_skip (&reader, 24)) goto error; if (keyframe) { /* check start tag: 0x9d 0x01 0x2a */ - if (!gst_bit_reader_get_bits_uint8 (reader, &tmp8, 8) || tmp8 != 0x9d) + if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 8) || tmp8 != 0x9d) goto error; - if (!gst_bit_reader_get_bits_uint8 (reader, &tmp8, 8) || tmp8 != 0x01) + if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 8) || tmp8 != 0x01) goto error; - if (!gst_bit_reader_get_bits_uint8 (reader, &tmp8, 8) || tmp8 != 0x2a) + if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 8) || tmp8 != 0x2a) goto error; /* Skip horizontal size code (16 bits) vertical size code (16 bits) */ - if (!gst_bit_reader_skip (reader, 32)) + if (!gst_bit_reader_skip (&reader, 32)) goto error; } @@ -332,14 +334,12 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer) self->partition_offset[i + 1] = size; - gst_bit_reader_free (reader); gst_buffer_unmap (buffer, &map); return TRUE; error: GST_DEBUG ("Failed to parse frame"); - if (reader) { - gst_bit_reader_free (reader); + if (map.memory != NULL) { gst_buffer_unmap (buffer, &map); } return FALSE; @@ -373,16 +373,7 @@ gst_rtp_vp8_calc_header_len (GstRtpVP8Pay * self) } } -static gsize -gst_rtp_vp8_calc_payload_len (GstRtpVP8Pay * self) -{ - GstRTPBasePayload *payload = GST_RTP_BASE_PAYLOAD (self); - - return gst_rtp_buffer_calc_payload_len (GST_RTP_BASE_PAYLOAD_MTU (payload) - - gst_rtp_vp8_calc_header_len (self), 0, 0); -} - -/* When growing the vp8 header keep gst_rtp_vp8_calc_payload_len in sync */ +/* When growing the vp8 header keep max payload len calculation in sync */ static GstBuffer * gst_rtp_vp8_create_header_buffer (GstRtpVP8Pay * self, guint8 partid, gboolean start, gboolean mark, GstBuffer * in) @@ -423,8 +414,8 @@ gst_rtp_vp8_create_header_buffer (GstRtpVP8Pay * self, guint8 partid, static guint -gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, - GstBufferList * list, guint offset, GstBuffer * buffer) +gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, GstBufferList * list, + guint offset, GstBuffer * buffer, gsize buffer_size, gsize max_payload_len) { guint partition; GstBuffer *header; @@ -434,8 +425,8 @@ gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, gsize remaining; gsize available; - remaining = gst_buffer_get_size (buffer) - offset; - available = gst_rtp_vp8_calc_payload_len (self); + remaining = buffer_size - offset; + available = max_payload_len; if (available > remaining) available = remaining; @@ -462,17 +453,28 @@ gst_rtp_vp8_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) GstRtpVP8Pay *self = GST_RTP_VP8_PAY (payload); GstFlowReturn ret; GstBufferList *list; - guint offset; + gsize size, max_paylen; + guint offset, mtu, vp8_hdr_len; + + size = gst_buffer_get_size (buffer); - if (G_UNLIKELY (!gst_rtp_vp8_pay_parse_frame (self, buffer))) { - g_message ("Failed to parse frame"); + if (G_UNLIKELY (!gst_rtp_vp8_pay_parse_frame (self, buffer, size))) { + GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL), + ("Failed to parse VP8 frame")); return GST_FLOW_ERROR; } - list = gst_buffer_list_new (); + mtu = GST_RTP_BASE_PAYLOAD_MTU (payload); + vp8_hdr_len = gst_rtp_vp8_calc_header_len (self); + max_paylen = gst_rtp_buffer_calc_payload_len (mtu - vp8_hdr_len, 0, 0); - for (offset = 0; offset < gst_buffer_get_size (buffer);) - offset += gst_rtp_vp8_payload_next (self, list, offset, buffer); + list = gst_buffer_list_new_sized ((size / max_paylen) + 1); + + offset = 0; + while (offset < size) { + offset += + gst_rtp_vp8_payload_next (self, list, offset, buffer, size, max_paylen); + } ret = gst_rtp_base_payload_push_list (payload, list); diff --git a/gst/rtp/gstrtpvrawpay.c b/gst/rtp/gstrtpvrawpay.c index cade1786..c03c8d2f 100644 --- a/gst/rtp/gstrtpvrawpay.c +++ b/gst/rtp/gstrtpvrawpay.c @@ -27,6 +27,13 @@ #include "gstrtpvrawpay.h" +enum +{ + PROP_CHUNKS_PER_FRAME = 1 +}; + +#define DEFAULT_CHUNKS_PER_FRAME 10 + GST_DEBUG_CATEGORY_STATIC (rtpvrawpay_debug); #define GST_CAT_DEFAULT (rtpvrawpay_debug) @@ -70,6 +77,10 @@ static gboolean gst_rtp_vraw_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps); static GstFlowReturn gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer); +static void gst_rtp_vraw_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rtp_vraw_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); G_DEFINE_TYPE (GstRtpVRawPay, gst_rtp_vraw_pay, GST_TYPE_RTP_BASE_PAYLOAD) @@ -77,10 +88,23 @@ G_DEFINE_TYPE (GstRtpVRawPay, gst_rtp_vraw_pay, GST_TYPE_RTP_BASE_PAYLOAD) { GstRTPBasePayloadClass *gstrtpbasepayload_class; GstElementClass *gstelement_class; + GObjectClass *gobject_class; + gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + gobject_class->set_property = gst_rtp_vraw_pay_set_property; + gobject_class->get_property = gst_rtp_vraw_pay_get_property; + + g_object_class_install_property (gobject_class, + PROP_CHUNKS_PER_FRAME, + g_param_spec_int ("chunks-per-frame", "Chunks per Frame", + "Split and send out each frame in multiple chunks to reduce overhead", + 1, G_MAXINT, DEFAULT_CHUNKS_PER_FRAME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + gstrtpbasepayload_class->set_caps = gst_rtp_vraw_pay_setcaps; gstrtpbasepayload_class->handle_buffer = gst_rtp_vraw_pay_handle_buffer; @@ -101,6 +125,7 @@ G_DEFINE_TYPE (GstRtpVRawPay, gst_rtp_vraw_pay, GST_TYPE_RTP_BASE_PAYLOAD) static void gst_rtp_vraw_pay_init (GstRtpVRawPay * rtpvrawpay) { + rtpvrawpay->chunks_per_frame = DEFAULT_CHUNKS_PER_FRAME; } static gboolean @@ -111,7 +136,6 @@ gst_rtp_vraw_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) gint pgroup, xinc, yinc; const gchar *depthstr, *samplingstr, *colorimetrystr; gchar *wstr, *hstr; - gint depth; GstVideoInfo info; rtpvrawpay = GST_RTP_VRAW_PAY (payload); @@ -138,7 +162,6 @@ gst_rtp_vraw_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) /* these values are the only thing we can do */ depthstr = "8"; - depth = 8; switch (GST_VIDEO_INFO_FORMAT (&info)) { case GST_VIDEO_FORMAT_RGBA: @@ -180,7 +203,6 @@ gst_rtp_vraw_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) samplingstr = "YCbCr-4:2:2"; pgroup = 5; xinc = 2; - depth = 10; depthstr = "10"; break; default: @@ -195,7 +217,6 @@ gst_rtp_vraw_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) rtpvrawpay->pgroup = pgroup; rtpvrawpay->xinc = xinc; rtpvrawpay->yinc = yinc; - rtpvrawpay->depth = depth; GST_DEBUG_OBJECT (payload, "width %d, height %d, sampling %s", GST_VIDEO_INFO_WIDTH (&info), GST_VIDEO_INFO_HEIGHT (&info), samplingstr); @@ -239,15 +260,24 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) { GstRtpVRawPay *rtpvrawpay; GstFlowReturn ret = GST_FLOW_OK; + gfloat packets_per_packline; + guint pgroups_per_packet; + guint packlines_per_list, buffers_per_list; + guint lines_delay; /* after how many packed lines we push out a buffer list */ + guint last_line; /* last pack line number we pushed out a buffer list */ guint line, offset; guint8 *p0, *yp, *up, *vp; guint ystride, uvstride; + guint xinc, yinc; guint pgroup; guint mtu; guint width, height; - gint field; + gint field, fields; + GstVideoFormat format; GstVideoFrame frame; gint interlaced; + gboolean use_buffer_lists; + GstBufferList *list = NULL; GstRTPBuffer rtp = { NULL, }; rtpvrawpay = GST_RTP_VRAW_PAY (payload); @@ -275,17 +305,40 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) interlaced = GST_VIDEO_INFO_IS_INTERLACED (&rtpvrawpay->vinfo); + format = GST_VIDEO_INFO_FORMAT (&rtpvrawpay->vinfo); + + yinc = rtpvrawpay->yinc; + xinc = rtpvrawpay->xinc; + + /* after how many packed lines we push out a buffer list */ + lines_delay = GST_ROUND_UP_4 (height / rtpvrawpay->chunks_per_frame); + + /* calculate how many buffers we expect to store in a single buffer list */ + pgroups_per_packet = (mtu - (12 + 14)) / pgroup; + packets_per_packline = width / (xinc * pgroups_per_packet * 1.0); + packlines_per_list = height / (yinc * rtpvrawpay->chunks_per_frame); + buffers_per_list = packlines_per_list * packets_per_packline; + buffers_per_list = GST_ROUND_UP_8 (buffers_per_list); + + use_buffer_lists = (rtpvrawpay->chunks_per_frame < (height / yinc)); + + fields = 1 + interlaced; + /* start with line 0, offset 0 */ - for (field = 0; field < 1 + interlaced; field++) { + for (field = 0; field < fields; field++) { line = field; offset = 0; + last_line = 0; + + if (use_buffer_lists) + list = gst_buffer_list_new_sized (buffers_per_list); /* write all lines */ while (line < height) { - guint left; + guint left, pack_line; GstBuffer *out; guint8 *outdata, *headers; - gboolean next_line; + gboolean next_line, complete = FALSE; guint length, cont, pixels; /* get the max allowed payload length size, we try to fill the complete MTU */ @@ -345,7 +398,7 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) /* get how may bytes we need for the remaining pixels */ pixels = width - offset; - length = (pixels * pgroup) / rtpvrawpay->xinc; + length = (pixels * pgroup) / xinc; if (left >= length) { /* pixels and header fit completely, we will write them and skip to the @@ -353,8 +406,8 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) next_line = TRUE; } else { /* line does not fit completely, see how many pixels fit */ - pixels = (left / pgroup) * rtpvrawpay->xinc; - length = (pixels * pgroup) / rtpvrawpay->xinc; + pixels = (left / pgroup) * xinc; + length = (pixels * pgroup) / xinc; next_line = FALSE; } GST_LOG_OBJECT (rtpvrawpay, "filling %u bytes in %u pixels", length, @@ -371,7 +424,7 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) if (next_line) { /* go to next line we do this here to make the check below easier */ - line += rtpvrawpay->yinc; + line += yinc; } /* calculate continuation marker */ @@ -412,14 +465,14 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) "writing length %u, line %u, offset %u, cont %d", length, lin, offs, cont); - switch (GST_VIDEO_INFO_FORMAT (&rtpvrawpay->vinfo)) { + switch (format) { case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_BGR: case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_UYVY: case GST_VIDEO_FORMAT_UYVP: - offs /= rtpvrawpay->xinc; + offs /= xinc; memcpy (outdata, p0 + (lin * ystride) + (offs * pgroup), length); outdata += length; break; @@ -446,8 +499,7 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) yd1p = yp + (lin * ystride) + (offs); yd2p = yd1p + ystride; - uvoff = - (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc); + uvoff = (lin / yinc * uvstride) + (offs / xinc); udp = up + uvoff; vdp = vp + uvoff; @@ -468,8 +520,7 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) guint8 *ydp, *udp, *vdp; ydp = yp + (lin * ystride) + offs; - uvoff = - (lin / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->xinc); + uvoff = (lin / yinc * uvstride) + (offs / xinc); udp = up + uvoff; vdp = vp + uvoff; @@ -496,6 +547,7 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) if (line >= height) { GST_LOG_OBJECT (rtpvrawpay, "field/frame complete, set marker"); gst_rtp_buffer_set_marker (&rtp, TRUE); + complete = TRUE; } gst_rtp_buffer_unmap (&rtp); if (left > 0) { @@ -503,8 +555,26 @@ gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) gst_buffer_resize (out, 0, gst_buffer_get_size (out) - left); } - /* push buffer */ - ret = gst_rtp_base_payload_push (payload, out); + /* Now either push out the buffer directly */ + if (!use_buffer_lists) { + ret = gst_rtp_base_payload_push (payload, out); + continue; + } + + /* or add the buffer to buffer list ... */ + gst_buffer_list_add (list, out); + + /* .. and check if we need to push out the list */ + pack_line = (line - field) / fields; + if (complete || (pack_line > last_line && pack_line % lines_delay == 0)) { + GST_LOG_OBJECT (rtpvrawpay, "pushing list of %u buffers up to pack " + "line %u", gst_buffer_list_length (list), pack_line); + ret = gst_rtp_base_payload_push_list (payload, list); + list = NULL; + if (!complete) + list = gst_buffer_list_new_sized (buffers_per_list); + last_line = pack_line; + } } } @@ -533,6 +603,42 @@ too_small: } } +static void +gst_rtp_vraw_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpVRawPay *rtpvrawpay; + + rtpvrawpay = GST_RTP_VRAW_PAY (object); + + switch (prop_id) { + case PROP_CHUNKS_PER_FRAME: + rtpvrawpay->chunks_per_frame = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_vraw_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpVRawPay *rtpvrawpay; + + rtpvrawpay = GST_RTP_VRAW_PAY (object); + + switch (prop_id) { + case PROP_CHUNKS_PER_FRAME: + g_value_set_int (value, rtpvrawpay->chunks_per_frame); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + gboolean gst_rtp_vraw_pay_plugin_init (GstPlugin * plugin) { diff --git a/gst/rtp/gstrtpvrawpay.h b/gst/rtp/gstrtpvrawpay.h index 7cd86b8b..3fd2442d 100644 --- a/gst/rtp/gstrtpvrawpay.h +++ b/gst/rtp/gstrtpvrawpay.h @@ -48,11 +48,9 @@ struct _GstRtpVRawPay gint pgroup; gint xinc, yinc; -// guint yp, up, vp; -// gint ystride; -// gint uvstride; -// gboolean interlaced; - gint depth; + + /* properties */ + guint chunks_per_frame; }; struct _GstRtpVRawPayClass diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index ffb0dee1..caeed748 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -123,6 +123,7 @@ enum SIGNAL_ON_SDP, SIGNAL_SELECT_STREAM, SIGNAL_NEW_MANAGER, + SIGNAL_REQUEST_RTCP_KEY, LAST_SIGNAL }; @@ -163,6 +164,12 @@ gst_rtsp_src_buffer_mode_get_type (void) return buffer_mode_type; } +#define AES_128_KEY_LEN 16 +#define AES_256_KEY_LEN 32 + +#define HMAC_32_KEY_LEN 4 +#define HMAC_80_KEY_LEN 10 + #define DEFAULT_LOCATION NULL #define DEFAULT_PROTOCOLS GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP #define DEFAULT_DEBUG FALSE @@ -683,6 +690,21 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass) G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_CLEANUP, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); + /** + * GstRTSPSrc::request-rtcp-key: + * @rtspsrc: a #GstRTSPSrc + * @num: the stream number + * + * Signal emited to get the crypto parameters relevant to the RTCP + * stream. User should provide the key and the RTCP encryption ciphers + * and authentication, and return them wrapped in a GstCaps. + * + * Since: 1.4 + */ + gst_rtspsrc_signals[SIGNAL_REQUEST_RTCP_KEY] = + g_signal_new ("request-rtcp-key", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, GST_TYPE_CAPS, 1, G_TYPE_UINT); + gstelement_class->send_event = gst_rtspsrc_send_event; gstelement_class->provide_clock = gst_rtspsrc_provide_clock; gstelement_class->change_state = gst_rtspsrc_change_state; @@ -1537,8 +1559,8 @@ gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream) gst_object_unref (stream->srtpenc); if (stream->srtpdec) gst_object_unref (stream->srtpdec); - if (stream->key) - gst_buffer_unref (stream->key); + if (stream->srtcpparams) + gst_caps_unref (stream->srtcpparams); if (stream->rtcppad) gst_object_unref (stream->rtcppad); if (stream->session) @@ -1738,6 +1760,18 @@ parse_keymgmt (const gchar * keymgmt, GstCaps * caps) break; } break; + case GST_MIKEY_SP_SRTP_ENC_KEY_LEN: + switch (param->val[0]) { + case AES_128_KEY_LEN: + srtp_cipher = "aes-128-icm"; + break; + case AES_256_KEY_LEN: + srtp_cipher = "aes-256-icm"; + break; + default: + break; + } + break; case GST_MIKEY_SP_SRTP_AUTH_ALG: switch (param->val[0]) { case 0: @@ -1751,6 +1785,18 @@ parse_keymgmt (const gchar * keymgmt, GstCaps * caps) break; } break; + case GST_MIKEY_SP_SRTP_AUTH_KEY_LEN: + switch (param->val[0]) { + case HMAC_32_KEY_LEN: + srtp_auth = "hmac-sha1-32"; + break; + case HMAC_80_KEY_LEN: + srtp_auth = "hmac-sha1-80"; + break; + default: + break; + } + break; case GST_MIKEY_SP_SRTP_SRTP_ENC: break; case GST_MIKEY_SP_SRTP_SRTCP_ENC: @@ -3006,12 +3052,40 @@ request_rtcp_encoder (GstElement * rtpbin, guint session, return NULL; if (stream->srtpenc == NULL) { + GstStructure *s; + name = g_strdup_printf ("srtpenc_%u", session); stream->srtpenc = gst_element_factory_make ("srtpenc", name); g_free (name); - /* key has been made before */ - g_object_set (stream->srtpenc, "key", stream->key, NULL); + /* get RTCP crypto parameters from caps */ + s = gst_caps_get_structure (stream->srtcpparams, 0); + if (s) { + GstBuffer *buf; + const gchar *str; + GType ciphertype, authtype; + GValue rtcp_cipher = G_VALUE_INIT, rtcp_auth = G_VALUE_INIT; + + ciphertype = g_type_from_name ("GstSrtpCipherType"); + authtype = g_type_from_name ("GstSrtpAuthType"); + g_value_init (&rtcp_cipher, ciphertype); + g_value_init (&rtcp_auth, authtype); + + str = gst_structure_get_string (s, "srtcp-cipher"); + gst_value_deserialize (&rtcp_cipher, str); + str = gst_structure_get_string (s, "srtcp-auth"); + gst_value_deserialize (&rtcp_auth, str); + gst_structure_get (s, "srtp-key", GST_TYPE_BUFFER, &buf, NULL); + + g_object_set_property (G_OBJECT (stream->srtpenc), "rtcp-cipher", + &rtcp_cipher); + g_object_set_property (G_OBJECT (stream->srtpenc), "rtcp-auth", + &rtcp_auth); + g_object_set (stream->srtpenc, "key", buf, NULL); + + g_value_unset (&rtcp_cipher); + g_value_unset (&rtcp_auth); + } } name = g_strdup_printf ("rtcp_sink_%d", session); pad = gst_element_get_request_pad (stream->srtpenc, name); @@ -5812,27 +5886,102 @@ failed: } } +static guint8 +enc_key_length_from_cipher_name (const gchar * cipher) +{ + if (g_strcmp0 (cipher, "aes-128-icm") == 0) + return AES_128_KEY_LEN; + else if (g_strcmp0 (cipher, "aes-256-icm") == 0) + return AES_256_KEY_LEN; + else { + GST_ERROR ("encryption algorithm '%s' not supported", cipher); + return 0; + } +} + +static guint8 +auth_key_length_from_auth_name (const gchar * auth) +{ + if (g_strcmp0 (auth, "hmac-sha1-32") == 0) + return HMAC_32_KEY_LEN; + else if (g_strcmp0 (auth, "hmac-sha1-80") == 0) + return HMAC_80_KEY_LEN; + else { + GST_ERROR ("authentication algorithm '%s' not supported", auth); + return 0; + } +} + +static GstCaps * +signal_get_srtcp_params (GstRTSPSrc * src, GstRTSPStream * stream) +{ + GstCaps *caps = NULL; + + g_signal_emit (src, gst_rtspsrc_signals[SIGNAL_REQUEST_RTCP_KEY], 0, + stream->id, &caps); + + if (caps != NULL) + GST_DEBUG_OBJECT (src, "SRTP parameters received"); + + return caps; +} + +static GstCaps * +default_srtcp_params (void) +{ + guint i; + GstCaps *caps; + GstBuffer *buf; + guint8 *key_data; +#define KEY_SIZE 30 + + /* create a random key */ + key_data = g_malloc (KEY_SIZE); + for (i = 0; i < KEY_SIZE; i += 4) + GST_WRITE_UINT32_BE (key_data + i, g_random_int ()); + + buf = gst_buffer_new_wrapped (key_data, KEY_SIZE); + + caps = gst_caps_new_simple ("application/x-srtp", + "srtp-key", GST_TYPE_BUFFER, buf, + "srtcp-cipher", G_TYPE_STRING, "aes-128-icm", + "srtcp-auth", G_TYPE_STRING, "hmac-sha1-80", NULL); + + return caps; +} + static gchar * gst_rtspsrc_stream_make_keymgmt (GstRTSPSrc * src, GstRTSPStream * stream) { GBytes *bytes; gchar *result, *base64; - guint8 *key_data; const guint8 *data; gsize size; - guint i; GstMIKEYMessage *msg; GstMIKEYPayload *payload, *pkd; guint8 byte; -#define KEY_SIZE 30 + GstStructure *s; + GstMapInfo info; + GstBuffer *srtpkey; + const GValue *val; + const gchar *srtcpcipher, *srtcpauth; - key_data = g_malloc (KEY_SIZE); - for (i = 0; i < KEY_SIZE; i += 4) - GST_WRITE_UINT32_BE (key_data + i, g_random_int ()); + stream->srtcpparams = signal_get_srtcp_params (src, stream); + if (stream->srtcpparams == NULL) + stream->srtcpparams = default_srtcp_params (); + + s = gst_caps_get_structure (stream->srtcpparams, 0); + + srtcpcipher = gst_structure_get_string (s, "srtcp-cipher"); + srtcpauth = gst_structure_get_string (s, "srtcp-auth"); + val = gst_structure_get_value (s, "srtp-key"); - if (stream->key) - gst_buffer_unref (stream->key); - stream->key = gst_buffer_new_wrapped (key_data, KEY_SIZE); + if (srtcpcipher == NULL || srtcpauth == NULL || val == NULL) { + GST_ERROR_OBJECT (src, "could not find the right SRTP parameters in caps"); + return NULL; + } + + srtpkey = gst_value_get_buffer (val); msg = gst_mikey_message_new (); /* unencrypted MIKEY message, we send this over TLS so this is allowed */ @@ -5852,9 +6001,17 @@ gst_rtspsrc_stream_make_keymgmt (GstRTSPSrc * src, GstRTSPStream * stream) /* only AES-CM is supported */ byte = 1; gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_ENC_ALG, 1, &byte); + /* encryption key length */ + byte = enc_key_length_from_cipher_name (srtcpcipher); + gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_ENC_KEY_LEN, 1, + &byte); /* only HMAC-SHA1 */ gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_AUTH_ALG, 1, &byte); + /* authentication key length */ + byte = auth_key_length_from_auth_name (srtcpauth); + gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_AUTH_KEY_LEN, 1, + &byte); /* we enable encryption on RTP and RTCP */ gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTP_ENC, 1, &byte); @@ -5870,8 +6027,10 @@ gst_rtspsrc_stream_make_keymgmt (GstRTSPSrc * src, GstRTSPStream * stream) gst_mikey_payload_kemac_set (payload, GST_MIKEY_ENC_NULL, GST_MIKEY_MAC_NULL); /* add the key in KEMAC */ pkd = gst_mikey_payload_new (GST_MIKEY_PT_KEY_DATA); - gst_mikey_payload_key_data_set_key (pkd, GST_MIKEY_KD_TEK, KEY_SIZE, - key_data); + gst_buffer_map (srtpkey, &info, GST_MAP_READ); + gst_mikey_payload_key_data_set_key (pkd, GST_MIKEY_KD_TEK, info.size, + info.data); + gst_buffer_unmap (srtpkey, &info); gst_mikey_payload_kemac_add_sub (payload, pkd); gst_mikey_message_add_payload (msg, payload); diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index f38f6fce..e90d0599 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -137,7 +137,7 @@ struct _GstRTSPStream { guint32 seqbase; guint64 timebase; GstElement *srtpdec; - GstBuffer *key; + GstCaps *srtcpparams; GstElement *srtpenc; guint32 send_ssrc; diff --git a/gst/udp/gstdynudpsink.c b/gst/udp/gstdynudpsink.c index 328e9020..9c2b6bbd 100644 --- a/gst/udp/gstdynudpsink.c +++ b/gst/udp/gstdynudpsink.c @@ -21,10 +21,6 @@ * Boston, MA 02110-1301, USA. */ -/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray - * with newer GLib versions (>= 2.31.0) */ -#define GLIB_DISABLE_DEPRECATION_WARNINGS - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -139,7 +135,8 @@ gst_dynudpsink_class_init (GstDynUDPSinkClass * klass) gst_element_class_set_static_metadata (gstelement_class, "UDP packet sender", "Sink/Network", - "Send data over the network via UDP", + "Send data over the network via UDP with packet destinations picked up " + "dynamically from meta on the buffers passed", "Philippe Khalaf <burger@speedy.org>"); gstbasesink_class->render = gst_dynudpsink_render; @@ -267,9 +264,18 @@ gst_dynudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) send_error: { - GST_DEBUG ("got send error %s", err->message); + GstFlowReturn flow_ret; + + if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + GST_DEBUG_OBJECT (sink, "send cancelled"); + flow_ret = GST_FLOW_FLUSHING; + } else { + GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), + ("send error: %s", err->message)); + flow_ret = GST_FLOW_ERROR; + } g_clear_error (&err); - return GST_FLOW_ERROR; + return flow_ret; } invalid_family: { diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c index 988de2de..d3af62e7 100644 --- a/gst/udp/gstmultiudpsink.c +++ b/gst/udp/gstmultiudpsink.c @@ -29,10 +29,6 @@ * It can be combined with rtp payload encoders to implement RTP streaming. */ -/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray - * with newer GLib versions (>= 2.31.0) */ -#define GLIB_DISABLE_DEPRECATION_WARNINGS - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -356,7 +352,8 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) gst_element_class_set_static_metadata (gstelement_class, "UDP packet sender", "Sink/Network", - "Send data over the network via UDP", + "Send data over the network via UDP to one or multiple recipients " + "which can be added or removed at runtime using action signals", "Wim Taymans <wim.taymans@gmail.com>"); gstbasesink_class->render = gst_multiudpsink_render; @@ -529,7 +526,7 @@ gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) gint num, no_clients; GError *err = NULL; - sink = GST_MULTIUDPSINK (bsink); + sink = GST_MULTIUDPSINK_CAST (bsink); n_mem = gst_buffer_n_memory (buffer); if (n_mem == 0) @@ -542,7 +539,7 @@ gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) size = 0; for (i = 0; i < n_mem; i++) { - mem = gst_buffer_get_memory (buffer, i); + mem = gst_buffer_peek_memory (buffer, i); gst_memory_map (mem, &map[i], GST_MAP_READ); vec[i].buffer = map[i].data; @@ -616,10 +613,8 @@ gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) g_mutex_unlock (&sink->client_lock); /* unmap all memory again */ - for (i = 0; i < n_mem; i++) { + for (i = 0; i < n_mem; i++) gst_memory_unmap (map[i].memory, &map[i]); - gst_memory_unref (map[i].memory); - } GST_LOG_OBJECT (sink, "sent %" G_GSIZE_FORMAT " bytes to %d (of %d) clients", size, num, no_clients); @@ -637,10 +632,8 @@ flushing: g_clear_error (&err); /* unmap all memory */ - for (i = 0; i < n_mem; i++) { + for (i = 0; i < n_mem; i++) gst_memory_unmap (map[i].memory, &map[i]); - gst_memory_unref (map[i].memory); - } return GST_FLOW_FLUSHING; } @@ -1330,9 +1323,8 @@ gst_multiudpsink_remove (GstMultiUDPSink * sink, const gchar * host, gint port) client->refcount--; if (client->refcount == 0) { GInetSocketAddress *saddr = G_INET_SOCKET_ADDRESS (client->addr); + GSocketFamily family = g_socket_address_get_family (client->addr); GInetAddress *addr = g_inet_socket_address_get_address (saddr); - GSocketFamily family = - g_socket_address_get_family (G_SOCKET_ADDRESS (saddr)); GSocket *socket; /* Select socket to send from for this address */ diff --git a/gst/udp/gstmultiudpsink.h b/gst/udp/gstmultiudpsink.h index a1be566b..9b126bb6 100644 --- a/gst/udp/gstmultiudpsink.h +++ b/gst/udp/gstmultiudpsink.h @@ -33,6 +33,7 @@ G_BEGIN_DECLS #define GST_MULTIUDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MULTIUDPSINK,GstMultiUDPSinkClass)) #define GST_IS_MULTIUDPSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MULTIUDPSINK)) #define GST_IS_MULTIUDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MULTIUDPSINK)) +#define GST_MULTIUDPSINK_CAST(obj) ((GstMultiUDPSink*)(obj)) typedef struct _GstMultiUDPSink GstMultiUDPSink; typedef struct _GstMultiUDPSinkClass GstMultiUDPSinkClass; diff --git a/gst/videobox/gstvideobox.c b/gst/videobox/gstvideobox.c index 5df25883..861bcaea 100644 --- a/gst/videobox/gstvideobox.c +++ b/gst/videobox/gstvideobox.c @@ -2862,22 +2862,27 @@ gst_video_box_transform_caps (GstBaseTransform * trans, for (j = 0; j < gst_value_list_get_size (fval); j++) { lval = gst_value_list_get_value (fval, j); if ((str = g_value_get_string (lval))) { - if (strstr (str, "RGB") || strstr (str, "BGR") || - strcmp (str, "AYUV") == 0) + if (strcmp (str, "AYUV") == 0) { + seen_yuv = TRUE; + seen_rgb = TRUE; + break; + } else if (strstr (str, "RGB") || strstr (str, "BGR")) { seen_rgb = TRUE; - else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0 || - strcmp (str, "AYUV") == 0) + } else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0) { seen_yuv = TRUE; + } } } } else if (fval && G_VALUE_HOLDS_STRING (fval)) { if ((str = g_value_get_string (fval))) { - if (strstr (str, "RGB") || strstr (str, "BGR") || - strcmp (str, "AYUV") == 0) + if (strcmp (str, "AYUV") == 0) { + seen_yuv = TRUE; seen_rgb = TRUE; - else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0 || - strcmp (str, "AYUV") == 0) + } else if (strstr (str, "RGB") || strstr (str, "BGR")) { + seen_rgb = TRUE; + } else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0) { seen_yuv = TRUE; + } } } diff --git a/gst/wavparse/gstwavparse.c b/gst/wavparse/gstwavparse.c index e50a82d9..7e5f043b 100644 --- a/gst/wavparse/gstwavparse.c +++ b/gst/wavparse/gstwavparse.c @@ -1037,6 +1037,7 @@ gst_wavparse_stream_headers (GstWavParse * wav) gchar *codec_name = NULL; GstEvent **event_p; gint64 upstream_size = 0; + GstStructure *s; /* search for "_fmt" chunk, which should be first */ while (!wav->got_fmt) { @@ -1107,6 +1108,15 @@ gst_wavparse_stream_headers (GstWavParse * wav) if (!caps) goto unknown_format; + /* If we got raw audio from upstream, we remove the codec_data field, + * which may have been added if the wav header included an extended + * chunk. We want to keep it for non raw audio. + */ + s = gst_caps_get_structure (caps, 0); + if (s && gst_structure_has_name (s, "audio/x-raw")) { + gst_structure_remove_field (s, "codec_data"); + } + /* do more sanity checks of header fields * (these can be sanitized by gst_riff_create_audio_caps() */ |