From b0533d1ea0c6ae0014329cdedee938bfb2e4edc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Aug 2022 18:37:18 +0300 Subject: [PATCH] qtdemux: Add reference timestamp meta with UTC times based on the ONVIF Export File Format CorrectStartTime box to outgoing buffers Part-of: --- .../gst-plugins-good/gst/isomp4/fourcc.h | 3 + .../gst-plugins-good/gst/isomp4/qtdemux.c | 105 ++++++++++++++++++ .../gst-plugins-good/gst/isomp4/qtdemux.h | 4 + .../gst/isomp4/qtdemux_types.c | 1 + 4 files changed, 113 insertions(+) diff --git a/subprojects/gst-plugins-good/gst/isomp4/fourcc.h b/subprojects/gst-plugins-good/gst/isomp4/fourcc.h index 32fa4c8db5..8e15bf76af 100644 --- a/subprojects/gst-plugins-good/gst/isomp4/fourcc.h +++ b/subprojects/gst-plugins-good/gst/isomp4/fourcc.h @@ -420,6 +420,9 @@ G_BEGIN_DECLS #define FOURCC_metx GST_MAKE_FOURCC('m','e','t','x') +/* ONVIF Export File Format */ +#define FOURCC_cstb GST_MAKE_FOURCC('c','s','t','b') + G_END_DECLS #endif /* __FOURCC_H__ */ diff --git a/subprojects/gst-plugins-good/gst/isomp4/qtdemux.c b/subprojects/gst-plugins-good/gst/isomp4/qtdemux.c index 43d2c2536f..4c17f08731 100644 --- a/subprojects/gst-plugins-good/gst/isomp4/qtdemux.c +++ b/subprojects/gst-plugins-good/gst/isomp4/qtdemux.c @@ -2067,6 +2067,7 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) gst_caps_replace (&qtdemux->media_caps, NULL); qtdemux->timescale = 0; qtdemux->got_moov = FALSE; + qtdemux->start_utc_time = GST_CLOCK_TIME_NONE; qtdemux->cenc_aux_info_offset = 0; qtdemux->cenc_aux_info_sizes = NULL; qtdemux->cenc_aux_sample_count = 0; @@ -3054,6 +3055,57 @@ qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length) gst_isoff_qt_sidx_parser_clear (&sidx_parser); } +static void +qtdemux_parse_cstb (GstQTDemux * qtdemux, GstByteReader * data) +{ + guint64 start_time; + guint32 entry_count; + + GST_DEBUG_OBJECT (qtdemux, "Parsing CorrectStartTime box"); + + qtdemux->start_utc_time = GST_CLOCK_TIME_NONE; + + if (gst_byte_reader_get_remaining (data) < 4) { + GST_WARNING_OBJECT (qtdemux, "Too small CorrectStartTime box"); + return; + } + + entry_count = gst_byte_reader_get_uint32_be_unchecked (data); + if (entry_count == 0) + return; + + /* XXX: We assume that all start times are the same as different start times + * would violate the MP4 synchronization model, so we just take the first + * one here and apply it to all tracks. + */ + + if (gst_byte_reader_get_remaining (data) < entry_count * 12) { + GST_WARNING_OBJECT (qtdemux, "Too small CorrectStartTime box"); + return; + } + + /* Skip track id */ + gst_byte_reader_skip_unchecked (data, 4); + + /* In 100ns intervals */ + start_time = gst_byte_reader_get_uint64_be_unchecked (data); + + /* Convert from Jan 1 1601 to Jan 1 1970 */ + if (start_time < 11644473600 * G_GUINT64_CONSTANT (10000000)) { + GST_WARNING_OBJECT (qtdemux, "Start UTC time before UNIX epoch"); + return; + } + start_time -= 11644473600 * G_GUINT64_CONSTANT (10000000); + + /* Convert to GstClockTime */ + start_time *= 100; + + GST_DEBUG_OBJECT (qtdemux, "Start UTC time: %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time)); + + qtdemux->start_utc_time = start_time; +} + /* caller verifies at least 8 bytes in buf */ static void extract_initial_length_and_fourcc (const guint8 * data, guint size, @@ -4701,6 +4753,34 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) gst_buffer_unref (sidx); break; } + case FOURCC_meta: + { + GstBuffer *meta = NULL; + GNode *node, *child; + GstByteReader child_data; + ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &meta); + if (ret != GST_FLOW_OK) + goto beach; + qtdemux->offset += length; + gst_buffer_map (meta, &map, GST_MAP_READ); + + node = g_node_new (map.data); + + qtdemux_parse_node (qtdemux, node, map.data, map.size); + + /* Parse ONVIF Export File Format CorrectStartTime box if available */ + if ((child = + qtdemux_tree_get_child_by_type_full (node, FOURCC_cstb, + &child_data))) { + qtdemux_parse_cstb (qtdemux, &child_data); + } + + g_node_destroy (node); + + gst_buffer_unmap (meta, &map); + gst_buffer_unref (meta); + break; + } default: { GstBuffer *unknown = NULL; @@ -6210,6 +6290,15 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, /* we're going to modify the metadata */ buf = gst_buffer_make_writable (buf); + if (qtdemux->start_utc_time != GST_CLOCK_TIME_NONE) { + static GstStaticCaps unix_caps = GST_STATIC_CAPS ("timestamp/x-unix"); + GstCaps *caps = gst_static_caps_get (&unix_caps); + gst_buffer_add_reference_timestamp_meta (buf, caps, + pts + qtdemux->start_utc_time - stream->cslg_shift, + GST_CLOCK_TIME_NONE); + gst_caps_unref (caps); + } + GST_BUFFER_DTS (buf) = dts; GST_BUFFER_PTS (buf) = pts; GST_BUFFER_DURATION (buf) = duration; @@ -7353,6 +7442,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) if (demux->moov_node) g_node_destroy (demux->moov_node); demux->moov_node = NULL; + demux->start_utc_time = GST_CLOCK_TIME_NONE; } demux->last_moov_offset = demux->offset; @@ -7475,6 +7565,21 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) } else if (fourcc == FOURCC_sidx) { GST_DEBUG_OBJECT (demux, "Parsing [sidx]"); qtdemux_parse_sidx (demux, data, demux->neededbytes); + } else if (fourcc == FOURCC_meta) { + GNode *node, *child; + GstByteReader child_data; + + node = g_node_new ((gpointer) data); + qtdemux_parse_node (demux, node, data, demux->neededbytes); + + /* Parse ONVIF Export File Format CorrectStartTime box if available */ + if ((child = + qtdemux_tree_get_child_by_type_full (node, FOURCC_cstb, + &child_data))) { + qtdemux_parse_cstb (demux, &child_data); + } + + g_node_destroy (node); } else { switch (fourcc) { case FOURCC_styp: diff --git a/subprojects/gst-plugins-good/gst/isomp4/qtdemux.h b/subprojects/gst-plugins-good/gst/isomp4/qtdemux.h index c03c814b17..ad150727c0 100644 --- a/subprojects/gst-plugins-good/gst/isomp4/qtdemux.h +++ b/subprojects/gst-plugins-good/gst/isomp4/qtdemux.h @@ -119,6 +119,10 @@ struct _GstQTDemux { /* Global duration (in global timescale). Use QTTIME macros to get GstClockTime */ guint64 duration; + /* Start UTC time as extracted from the AFIdentification box, reset on every + * moov */ + GstClockTime start_utc_time; + /* Total size of header atoms. Used to calculate fallback overall bitrate */ guint header_size; diff --git a/subprojects/gst-plugins-good/gst/isomp4/qtdemux_types.c b/subprojects/gst-plugins-good/gst/isomp4/qtdemux_types.c index df9c99a149..f9bb7c3143 100644 --- a/subprojects/gst-plugins-good/gst/isomp4/qtdemux_types.c +++ b/subprojects/gst-plugins-good/gst/isomp4/qtdemux_types.c @@ -232,6 +232,7 @@ static const QtNodeType qt_node_types[] = { {FOURCC_adrm, "AAX DRM key data", 0}, {FOURCC_vttc, "VTTCueBox 14496-30", QT_FLAG_CONTAINER}, {FOURCC_metx, "XML MetaData Sample Entry", 0}, + {FOURCC_cstb, "Correct Start Time Box", 0}, {0, "unknown", 0,}, };