gint64 min_byte_offset = -1;
guint i;
- min_offset = desired_time;
+ min_offset = next ? G_MAXUINT64 : desired_time;
/* for each stream, find the index of the sample in the segment
* and move back to the previous keyframe. */
index++;
if (!empty_segment) {
- /* find previous keyframe */
+ /* find previous or next keyframe */
kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
- /* we will settle for one before if none found after */
+ /* if looking for next one, we will settle for one before if none found after */
if (next && kindex == -1)
kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
/* this keyframe is inside the segment, convert back to
* segment time */
seg_time = (media_time - seg->media_start) + seg->time;
- if ((!next && (seg_time < min_offset)) ||
- (next && (seg_time > min_offset)))
+
+ /* Adjust the offset based on the earliest suitable keyframe found,
+ * based on which GST_SEEK_FLAG_SNAP_* is present (indicated by 'next').
+ * For SNAP_BEFORE we look for the earliest keyframe before desired_time,
+ * and in case of SNAP_AFTER - for the closest one after it. */
+ if (seg_time < min_offset)
min_offset = seg_time;
}
}
structure = gst_caps_get_structure (caps, 0);
variant = gst_structure_get_string (structure, "variant");
+ if (variant && strcmp (variant, "mse-bytestream") == 0) {
+ demux->variant = VARIANT_MSE_BYTESTREAM;
+ }
+
if (variant && strcmp (variant, "mss-fragmented") == 0) {
QtDemuxStream *stream;
const GValue *value;
demux->fragmented = TRUE;
- demux->mss_mode = TRUE;
+ demux->variant = VARIANT_MSS_FRAGMENTED;
if (QTDEMUX_N_STREAMS (demux) > 1) {
/* can't do this, we can only renegotiate for another mss format */
}
}
gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
- } else {
- demux->mss_mode = FALSE;
}
return TRUE;
qtdemux->n_meta_streams = 0;
qtdemux->exposed = FALSE;
qtdemux->fragmented = FALSE;
- qtdemux->mss_mode = FALSE;
+ qtdemux->variant = VARIANT_NONE;
gst_caps_replace (&qtdemux->media_caps, NULL);
qtdemux->timescale = 0;
qtdemux->got_moov = FALSE;
g_free (qtdemux->preferred_protection_system_id);
qtdemux->preferred_protection_system_id = NULL;
}
- } else if (qtdemux->mss_mode) {
+ } else if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
gst_flow_combiner_reset (qtdemux->flowcombiner);
g_ptr_array_foreach (qtdemux->active_streams,
(GFunc) gst_qtdemux_stream_clear, NULL);
/* caller verifies at least 8 bytes in buf */
static void
-extract_initial_length_and_fourcc (const guint8 * data, guint size,
+extract_initial_length_and_fourcc (const guint8 * data, gsize size,
guint64 * plength, guint32 * pfourcc)
{
guint64 length;
if (stream->track_id == id)
return stream;
}
- if (qtdemux->mss_mode) {
+ if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
/* mss should have only 1 stream anyway */
return QTDEMUX_NTH_STREAM (qtdemux, 0);
}
(*stream)->stsd_sample_description_id = sample_description_index - 1;
}
- if (qtdemux->mss_mode) {
+ if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
/* mss has no stsd entry */
(*stream)->stsd_sample_description_id = 0;
}
pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
}
- if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
+ if (!qtdemux->upstream_format_is_time
+ && qtdemux->variant != VARIANT_MSE_BYTESTREAM
+ && !qtdemux->first_moof_already_parsed
&& !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
&& min_dts != 0) {
/* Unless the user has explicitly requested another seek, perform an
goto beach;
}
+ if (length == G_MAXUINT64) {
+ /* Read until the end */
+ gint64 duration;
+ if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES,
+ &duration)) {
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
+ (_("Cannot query file size")),
+ ("Duration query on sink pad failed"));
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+ if (G_UNLIKELY (cur_offset > duration)) {
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
+ (_("Cannot query file size")),
+ ("Duration %" G_GINT64_FORMAT " < current offset %"
+ G_GUINT64_FORMAT, duration, cur_offset));
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+ length = duration - cur_offset;
+ if (length > QTDEMUX_MAX_ATOM_SIZE) {
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
+ (_("Cannot demux file")),
+ ("Moov atom size %" G_GINT64_FORMAT " > maximum %d", length,
+ QTDEMUX_MAX_ATOM_SIZE));
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+ }
+
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
if (ret != GST_FLOW_OK)
goto beach;
GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
(_("This file is incomplete and cannot be played.")),
("We got less than expected (received %" G_GSIZE_FORMAT
- ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
- (guint) length, cur_offset));
+ ", wanted %" G_GUINT64_FORMAT ", offset %" G_GUINT64_FORMAT ")",
+ map.size, length, cur_offset));
gst_buffer_unmap (moov, &map);
gst_buffer_unref (moov);
ret = GST_FLOW_ERROR;
if (info->crypto_info == NULL) {
if (stream->protection_scheme_type == FOURCC_cbcs) {
- crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
- if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) {
- GST_ERROR_OBJECT (qtdemux,
- "failed to attach cbcs metadata to buffer");
- qtdemux_gst_structure_free (crypto_info);
+ if (CUR_STREAM (stream)->fourcc == FOURCC_enca ||
+ CUR_STREAM (stream)->fourcc == FOURCC_encs ||
+ CUR_STREAM (stream)->fourcc == FOURCC_enct ||
+ CUR_STREAM (stream)->fourcc == FOURCC_encv) {
+ crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
+ if (!crypto_info
+ || !gst_buffer_add_protection_meta (buf, crypto_info)) {
+ GST_ERROR_OBJECT (qtdemux,
+ "failed to attach cbcs metadata to buffer");
+ qtdemux_gst_structure_free (crypto_info);
+ } else {
+ GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
+ }
} else {
- GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
+ GST_TRACE_OBJECT (qtdemux,
+ "cbcs stream is not encrypted yet, not adding protection metadata");
}
} else {
GST_DEBUG_OBJECT (qtdemux,
stream = QTDEMUX_NTH_STREAM (qtdemux, i);
position = stream->time_position;
- if (!GST_CLOCK_TIME_IS_VALID (position))
- continue;
-
- if (stream->segment_index != -1) {
- QtDemuxSegment *segment = &stream->segments[stream->segment_index];
- position += segment->media_start;
- }
-
/* position of -1 is EOS */
- if (position < min_time) {
+ if (position != GST_CLOCK_TIME_NONE && position < min_time) {
min_time = position;
target_stream = stream;
}
return gst_qtdemux_process_adapter (demux, FALSE);
}
+static guint64
+gst_segment_to_stream_time_clamped (const GstSegment * segment,
+ guint64 position)
+{
+ guint64 segment_stream_time_start;
+ guint64 segment_stream_time_stop = GST_CLOCK_TIME_NONE;
+ guint64 stream_pts_unsigned;
+ int ret;
+
+ g_return_val_if_fail (segment != NULL, GST_CLOCK_TIME_NONE);
+ g_return_val_if_fail (segment->format == GST_FORMAT_TIME,
+ GST_CLOCK_TIME_NONE);
+
+ segment_stream_time_start = segment->time;
+ if (segment->stop != GST_CLOCK_TIME_NONE)
+ segment_stream_time_stop =
+ gst_segment_to_stream_time (segment, GST_FORMAT_TIME, segment->stop);
+
+ ret =
+ gst_segment_to_stream_time_full (segment, GST_FORMAT_TIME, position,
+ &stream_pts_unsigned);
+ /* ret == 0 if the segment is invalid (either position, segment->time or the segment start are -1). */
+ g_return_val_if_fail (ret != 0, GST_CLOCK_TIME_NONE);
+
+ if (ret == -1 || stream_pts_unsigned < segment_stream_time_start) {
+ /* Negative or prior to segment start stream time, clamp to segment start. */
+ return segment_stream_time_start;
+ } else if (segment_stream_time_stop != GST_CLOCK_TIME_NONE
+ && stream_pts_unsigned > segment_stream_time_stop) {
+ /* Clamp to segment end. */
+ return segment_stream_time_stop;
+ } else {
+ return stream_pts_unsigned;
+ }
+}
+
static GstFlowReturn
gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
{
}
/* in MSS we need to expose the pads after the first moof as we won't get a moov */
- if (demux->mss_mode && !demux->exposed) {
+ if (demux->variant == VARIANT_MSS_FRAGMENTED && !demux->exposed) {
QTDEMUX_EXPOSE_LOCK (demux);
qtdemux_expose_streams (demux);
QTDEMUX_EXPOSE_UNLOCK (demux);
case QTDEMUX_STATE_MOVIE:{
QtDemuxStream *stream = NULL;
QtDemuxSample *sample;
- GstClockTime dts, pts, duration;
+ GstClockTime dts, pts, stream_pts, duration;
gboolean keyframe;
gint i;
dts = QTSAMPLE_DTS (stream, sample);
pts = QTSAMPLE_PTS (stream, sample);
+ stream_pts =
+ gst_segment_to_stream_time_clamped (&stream->segment, pts);
duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
keyframe = QTSAMPLE_KEYFRAME (stream, sample);
/* check for segment end */
if (G_UNLIKELY (demux->segment.stop != -1
- && demux->segment.stop <= pts && stream->on_keyframe)
+ && demux->segment.stop <= stream_pts && keyframe)
&& !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
case FOURCC_alac:
case FOURCC_fLaC:
case FOURCC_aavd:
+ case FOURCC_opus:
{
guint32 version;
guint32 offset;
min_size = 20;
else if (fourcc == FOURCC_fLaC)
min_size = 86;
+ else if (fourcc == FOURCC_opus)
+ min_size = 55;
else
min_size = 40;
{
/* parse, if found */
GstBuffer *buf;
- guint8 pres_delay_field;
GST_DEBUG_OBJECT (qtdemux,
"found av1C codec_data in stsd of size %d", size);
/* not enough data, just ignore and hope for the best */
- if (size < 5)
+ if (size < 4)
break;
/* Content is:
* 4 bytes: atom length
* 4 bytes: fourcc
- * 1 byte: version
- * 3 bytes: flags
- * 3 bits: reserved
- * 1 bits: initial_presentation_delay_present
- * 4 bits: initial_presentation_delay (if present else reserved
+ *
+ * version 1 (marker=1):
+ *
+ * unsigned int (1) marker = 1;
+ * unsigned int (7) version = 1;
+ * unsigned int (3) seq_profile;
+ * unsigned int (5) seq_level_idx_0;
+ * unsigned int (1) seq_tier_0;
+ * unsigned int (1) high_bitdepth;
+ * unsigned int (1) twelve_bit;
+ * unsigned int (1) monochrome;
+ * unsigned int (1) chroma_subsampling_x;
+ * unsigned int (1) chroma_subsampling_y;
+ * unsigned int (2) chroma_sample_position;
+ * unsigned int (3) reserved = 0;
+ *
+ * unsigned int (1) initial_presentation_delay_present;
+ * if (initial_presentation_delay_present) {
+ * unsigned int (4) initial_presentation_delay_minus_one;
+ * } else {
+ * unsigned int (4) reserved = 0;
+ * }
+ *
+ * unsigned int (8) configOBUs[];
+ *
* rest: OBUs.
*/
- if (av1_data[9] != 0) {
- GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
- break;
- }
+ switch (av1_data[8]) {
+ case 0x81:{
+ guint8 pres_delay_field;
+
+ /* We let profile and the other parts be figured out by
+ * av1parse and only include the presentation delay here
+ * if present */
+ /* We skip initial_presentation_delay* for now */
+ pres_delay_field = *(av1_data + 11);
+ if (pres_delay_field & (1 << 5)) {
+ gst_caps_set_simple (entry->caps,
+ "presentation-delay", G_TYPE_INT,
+ (gint) (pres_delay_field & 0x0F) + 1, NULL);
+ }
- /* We skip initial_presentation_delay* for now */
- pres_delay_field = *(av1_data + 12);
- if (pres_delay_field & (1 << 5)) {
- gst_caps_set_simple (entry->caps,
- "presentation-delay", G_TYPE_INT,
- (gint) (pres_delay_field & 0x0F) + 1, NULL);
- }
- if (size > 5) {
- buf = gst_buffer_new_and_alloc (size - 5);
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
- gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
- gst_caps_set_simple (entry->caps,
- "codec_data", GST_TYPE_BUFFER, buf, NULL);
- gst_buffer_unref (buf);
+ buf = gst_buffer_new_and_alloc (size);
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
+ gst_buffer_fill (buf, 0, av1_data + 8, size);
+ gst_caps_set_simple (entry->caps,
+ "codec_data", GST_TYPE_BUFFER, buf, NULL);
+ gst_buffer_unref (buf);
+ break;
+ }
+ default:
+ GST_WARNING ("Unknown version 0x%02x of av1C box",
+ av1_data[8]);
+ break;
}
+
break;
}
default:
entry->bytes_per_packet = entry->bytes_per_sample;
break;
}
+
+ /* According to TS 102 366, the channel count in
+ * a (E)AC3SampleEntry box is to be ignored */
+ case 0x20736d:
+ case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
+ case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
+ case FOURCC_ac_3:
+ entry->n_channels = 0;
+ break;
+
default:
break;
}
}
case FOURCC_opus:
{
- const guint8 *dops_data;
guint8 *channel_mapping = NULL;
- guint32 rate;
- guint8 channels;
+ guint32 dops_len, rate;
+ guint8 n_channels;
guint8 channel_mapping_family;
guint8 stream_count;
guint8 coupled_count;
guint8 i;
- version = GST_READ_UINT16_BE (stsd_entry_data + 16);
- if (version == 1)
- dops_data = stsd_entry_data + 51;
- else
- dops_data = stsd_entry_data + 35;
-
- channels = GST_READ_UINT8 (dops_data + 10);
- rate = GST_READ_UINT32_LE (dops_data + 13);
- channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
- stream_count = GST_READ_UINT8 (dops_data + 20);
- coupled_count = GST_READ_UINT8 (dops_data + 21);
-
- if (channels > 0) {
- channel_mapping = g_malloc (channels * sizeof (guint8));
- for (i = 0; i < channels; i++)
- channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
+ GNode *opus;
+ GNode *dops;
+
+ opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus);
+ if (opus == NULL) {
+ GST_WARNING_OBJECT (qtdemux, "Opus Sample Entry not found");
+ goto corrupt_file;
+ }
+
+ dops = qtdemux_tree_get_child_by_type (opus, FOURCC_dops);
+ if (dops == NULL) {
+ GST_WARNING_OBJECT (qtdemux, "Opus Specific Box not found");
+ goto corrupt_file;
}
- entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
+ /* Opus Specific Box content:
+ * 4 bytes: length
+ * 4 bytes: "dOps"
+ * 1 byte: Version;
+ * 1 byte: OutputChannelCount;
+ * 2 bytes: PreSkip (big-endians);
+ * 4 bytes: InputSampleRate (big-endians);
+ * 2 bytes: OutputGain (big-endians);
+ * 1 byte: ChannelMappingFamily;
+ * if (ChannelMappingFamily != 0) {
+ * 1 byte: StreamCount;
+ * 1 byte: CoupledCount;
+ * for (OutputChannel in 0..OutputChannelCount) {
+ * 1 byte: ChannelMapping;
+ * }
+ * }
+ */
+
+ dops_len = QT_UINT32 ((guint8 *) dops->data);
+ if (len < offset + dops_len) {
+ GST_WARNING_OBJECT (qtdemux,
+ "Opus Sample Entry has bogus size %" G_GUINT32_FORMAT, len);
+ goto corrupt_file;
+ }
+ if (dops_len < 19) {
+ GST_WARNING_OBJECT (qtdemux,
+ "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
+ dops_len);
+ goto corrupt_file;
+ }
+
+ n_channels = GST_READ_UINT8 ((guint8 *) dops->data + 9);
+ rate = GST_READ_UINT32_BE ((guint8 *) dops->data + 12);
+ channel_mapping_family = GST_READ_UINT8 ((guint8 *) dops->data + 18);
+
+ if (channel_mapping_family != 0) {
+ if (dops_len < 21 + n_channels) {
+ GST_WARNING_OBJECT (qtdemux,
+ "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
+ dops_len);
+ goto corrupt_file;
+ }
+
+ stream_count = GST_READ_UINT8 ((guint8 *) dops->data + 19);
+ coupled_count = GST_READ_UINT8 ((guint8 *) dops->data + 20);
+
+ if (n_channels > 0) {
+ channel_mapping = g_malloc (n_channels * sizeof (guint8));
+ for (i = 0; i < n_channels; i++)
+ channel_mapping[i] =
+ GST_READ_UINT8 ((guint8 *) dops->data + i + 21);
+ }
+ } else if (n_channels == 1) {
+ stream_count = 1;
+ coupled_count = 0;
+ } else if (n_channels == 2) {
+ stream_count = 1;
+ coupled_count = 1;
+ } else {
+ GST_WARNING_OBJECT (qtdemux,
+ "Opus unexpected nb of channels %d without channel mapping",
+ n_channels);
+ goto corrupt_file;
+ }
+
+ entry->caps = gst_codec_utils_opus_create_caps (rate, n_channels,
channel_mapping_family, stream_count, coupled_count,
channel_mapping);
g_free (channel_mapping);
+
+ entry->sampled = TRUE;
+
break;
}
default:
}
break;
}
+ case FOURCC_opus:
case FOURCC_lpcm:
case FOURCC_in24:
case FOURCC_in32:
gst_qtdemux_guess_bitrate (qtdemux);
- gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
-
/* If we have still old_streams, it's no more used stream */
for (i = 0; i < qtdemux->old_streams->len; i++) {
QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
g_ptr_array_set_size (qtdemux->old_streams, 0);
+ gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
+
/* check if we should post a redirect in case there is a single trak
* and it is a redirecting trak */
if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&