diff options
Diffstat (limited to 'gst/isomp4/gstqtmux.c')
-rw-r--r-- | gst/isomp4/gstqtmux.c | 149 |
1 files changed, 125 insertions, 24 deletions
diff --git a/gst/isomp4/gstqtmux.c b/gst/isomp4/gstqtmux.c index 530c611..5e9fcdd 100644 --- a/gst/isomp4/gstqtmux.c +++ b/gst/isomp4/gstqtmux.c @@ -262,17 +262,20 @@ gst_qt_mux_base_init (gpointer g_class) srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, params->src_caps); gst_element_class_add_pad_template (element_class, srctempl); + gst_object_unref (srctempl); if (params->audio_sink_caps) { audiosinktempl = gst_pad_template_new ("audio_%d", GST_PAD_SINK, GST_PAD_REQUEST, params->audio_sink_caps); gst_element_class_add_pad_template (element_class, audiosinktempl); + gst_object_unref (audiosinktempl); } if (params->video_sink_caps) { videosinktempl = gst_pad_template_new ("video_%d", GST_PAD_SINK, GST_PAD_REQUEST, params->video_sink_caps); gst_element_class_add_pad_template (element_class, videosinktempl); + gst_object_unref (videosinktempl); } klass->format = params->prop->format; @@ -367,6 +370,8 @@ gst_qt_mux_pad_reset (GstQTPad * qtpad) qtpad->avg_bitrate = 0; qtpad->max_bitrate = 0; qtpad->ts_n_entries = 0; + qtpad->total_duration = 0; + qtpad->total_bytes = 0; qtpad->buf_head = 0; qtpad->buf_tail = 0; @@ -570,9 +575,11 @@ gst_qt_mux_add_mp4_tag (GstQTMux * qtmux, const GstTagList * list, if (tag2) { /* paired unsigned integers */ guint count = 0; + gboolean got_tag; - if (!(gst_tag_list_get_uint (list, tag, &value) || - gst_tag_list_get_uint (list, tag2, &count))) + got_tag = gst_tag_list_get_uint (list, tag, &value); + got_tag = gst_tag_list_get_uint (list, tag2, &count) || got_tag; + if (!got_tag) break; GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u/%u", GST_FOURCC_ARGS (fourcc), value, count); @@ -613,6 +620,8 @@ gst_qt_mux_add_mp4_date (GstQTMux * qtmux, const GstTagList * list, month = g_date_get_month (date); day = g_date_get_day (date); + g_date_free (date); + if (year == G_DATE_BAD_YEAR && month == G_DATE_BAD_MONTH && day == G_DATE_BAD_DAY) { GST_WARNING_OBJECT (qtmux, "invalid date in tag"); @@ -991,6 +1000,7 @@ static const GstTagToFourcc tag_matches_mp4[] = { {FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT, gst_qt_mux_add_mp4_tag}, {FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, gst_qt_mux_add_mp4_cover}, + {FOURCC_covr, GST_TAG_IMAGE, NULL, gst_qt_mux_add_mp4_cover}, {0, NULL,} }; @@ -1466,7 +1476,7 @@ gst_qt_mux_set_header_on_caps (GstQTMux * mux, GstBuffer * buf) GstStructure *structure; GValue array = { 0 }; GValue value = { 0 }; - GstCaps *caps = GST_PAD_CAPS (mux->srcpad); + GstCaps *caps; caps = gst_caps_copy (GST_PAD_CAPS (mux->srcpad)); structure = gst_caps_get_structure (caps, 0); @@ -1735,15 +1745,25 @@ gst_qt_mux_stop_file (GstQTMux * qtmux) GstCollectData *cdata = (GstCollectData *) walk->data; GstQTPad *qtpad = (GstQTPad *) cdata; - /* send last buffer */ + /* avoid add_buffer complaining if not negotiated + * in which case no buffers either, so skipping */ + if (!qtpad->fourcc) { + GST_DEBUG_OBJECT (qtmux, "Pad %s has never had buffers", + GST_PAD_NAME (qtpad->collect.pad)); + continue; + } + + /* send last buffer; also flushes possibly queued buffers/ts */ GST_DEBUG_OBJECT (qtmux, "Sending the last buffer for pad %s", GST_PAD_NAME (qtpad->collect.pad)); ret = gst_qt_mux_add_buffer (qtmux, qtpad, NULL); - if (ret != GST_FLOW_OK) + if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (qtmux, "Failed to send last buffer for %s, " "flow return: %s", GST_PAD_NAME (qtpad->collect.pad), gst_flow_get_name (ret)); + } + /* having flushed above, can check for buffers now */ if (!GST_CLOCK_TIME_IS_VALID (qtpad->first_ts)) { GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers", GST_PAD_NAME (qtpad->collect.pad)); @@ -1756,6 +1776,20 @@ gst_qt_mux_stop_file (GstQTMux * qtmux) qtpad->last_dts > first_ts)) { first_ts = qtpad->last_dts; } + + /* update average bitrate of streams if needed */ + { + guint32 avgbitrate = 0; + guint32 maxbitrate = qtpad->max_bitrate; + + if (qtpad->avg_bitrate) + avgbitrate = qtpad->avg_bitrate; + else if (qtpad->total_duration > 0) + avgbitrate = (guint32) gst_util_uint64_scale_round (qtpad->total_bytes, + 8 * GST_SECOND, qtpad->total_duration); + + atom_trak_update_bitrates (qtpad->trak, avgbitrate, maxbitrate); + } } if (qtmux->fragment_sequence) { @@ -2026,6 +2060,39 @@ gst_qt_mux_push_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts) pad->ts_n_entries++; } +static void +check_and_subtract_ts (GstQTMux * qtmux, GstClockTime * ts_a, GstClockTime ts_b) +{ + if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (*ts_a))) { + if (G_LIKELY (*ts_a > ts_b)) { + *ts_a -= ts_b; + } else { + *ts_a = 0; + GST_WARNING_OBJECT (qtmux, "Subtraction would result in negative value, " + "using 0 as result"); + } + } +} + +/* subtract ts from all buffers enqueued on the pad */ +static void +gst_qt_mux_subtract_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts) +{ + gint i; + + for (i = 0; (i < QTMUX_NO_OF_TS) && (i < pad->ts_n_entries); i++) { + check_and_subtract_ts (qtmux, &pad->ts_entries[i], ts); + } + for (i = 0; i < G_N_ELEMENTS (pad->buf_entries); i++) { + if (pad->buf_entries[i]) { + check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (pad->buf_entries[i]), + ts); + check_and_subtract_ts (qtmux, + &GST_BUFFER_OFFSET_END (pad->buf_entries[i]), ts); + } + } +} + /* takes ownership of @buf */ static GstBuffer * gst_qt_mux_get_asc_buffer_ts (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) @@ -2082,9 +2149,17 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) buf = pad->prepare_buf_func (pad, buf, qtmux); } + if (G_LIKELY (buf != NULL && GST_CLOCK_TIME_IS_VALID (pad->first_ts) && + pad->first_ts != 0)) { + buf = gst_buffer_make_metadata_writable (buf); + check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts); + } + /* when we obtain the first_ts we subtract from all stored buffers we have, + * after that we can subtract on input */ + again: last_buf = pad->last_buf; - if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER)) { + if (qtmux->dts_method == DTS_METHOD_REORDER) { buf = gst_qt_mux_get_asc_buffer_ts (qtmux, pad, buf); if (!buf && !last_buf) { GST_DEBUG_OBJECT (qtmux, "no reordered buffer"); @@ -2155,6 +2230,31 @@ again: goto no_order; } + /* if this is the first buffer, store the timestamp */ + if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) { + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) { + pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf); + } else { + GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, " + "using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad)); + pad->first_ts = 0; + } + GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %" + GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad), + GST_TIME_ARGS (pad->first_ts)); + + gst_qt_mux_subtract_ts (qtmux, pad, pad->first_ts); + + GST_BUFFER_TIMESTAMP (last_buf) = 0; + check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (last_buf), + pad->first_ts); + if (buf) { + check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts); + check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (buf), + pad->first_ts); + } + } + /* fall back to duration if last buffer or * out-of-order (determined previously), otherwise use input ts */ if (buf == NULL || @@ -2212,6 +2312,12 @@ again: duration = MAX (duration, ts); } + /* for computing the avg bitrate */ + if (G_LIKELY (last_buf)) { + pad->total_bytes += GST_BUFFER_SIZE (last_buf); + pad->total_duration += duration; + } + gst_buffer_replace (&pad->last_buf, buf); last_dts = gst_util_uint64_scale_round (pad->last_dts, @@ -2314,20 +2420,6 @@ again: qtmux->longest_chunk = duration; } - /* if this is the first buffer, store the timestamp */ - if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) { - if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) { - pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf); - } else { - GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, " - "using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad)); - pad->first_ts = 0; - } - GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %" - GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad), - GST_TIME_ARGS (pad->first_ts)); - } - /* now we go and register this buffer/sample all over */ /* note that a new chunk is started each time (not fancy but works) */ if (qtmux->moov_recov_file) { @@ -2527,7 +2619,6 @@ gst_qt_mux_audio_sink_set_caps (GstPad * pad, GstCaps * caps) AtomInfo *ext_atom = NULL; gint constant_size = 0; const gchar *stream_format; - GstCaps *current_caps = NULL; /* find stream data */ qtpad = (GstQTPad *) gst_pad_get_element_private (pad); @@ -2539,10 +2630,14 @@ gst_qt_mux_audio_sink_set_caps (GstPad * pad, GstCaps * caps) * the old caps are a subset of the new one (this means upstream * added more info to the caps, as both should be 'fixed' caps) */ if (qtpad->fourcc) { + GstCaps *current_caps = NULL; + gboolean is_subset; g_object_get (pad, "caps", ¤t_caps, NULL); g_assert (caps != NULL); - if (!gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps)) { + is_subset = gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps); + gst_caps_unref (current_caps); + if (!is_subset) { goto refuse_renegotiation; } GST_DEBUG_OBJECT (qtmux, @@ -2849,7 +2944,6 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps) GList *ext_atom_list = NULL; gboolean sync = FALSE; int par_num, par_den; - GstCaps *current_caps = NULL; /* find stream data */ qtpad = (GstQTPad *) gst_pad_get_element_private (pad); @@ -2861,10 +2955,14 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps) * the old caps are a subset of the new one (this means upstream * added more info to the caps, as both should be 'fixed' caps) */ if (qtpad->fourcc) { + GstCaps *current_caps = NULL; + gboolean is_subset; g_object_get (pad, "caps", ¤t_caps, NULL); g_assert (caps != NULL); - if (!gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps)) { + is_subset = gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps); + gst_caps_unref (current_caps); + if (!is_subset) { goto refuse_renegotiation; } GST_DEBUG_OBJECT (qtmux, @@ -3117,6 +3215,9 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps) } else if (strcmp (mimetype, "video/x-vp8") == 0) { entry.fourcc = FOURCC_VP80; sync = FALSE; + } else if (strcmp (mimetype, "video/x-dirac") == 0) { + entry.fourcc = FOURCC_drac; + qtpad->have_dts = TRUE; } else if (strcmp (mimetype, "video/x-qt-part") == 0) { guint32 fourcc; |