X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fisomp4%2Fqtdemux.c;h=ad07c1e365de345bf359976fa210453c731ff476;hb=29f924643cd9730210ac05b083514fd6d6bde465;hp=d953ca039b4c08640b01448212b362a2038b65ca;hpb=5d542030dbe8cdd8f21f87a196a270cc1557f06f;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index d953ca0..ad07c1e 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -349,6 +349,8 @@ struct _QtDemuxStream /* buffer needs some custom processing, e.g. subtitles */ gboolean need_process; + /* buffer needs potentially be split, e.g. CEA608 subtitles */ + gboolean need_split; /* current position */ guint32 segment_index; @@ -546,6 +548,8 @@ static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf); static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent, GstEvent * event); +static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent, + GstQuery * query); static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps); static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream); @@ -554,6 +558,8 @@ static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux, static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force); +static void gst_qtdemux_check_seekability (GstQTDemux * demux); + static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length); static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, @@ -656,6 +662,7 @@ gst_qtdemux_init (GstQTDemux * qtdemux) qtdemux_sink_activate_mode); gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain); gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event); + gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query); gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad); qtdemux->adapter = gst_adapter_new (); @@ -2002,6 +2009,8 @@ _create_stream (GstQTDemux * demux, guint32 track_id) gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM); g_queue_init (&stream->protection_scheme_event_queue); stream->ref_count = 1; + /* consistent default for push based mode */ + gst_segment_init (&stream->segment, GST_FORMAT_TIME); return stream; } @@ -2165,10 +2174,8 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) if (hard) { qtdemux->segment_seqnum = GST_SEQNUM_INVALID; - g_ptr_array_remove_range (qtdemux->active_streams, - 0, qtdemux->active_streams->len); - g_ptr_array_remove_range (qtdemux->old_streams, - 0, qtdemux->old_streams->len); + g_ptr_array_set_size (qtdemux->active_streams, 0); + g_ptr_array_set_size (qtdemux->old_streams, 0); qtdemux->n_video_streams = 0; qtdemux->n_audio_streams = 0; qtdemux->n_sub_streams = 0; @@ -2274,7 +2281,7 @@ gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest, g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream)); } - g_ptr_array_remove_range (src, 0, len); + g_ptr_array_set_size (src, 0); } static gboolean @@ -2505,6 +2512,46 @@ drop: return res; } +static gboolean +gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstQTDemux *demux = GST_QTDEMUX (parent); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_BITRATE: + { + GstClockTime duration; + + /* populate demux->upstream_size if not done yet */ + gst_qtdemux_check_seekability (demux); + + if (demux->upstream_size != -1 + && gst_qtdemux_get_duration (demux, &duration)) { + guint bitrate = + gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND, + duration); + + GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT + " duration %" GST_TIME_FORMAT " resulting a bitrate of %u", + demux->upstream_size, GST_TIME_ARGS (duration), bitrate); + + /* TODO: better results based on ranges/index tables */ + gst_query_set_bitrate (query, bitrate); + res = TRUE; + } + break; + } + default: + res = gst_pad_query_default (pad, (GstObject *) demux, query); + break; + } + + return res; +} + + #if 0 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index) @@ -4756,7 +4803,7 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux) seg_media_start_mov = seg->trak_media_start; GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT - " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT "\n", + " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT, k_index, target_ts, seg_media_start_mov, GST_TIME_ARGS (seg->media_start)); @@ -5167,8 +5214,8 @@ gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux, if (G_UNLIKELY (stream->segment_index != seg_idx)) gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position); - if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->segments[stream-> - segment_index]))) { + if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream-> + segments[stream->segment_index]))) { QtDemuxSegment *seg = &stream->segments[stream->segment_index]; GST_LOG_OBJECT (qtdemux, "Empty segment activated," @@ -5559,7 +5606,7 @@ gst_qtdemux_align_buffer (GstQTDemux * demux, } static guint8 * -convert_to_ccdata (const guint8 * ccpair, guint8 ccpair_size, guint field, +convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field, gsize * res) { guint8 *storage; @@ -5569,10 +5616,11 @@ convert_to_ccdata (const guint8 * ccpair, guint8 ccpair_size, guint field, *res = ccpair_size / 2 * 3; storage = g_malloc (*res); for (i = 0; i * 2 < ccpair_size; i += 1) { + /* FIXME: Use line offset 0 as we simply can't know here */ if (field == 1) - storage[i * 3] = 0xfc; + storage[i * 3] = 0x80 | 0x00; else - storage[i * 3] = 0xfd; + storage[i * 3] = 0x00 | 0x00; storage[i * 3 + 1] = ccpair[i * 2]; storage[i * 3 + 2] = ccpair[i * 2 + 1]; } @@ -5616,11 +5664,11 @@ extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size, goto invalid_cdat; } - /* Convert to cc_data triplet */ + /* Convert to S334-1 Annex A byte triplet */ if (fourcc == FOURCC_cdat) - cdat = convert_to_ccdata (data + 8, atom_length - 8, 1, &cdat_size); + cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size); else - cdt2 = convert_to_ccdata (data + 8, atom_length - 8, 2, &cdt2_size); + cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size); GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u", size, atom_length); @@ -5632,7 +5680,7 @@ extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size, if (fourcc == FOURCC_cdat) { if (cdat == NULL) cdat = - convert_to_ccdata (data + atom_length + 8, + convert_to_s334_1a (data + atom_length + 8, new_atom_length - 8, 1, &cdat_size); else GST_WARNING_OBJECT (stream->pad, @@ -5640,7 +5688,7 @@ extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size, } else { if (cdt2 == NULL) cdt2 = - convert_to_ccdata (data + atom_length + 8, + convert_to_s334_1a (data + atom_length + 8, new_atom_length - 8, 2, &cdt2_size); else GST_WARNING_OBJECT (stream->pad, @@ -5763,6 +5811,210 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream, return buf; } +static GstFlowReturn +gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream, + GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstClockTime pts, duration; + + if (stream->need_clip) + buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf); + + if (G_UNLIKELY (buf == NULL)) + goto exit; + + if (G_UNLIKELY (stream->discont)) { + GST_LOG_OBJECT (qtdemux, "marking discont buffer"); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } else { + GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT); + } + + GST_LOG_OBJECT (qtdemux, + "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT + ", duration %" GST_TIME_FORMAT " on pad %s", + GST_TIME_ARGS (GST_BUFFER_DTS (buf)), + GST_TIME_ARGS (GST_BUFFER_PTS (buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad)); + + if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) { + GstStructure *crypto_info; + QtDemuxCencSampleSetInfo *info = + (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info; + gint index; + GstEvent *event; + + while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) { + GST_TRACE_OBJECT (stream->pad, "pushing protection event: %" + GST_PTR_FORMAT, event); + gst_pad_push_event (stream->pad, event); + } + + if (info->crypto_info == NULL) { + GST_DEBUG_OBJECT (qtdemux, + "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted"); + } else { + /* The end of the crypto_info array matches our n_samples position, + * so count backward from there */ + index = stream->sample_index - stream->n_samples + info->crypto_info->len; + if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) { + /* steal structure from array */ + crypto_info = g_ptr_array_index (info->crypto_info, index); + g_ptr_array_index (info->crypto_info, index) = NULL; + GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index, + info->crypto_info->len); + if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) + GST_ERROR_OBJECT (qtdemux, + "failed to attach cenc metadata to buffer"); + } else { + GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d", + index, stream->sample_index); + } + } + } + + if (stream->alignment > 1) + buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment); + + pts = GST_BUFFER_PTS (buf); + duration = GST_BUFFER_DURATION (buf); + + ret = gst_pad_push (stream->pad, buf); + + if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) { + /* mark position in stream, we'll need this to know when to send GAP event */ + stream->segment.position = pts + duration; + } + +exit: + + return ret; +} + +static GstFlowReturn +gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream, + GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + + if (stream->subtype == FOURCC_clcp + && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) { + GstMapInfo map; + guint n_output_buffers, n_field1 = 0, n_field2 = 0; + guint n_triplets, i; + guint field1_off = 0, field2_off = 0; + + /* We have to split CEA608 buffers so that each outgoing buffer contains + * one byte pair per field according to the framerate of the video track. + * + * If there is only a single byte pair per field we don't have to do + * anything + */ + + gst_buffer_map (buf, &map, GST_MAP_READ); + + n_triplets = map.size / 3; + for (i = 0; i < n_triplets; i++) { + if (map.data[3 * i] & 0x80) + n_field1++; + else + n_field2++; + } + + g_assert (n_field1 || n_field2); + + /* If there's more than 1 frame we have to split, otherwise we can just + * pass through */ + if (n_field1 > 1 || n_field2 > 1) { + n_output_buffers = + gst_util_uint64_scale (GST_BUFFER_DURATION (buf), + CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d); + + for (i = 0; i < n_output_buffers; i++) { + GstBuffer *outbuf = + gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0)); + GstMapInfo outmap; + guint8 *outptr; + + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + outptr = outmap.data; + + if (n_field1) { + gboolean found = FALSE; + + while (map.data + field1_off < map.data + map.size) { + if (map.data[field1_off] & 0x80) { + memcpy (outptr, &map.data[field1_off], 3); + field1_off += 3; + found = TRUE; + break; + } + field1_off += 3; + } + + if (!found) { + const guint8 empty[] = { 0x80, 0x80, 0x80 }; + + memcpy (outptr, empty, 3); + } + + outptr += 3; + } + + if (n_field2) { + gboolean found = FALSE; + + while (map.data + field2_off < map.data + map.size) { + if ((map.data[field2_off] & 0x80) == 0) { + memcpy (outptr, &map.data[field2_off], 3); + field2_off += 3; + found = TRUE; + break; + } + field2_off += 3; + } + + if (!found) { + const guint8 empty[] = { 0x00, 0x80, 0x80 }; + + memcpy (outptr, empty, 3); + } + + outptr += 3; + } + + gst_buffer_unmap (outbuf, &outmap); + + GST_BUFFER_PTS (outbuf) = + GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i, + GST_SECOND * CUR_STREAM (stream)->fps_d, + CUR_STREAM (stream)->fps_n); + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d, + CUR_STREAM (stream)->fps_n); + GST_BUFFER_OFFSET (outbuf) = -1; + GST_BUFFER_OFFSET_END (outbuf) = -1; + + ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf); + + if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) + break; + } + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + } else { + gst_buffer_unmap (buf, &map); + ret = gst_qtdemux_push_buffer (qtdemux, stream, buf); + } + } else { + ret = gst_qtdemux_push_buffer (qtdemux, stream, buf); + } + + return ret; +} + /* Sets a buffer's attributes properly and pushes it downstream. * Also checks for additional actions and custom processing that may * need to be done first. @@ -5845,6 +6097,13 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, GST_BUFFER_OFFSET (buf) = -1; GST_BUFFER_OFFSET_END (buf) = -1; + if (!keyframe) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + stream->on_keyframe = FALSE; + } else { + stream->on_keyframe = TRUE; + } + if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette)) gst_buffer_append_memory (buf, gst_memory_ref (CUR_STREAM (stream)->rgb8_palette)); @@ -5872,79 +6131,7 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, } #endif - if (stream->need_clip) - buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf); - - if (G_UNLIKELY (buf == NULL)) - goto exit; - - if (G_UNLIKELY (stream->discont)) { - GST_LOG_OBJECT (qtdemux, "marking discont buffer"); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - stream->discont = FALSE; - } else { - GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT); - } - - if (!keyframe) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); - stream->on_keyframe = FALSE; - } else { - stream->on_keyframe = TRUE; - } - - - GST_LOG_OBJECT (qtdemux, - "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT - ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts), - GST_TIME_ARGS (pts), GST_TIME_ARGS (duration), - GST_PAD_NAME (stream->pad)); - - if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) { - GstStructure *crypto_info; - QtDemuxCencSampleSetInfo *info = - (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info; - gint index; - GstEvent *event; - - while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) { - GST_TRACE_OBJECT (stream->pad, "pushing protection event: %" - GST_PTR_FORMAT, event); - gst_pad_push_event (stream->pad, event); - } - - if (info->crypto_info == NULL) { - GST_DEBUG_OBJECT (qtdemux, - "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted"); - } else { - /* The end of the crypto_info array matches our n_samples position, - * so count backward from there */ - index = stream->sample_index - stream->n_samples + info->crypto_info->len; - if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) { - /* steal structure from array */ - crypto_info = g_ptr_array_index (info->crypto_info, index); - g_ptr_array_index (info->crypto_info, index) = NULL; - GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index, - info->crypto_info->len); - if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) - GST_ERROR_OBJECT (qtdemux, - "failed to attach cenc metadata to buffer"); - } else { - GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d", - index, stream->sample_index); - } - } - } - - if (stream->alignment > 1) - buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment); - - ret = gst_pad_push (stream->pad, buf); - - if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) { - /* mark position in stream, we'll need this to know when to send GAP event */ - stream->segment.position = pts + duration; - } + ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf); exit: return ret; @@ -6154,8 +6341,8 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) } /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */ - if (G_UNLIKELY (qtdemux-> - segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) { + if (G_UNLIKELY (qtdemux->segment. + flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) { if (stream->subtype == FOURCC_vide && !keyframe) { GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u", stream->track_id); @@ -8283,66 +8470,75 @@ gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux, } static gboolean -gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) +gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream) { - if (stream->subtype == FOURCC_vide) { - /* fps is calculated base on the duration of the average framerate since - * qt does not have a fixed framerate. */ - gboolean fps_available = TRUE; - guint32 first_duration = 0; - - if (stream->n_samples > 0) - first_duration = stream->samples[0].duration; - - if ((stream->n_samples == 1 && first_duration == 0) - || (qtdemux->fragmented && stream->n_samples_moof == 1)) { - /* still frame */ - CUR_STREAM (stream)->fps_n = 0; + /* fps is calculated base on the duration of the average framerate since + * qt does not have a fixed framerate. */ + gboolean fps_available = TRUE; + guint32 first_duration = 0; + + if (stream->n_samples > 0) + first_duration = stream->samples[0].duration; + + if ((stream->n_samples == 1 && first_duration == 0) + || (qtdemux->fragmented && stream->n_samples_moof == 1)) { + /* still frame */ + CUR_STREAM (stream)->fps_n = 0; + CUR_STREAM (stream)->fps_d = 1; + } else { + if (stream->duration == 0 || stream->n_samples < 2) { + CUR_STREAM (stream)->fps_n = stream->timescale; CUR_STREAM (stream)->fps_d = 1; + fps_available = FALSE; } else { - if (stream->duration == 0 || stream->n_samples < 2) { - CUR_STREAM (stream)->fps_n = stream->timescale; - CUR_STREAM (stream)->fps_d = 1; - fps_available = FALSE; + GstClockTime avg_duration; + guint64 duration; + guint32 n_samples; + + /* duration and n_samples can be updated for fragmented format + * so, framerate of fragmented format is calculated using data in a moof */ + if (qtdemux->fragmented && stream->n_samples_moof > 0 + && stream->duration_moof > 0) { + n_samples = stream->n_samples_moof; + duration = stream->duration_moof; } else { - GstClockTime avg_duration; - guint64 duration; - guint32 n_samples; - - /* duration and n_samples can be updated for fragmented format - * so, framerate of fragmented format is calculated using data in a moof */ - if (qtdemux->fragmented && stream->n_samples_moof > 0 - && stream->duration_moof > 0) { - n_samples = stream->n_samples_moof; - duration = stream->duration_moof; - } else { - n_samples = stream->n_samples; - duration = stream->duration; - } + n_samples = stream->n_samples; + duration = stream->duration; + } - /* Calculate a framerate, ignoring the first sample which is sometimes truncated */ - /* stream->duration is guint64, timescale, n_samples are guint32 */ - avg_duration = - gst_util_uint64_scale_round (duration - - first_duration, GST_SECOND, - (guint64) (stream->timescale) * (n_samples - 1)); + /* Calculate a framerate, ignoring the first sample which is sometimes truncated */ + /* stream->duration is guint64, timescale, n_samples are guint32 */ + avg_duration = + gst_util_uint64_scale_round (duration - + first_duration, GST_SECOND, + (guint64) (stream->timescale) * (n_samples - 1)); - GST_LOG_OBJECT (qtdemux, - "Calculating avg sample duration based on stream (or moof) duration %" - G_GUINT64_FORMAT - " minus first sample %u, leaving %d samples gives %" - GST_TIME_FORMAT, duration, first_duration, - n_samples - 1, GST_TIME_ARGS (avg_duration)); + GST_LOG_OBJECT (qtdemux, + "Calculating avg sample duration based on stream (or moof) duration %" + G_GUINT64_FORMAT + " minus first sample %u, leaving %d samples gives %" + GST_TIME_FORMAT, duration, first_duration, + n_samples - 1, GST_TIME_ARGS (avg_duration)); - gst_video_guess_framerate (avg_duration, &CUR_STREAM (stream)->fps_n, - &CUR_STREAM (stream)->fps_d); + fps_available = + gst_video_guess_framerate (avg_duration, + &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d); - GST_DEBUG_OBJECT (qtdemux, - "Calculating framerate, timescale %u gave fps_n %d fps_d %d", - stream->timescale, CUR_STREAM (stream)->fps_n, - CUR_STREAM (stream)->fps_d); - } + GST_DEBUG_OBJECT (qtdemux, + "Calculating framerate, timescale %u gave fps_n %d fps_d %d", + stream->timescale, CUR_STREAM (stream)->fps_n, + CUR_STREAM (stream)->fps_d); } + } + + return fps_available; +} + +static gboolean +gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) +{ + if (stream->subtype == FOURCC_vide) { + gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream); if (CUR_STREAM (stream)->caps) { CUR_STREAM (stream)->caps = @@ -8462,6 +8658,56 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) } } + else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) { + const GstStructure *s; + QtDemuxStream *fps_stream = NULL; + gboolean fps_available = FALSE; + + /* CEA608 closed caption tracks are a bit special in that each sample + * can contain CCs for multiple frames, and CCs can be omitted and have to + * be inferred from the duration of the sample then. + * + * As such we take the framerate from the (first) video track here for + * CEA608 as there must be one CC byte pair for every video frame + * according to the spec. + * + * For CEA708 all is fine and there is one sample per frame. + */ + + s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0); + if (gst_structure_has_name (s, "closedcaption/x-cea-608")) { + gint i; + + for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) { + QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i); + + if (tmp->subtype == FOURCC_vide) { + fps_stream = tmp; + break; + } + } + + if (fps_stream) { + fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream); + CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n; + CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d; + } + } else { + fps_available = gst_qtdemux_guess_framerate (qtdemux, stream); + fps_stream = stream; + } + + CUR_STREAM (stream)->caps = + gst_caps_make_writable (CUR_STREAM (stream)->caps); + + /* set framerate if calculated framerate is reliable */ + if (fps_available) { + gst_caps_set_simple (CUR_STREAM (stream)->caps, + "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n, + CUR_STREAM (stream)->fps_d, NULL); + } + } + if (stream->pad) { GstCaps *prev_caps = NULL; @@ -8564,8 +8810,6 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, QtDemuxStream * stream, GstTagList * list) { gboolean ret = TRUE; - /* consistent default for push based mode */ - gst_segment_init (&stream->segment, GST_FORMAT_TIME); if (stream->subtype == FOURCC_vide) { gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams); @@ -10225,11 +10469,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) version = QT_UINT32 ((guint8 *) mdhd->data + 8); GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version); if (version == 0x01000000) { - if (len < 38) + if (len < 42) goto corrupt_file; stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28); stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32); - lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36); + lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40); } else { if (len < 30) goto corrupt_file; @@ -10607,14 +10851,17 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) fiel = NULL; /* pick 'the' stsd child */ mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index); - if (!stream->protected) { - if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) { + // We should skip parsing the stsd for non-protected streams if + // the entry doesn't match the fourcc, since they don't change + // format. However, for protected streams we can have partial + // encryption, where parts of the stream are encrypted and parts + // not. For both parts of such streams, we should ensure the + // esds overrides are parsed for both from the stsd. + if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) { + if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) mp4v = NULL; - } - } else { - if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) { + else if (!stream->protected) mp4v = NULL; - } } if (mp4v) { @@ -11798,20 +12045,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) } mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index); - if (!stream->protected) { - } else { - if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) { - mp4v = NULL; - } - } - if (stream->protected && fourcc == FOURCC_mp4a) { - if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) { + if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) { + if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) mp4a = NULL; - } - } else { - if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_mp4a) { + else if (!stream->protected) mp4a = NULL; - } } wave = NULL; @@ -12617,8 +12855,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) return GST_FLOW_ERROR; } - g_ptr_array_remove_range (qtdemux->old_streams, - 0, qtdemux->old_streams->len); + g_ptr_array_set_size (qtdemux->old_streams, 0); + qtdemux->need_segment = TRUE; return GST_FLOW_OK; } @@ -12659,7 +12897,7 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) } } - g_ptr_array_remove_range (qtdemux->old_streams, 0, qtdemux->old_streams->len); + g_ptr_array_set_size (qtdemux->old_streams, 0); /* check if we should post a redirect in case there is a single trak * and it is a redirecting trak */ @@ -15258,8 +15496,9 @@ qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, _codec ("CEA 608 Closed Caption"); caps = gst_caps_new_simple ("closedcaption/x-cea-608", "format", - G_TYPE_STRING, "cc_data", NULL); + G_TYPE_STRING, "s334-1a", NULL); stream->need_process = TRUE; + stream->need_split = TRUE; break; case FOURCC_c708: _codec ("CEA 708 Closed Caption");