*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* <refsect2>
* <title>Example launch line</title>
* |[
- * gst-launch filesrc location=test.mov ! qtdemux name=demux demux.audio_0 ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_0 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
+ * gst-launch-1.0 filesrc location=test.mov ! qtdemux name=demux demux.audio_0 ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_0 ! queue ! decodebin ! videoconvert ! videoscale ! autovideosink
* ]| Play (parse and decode) a .mov file and try to output it to
* an automatically detected soundcard and videosink. If the MOV file contains
* compressed audio or video data, this will only work if you have the
/* timestamp + duration - dts is the duration */
#define QTSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \
(sample)->duration, GST_SECOND, (stream)->timescale) - (dts));
-/* timestamp + offset + duration - pts is the duration */
-#define QTSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \
- (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));
#define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
guint bytes_per_frame;
guint compression;
+ /* allocation */
+ gboolean use_allocator;
+ GstAllocator *allocator;
+ GstAllocationParams params;
+
/* when a discontinuity is pending */
gboolean discont;
static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
gchar ** codec_name);
+static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
+ QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
+ gchar ** codec_name);
+
static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
QtDemuxStream * stream, guint32 n);
static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
gst_static_pad_template_get (&gst_qtdemux_audiosrc_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_qtdemux_subsrc_template));
- gst_element_class_set_details_simple (gstelement_class, "QuickTime demuxer",
+ gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
"Codec/Demuxer",
"Demultiplex a QuickTime file into audio and video streams",
"David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
}
static void
-_gst_buffer_copy_into_mem (GstBuffer * dest, const guint8 * src,
- gsize offset, gsize size)
+_gst_buffer_copy_into_mem (GstBuffer * dest, gsize offset, const guint8 * src,
+ gsize size)
{
- guint8 *bdata;
gsize bsize;
g_return_if_fail (gst_buffer_is_writable (dest));
bsize = gst_buffer_get_size (dest);
g_return_if_fail (bsize >= offset + size);
- bdata = gst_buffer_map (dest, &bsize, NULL, GST_MAP_WRITE);
- memcpy (bdata + offset, src, size);
- gst_buffer_unmap (dest, bdata, bsize);
+ gst_buffer_fill (dest, offset, src, size);
}
static GstBuffer *
GstBuffer *buf;
buf = gst_buffer_new ();
- gst_buffer_take_memory (buf, -1,
+ gst_buffer_append_memory (buf,
gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
- mem, free_func, size, 0, size));
+ mem, size, 0, size, mem, free_func));
return buf;
}
GstBuffer ** buf)
{
GstFlowReturn flow;
- guint8 *bdata;
+ GstMapInfo map;
gsize bsize;
if (G_UNLIKELY (size == 0)) {
if (ret != GST_FLOW_OK)
return ret;
- bdata = gst_buffer_map (tmp, &bsize, NULL, GST_MAP_READ);
- size = QT_UINT32 (bdata);
+ gst_buffer_map (tmp, &map, GST_MAP_READ);
+ size = QT_UINT32 (map.data);
GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
- gst_buffer_unmap (tmp, bdata, bsize);
+ gst_buffer_unmap (tmp, &map);
gst_buffer_unref (tmp);
}
gst_query_parse_duration (query, &fmt, NULL);
if (fmt == GST_FORMAT_TIME) {
- gint64 duration = -1;
-
- gst_qtdemux_get_duration (qtdemux, &duration);
- if (duration > 0) {
- gst_query_set_duration (query, GST_FORMAT_TIME, duration);
- res = TRUE;
+ /* First try to query upstream */
+ res = gst_pad_query_default (pad, parent, query);
+ if (!res) {
+ gint64 duration = -1;
+
+ gst_qtdemux_get_duration (qtdemux, &duration);
+ if (duration > 0) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ res = TRUE;
+ }
}
}
break;
GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
qtdemux->tag_list);
gst_pad_push_event (stream->pad,
- gst_event_new_tag (gst_tag_list_copy (qtdemux->tag_list)));
+ gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
stream->send_global_tags = FALSE;
}
}
qtdemux->streams[i]->last_ret = GST_FLOW_OK;
gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
- qtdemux->sinkpad);
+ qtdemux->sinkpad, NULL);
GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
}
if (qtdemux->pullbased) {
res = gst_qtdemux_do_seek (qtdemux, pad, event);
- } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams &&
- !qtdemux->fragmented) {
+ } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
+ GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
+ res = TRUE;
+ } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
+ && !qtdemux->fragmented) {
res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
} else {
GST_DEBUG_OBJECT (qtdemux,
}
/* accept upstream's notion of segment and distribute along */
- segment.time = segment.start;
+ segment.format = GST_FORMAT_TIME;
+ segment.position = segment.time = segment.start;
segment.duration = demux->segment.duration;
segment.base = gst_segment_to_running_time (&demux->segment,
GST_FORMAT_TIME, demux->segment.position);
static void
gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
{
+ if (stream->allocator)
+ gst_object_unref (stream->allocator);
while (stream->buffers) {
gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
gst_caps_unref (stream->caps);
g_free (stream->segments);
if (stream->pending_tags)
- gst_tag_list_free (stream->pending_tags);
+ gst_tag_list_unref (stream->pending_tags);
g_free (stream->redirect_uri);
/* free stbl sub-atoms */
gst_qtdemux_stbl_free (stream);
gst_buffer_unref (qtdemux->comp_brands);
qtdemux->comp_brands = NULL;
if (qtdemux->tag_list)
- gst_tag_list_free (qtdemux->tag_list);
+ gst_tag_list_unref (qtdemux->tag_list);
qtdemux->tag_list = NULL;
#if 0
if (qtdemux->element_index)
}
static void
-qtdemux_post_global_tags (GstQTDemux * qtdemux)
-{
- if (qtdemux->tag_list) {
- /* all header tags ready and parsed, push them */
- GST_INFO_OBJECT (qtdemux, "posting global tags: %" GST_PTR_FORMAT,
- qtdemux->tag_list);
- /* post now, send event on pads later */
- gst_element_post_message (GST_ELEMENT (qtdemux),
- gst_message_new_tag (GST_OBJECT (qtdemux),
- gst_tag_list_copy (qtdemux->tag_list)));
- }
-}
-
-static void
qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
{
/* counts as header data */
GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (qtdemux->major_brand));
buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
- _gst_buffer_copy_into_mem (buf, buffer + 16, 0, length - 16);
+ _gst_buffer_copy_into_mem (buf, 0, buffer + 16, length - 16);
}
}
if (qtdemux->tag_list) {
/* prioritize native tags using _KEEP mode */
gst_tag_list_insert (qtdemux->tag_list, taglist, GST_TAG_MERGE_KEEP);
- gst_tag_list_free (taglist);
+ gst_tag_list_unref (taglist);
} else
qtdemux->tag_list = taglist;
}
}
static gboolean
+qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
+ guint64 * decode_time)
+{
+ guint32 version = 0;
+
+ if (!gst_byte_reader_get_uint32_be (br, &version))
+ return FALSE;
+
+ version >>= 24;
+ if (version == 1) {
+ if (!gst_byte_reader_get_uint64_be (br, decode_time))
+ goto failed;
+ } else {
+ guint32 dec_time = 0;
+ if (!gst_byte_reader_get_uint32_be (br, &dec_time))
+ goto failed;
+ *decode_time = dec_time;
+ }
+
+ GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
+ *decode_time);
+
+ return TRUE;
+
+failed:
+ {
+ GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
+ return FALSE;
+ }
+}
+
+static gboolean
qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
guint64 moof_offset, QtDemuxStream * stream)
{
- GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
- GstByteReader trun_data, tfhd_data;
+ GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node;
+ GstByteReader trun_data, tfhd_data, tfdt_data;
guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
gint64 base_offset, running_offset;
if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
&ds_size, &ds_flags, &base_offset))
goto missing_tfhd;
+ tfdt_node =
+ qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
+ &tfdt_data);
+ if (tfdt_node) {
+ guint64 decode_time = 0;
+ qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
+ /* If there is a new segment pending, update the time/position */
+ if (qtdemux->pending_newsegment) {
+ GstSegment segment;
+
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ segment.time = gst_util_uint64_scale (decode_time,
+ GST_SECOND, stream->timescale);
+ gst_event_replace (&qtdemux->pending_newsegment,
+ gst_event_new_segment (&segment));
+ /* ref added when replaced, release the original _new one */
+ gst_event_unref (qtdemux->pending_newsegment);
+ }
+ }
+
if (G_UNLIKELY (!stream)) {
/* we lost track of offset, we'll need to regain it,
* but can delay complaining until later or avoid doing so altogether */
GstBuffer *buf = NULL;
GstFlowReturn ret = GST_FLOW_OK;
guint64 cur_offset = qtdemux->offset;
- guint8 *data;
- gsize size;
+ GstMapInfo map;
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto beach;
- data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
- if (G_LIKELY (size >= 8))
- extract_initial_length_and_fourcc (data, size, &length, &fourcc);
- gst_buffer_unmap (buf, data, size);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ if (G_LIKELY (map.size >= 8))
+ extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
+ gst_buffer_unmap (buf, &map);
gst_buffer_unref (buf);
/* maybe we already got most we needed, so only consider this eof */
}
case FOURCC_moov:
{
- GstBuffer *moov;
+ GstBuffer *moov = NULL;
if (qtdemux->got_moov) {
GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
if (ret != GST_FLOW_OK)
goto beach;
- data = gst_buffer_map (moov, &size, NULL, GST_MAP_READ);
- if (length != size) {
+ gst_buffer_map (moov, &map, GST_MAP_READ);
+ if (length != map.size) {
/* Some files have a 'moov' atom at the end of the file which contains
* a terminal 'free' atom where the body of the atom is missing.
* Check for, and permit, this special case.
*/
- if (size >= 8) {
- guint8 *final_data = data + (size - 8);
+ if (map.size >= 8) {
+ guint8 *final_data = map.data + (map.size - 8);
guint32 final_length = QT_UINT32 (final_data);
guint32 final_fourcc = QT_FOURCC (final_data + 4);
- gst_buffer_unmap (moov, data, size);
- if (final_fourcc == FOURCC_free && size + final_length - 8 == length) {
+ gst_buffer_unmap (moov, &map);
+ if (final_fourcc == FOURCC_free
+ && map.size + final_length - 8 == length) {
/* Ok, we've found that special case. Allocate a new buffer with
* that free atom actually present. */
GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
- gst_buffer_copy_into (newmoov, moov, 0, 0, size);
- data = gst_buffer_map (newmoov, &size, NULL, GST_MAP_WRITE);
- memset (data + length - final_length + 8, 0, final_length - 8);
+ gst_buffer_copy_into (newmoov, moov, 0, 0, map.size);
+ gst_buffer_map (newmoov, &map, GST_MAP_WRITE);
+ memset (map.data + length - final_length + 8, 0, final_length - 8);
gst_buffer_unref (moov);
moov = newmoov;
}
}
}
- if (length != size) {
+ if (length != map.size) {
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 ")", size,
+ ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
(guint) length, cur_offset));
- gst_buffer_unmap (moov, data, size);
+ gst_buffer_unmap (moov, &map);
gst_buffer_unref (moov);
ret = GST_FLOW_ERROR;
goto beach;
}
qtdemux->offset += length;
- qtdemux_parse_moov (qtdemux, data, length);
+ qtdemux_parse_moov (qtdemux, map.data, length);
qtdemux_node_dump (qtdemux, qtdemux->moov_node);
qtdemux_parse_tree (qtdemux);
g_node_destroy (qtdemux->moov_node);
- gst_buffer_unmap (moov, data, size);
+ gst_buffer_unmap (moov, &map);
gst_buffer_unref (moov);
qtdemux->moov_node = NULL;
qtdemux->got_moov = TRUE;
}
case FOURCC_ftyp:
{
- GstBuffer *ftyp;
+ GstBuffer *ftyp = NULL;
/* extract major brand; might come in handy for ISO vs QT issues */
ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
if (ret != GST_FLOW_OK)
goto beach;
qtdemux->offset += length;
- data = gst_buffer_map (ftyp, &size, NULL, GST_MAP_READ);
- qtdemux_parse_ftyp (qtdemux, data, size);
- gst_buffer_unmap (ftyp, data, size);
+ gst_buffer_map (ftyp, &map, GST_MAP_READ);
+ qtdemux_parse_ftyp (qtdemux, map.data, map.size);
+ gst_buffer_unmap (ftyp, &map);
gst_buffer_unref (ftyp);
break;
}
case FOURCC_uuid:
{
- GstBuffer *uuid;
+ GstBuffer *uuid = NULL;
/* uuid are extension atoms */
ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
if (ret != GST_FLOW_OK)
goto beach;
qtdemux->offset += length;
- data = gst_buffer_map (uuid, &size, NULL, GST_MAP_READ);
- qtdemux_parse_uuid (qtdemux, data, size);
- gst_buffer_unmap (uuid, data, size);
+ gst_buffer_map (uuid, &map, GST_MAP_READ);
+ qtdemux_parse_uuid (qtdemux, map.data, map.size);
+ gst_buffer_unmap (uuid, &map);
gst_buffer_unref (uuid);
break;
}
default:
{
- GstBuffer *unknown;
+ GstBuffer *unknown = NULL;
GST_LOG_OBJECT (qtdemux,
"unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
if (ret != GST_FLOW_OK)
goto beach;
- data = gst_buffer_map (unknown, &size, NULL, GST_MAP_READ);
- GST_MEMDUMP ("Unknown tag", data, size);
- gst_buffer_unmap (unknown, data, size);
+ gst_buffer_map (unknown, &map, GST_MAP_READ);
+ GST_MEMDUMP ("Unknown tag", map.data, map.size);
+ gst_buffer_unmap (unknown, &map);
gst_buffer_unref (unknown);
qtdemux->offset += length;
break;
/* digested all data, show what we have */
ret = qtdemux_expose_streams (qtdemux);
- /* Only post, event on pads is done after newsegment */
- qtdemux_post_global_tags (qtdemux);
-
qtdemux->state = QTDEMUX_STATE_MOVIE;
GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
qtdemux->state);
rate = segment->rate * qtdemux->segment.rate;
/* update the segment values used for clipping */
- gst_segment_init (&stream->segment, GST_FORMAT_TIME);
/* accumulate previous segments */
if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
stream->segment.base += (stream->segment.stop - stream->segment.start) /
*/
static gboolean
gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
- QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
- guint64 * duration, gboolean * keyframe)
+ QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * dts,
+ guint64 * pts, guint64 * duration, gboolean * keyframe)
{
QtDemuxSample *sample;
guint64 time_position;
/* now get the info for the sample we're at */
sample = &stream->samples[stream->sample_index];
- *timestamp = QTSAMPLE_PTS (stream, sample);
+ *dts = QTSAMPLE_DTS (stream, sample);
+ *pts = QTSAMPLE_PTS (stream, sample);
*offset = sample->offset;
*size = sample->size;
- *duration = QTSAMPLE_DUR_PTS (stream, sample, *timestamp);
+ *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
*keyframe = QTSAMPLE_KEYFRAME (stream, sample);
return TRUE;
GstBuffer * buf)
{
guint64 start, stop, cstart, cstop, diff;
- GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
- guint size;
+ GstClockTime pts, dts, duration;
+ gsize size, osize;
gint num_rate, denom_rate;
gint frame_size;
gboolean clip_data;
guint offset;
- size = gst_buffer_get_size (buf);
+ osize = size = gst_buffer_get_size (buf);
offset = 0;
/* depending on the type, setup the clip parameters */
} else
goto wrong_type;
- /* we can only clip if we have a valid timestamp */
- timestamp = GST_BUFFER_TIMESTAMP (buf);
- if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
- goto no_timestamp;
+ /* we can only clip if we have a valid pts */
+ pts = GST_BUFFER_PTS (buf);
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
+ goto no_pts;
- if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) {
- duration = GST_BUFFER_DURATION (buf);
- } else {
+ dts = GST_BUFFER_DTS (buf);
+ duration = GST_BUFFER_DURATION (buf);
+
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
duration =
gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
}
- start = timestamp;
+ start = pts;
stop = start + duration;
if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
/* see if some clipping happened */
diff = cstart - start;
if (diff > 0) {
- timestamp = cstart;
+ pts += diff;
+ dts += diff;
duration -= diff;
if (clip_data) {
}
}
- gst_buffer_resize (buf, offset, size);
- GST_BUFFER_TIMESTAMP (buf) = timestamp;
+ if (offset != 0 || size != osize)
+ gst_buffer_resize (buf, offset, size);
+
+ GST_BUFFER_DTS (buf) = dts;
+ GST_BUFFER_PTS (buf) = pts;
GST_BUFFER_DURATION (buf) = duration;
return buf;
GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
return buf;
}
-no_timestamp:
+no_pts:
{
- GST_DEBUG_OBJECT (qtdemux, "no timestamp on buffer");
+ GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
return buf;
}
clipped:
gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
GstBuffer * buf)
{
- guint8 *data;
+ GstMapInfo map;
guint nsize = 0;
- gsize size;
gchar *str;
/* not many cases for now */
return buf;
}
- data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
- if (G_LIKELY (size >= 2)) {
- nsize = GST_READ_UINT16_BE (data);
- nsize = MIN (nsize, size - 2);
+ if (G_LIKELY (map.size >= 2)) {
+ nsize = GST_READ_UINT16_BE (map.data);
+ nsize = MIN (nsize, map.size - 2);
}
GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
- nsize, size);
+ nsize, map.size);
/* takes care of UTF-8 validation or UTF-16 recognition,
* no other encoding expected */
- str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);
- gst_buffer_unmap (buf, data, size);
+ str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
+ gst_buffer_unmap (buf, &map);
if (str) {
gst_buffer_unref (buf);
buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
static gboolean
gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
QtDemuxStream * stream, GstBuffer * buf,
- guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,
- guint64 byte_position)
+ guint64 dts, guint64 pts, guint64 duration, gboolean keyframe,
+ guint64 position, guint64 byte_position)
{
GstFlowReturn ret = GST_FLOW_OK;
if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
gchar *url;
- guint8 *bdata;
- gsize bsize;
+ GstMapInfo map;
- bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
- url = g_strndup ((gchar *) bdata, bsize);
- gst_buffer_unmap (buf, bdata, bsize);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ url = g_strndup ((gchar *) map.data, map.size);
+ gst_buffer_unmap (buf, &map);
if (url != NULL && strlen (url) != 0) {
/* we have RTSP redirect now */
gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
if (G_UNLIKELY (stream->need_process))
buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
- GST_BUFFER_TIMESTAMP (buf) = timestamp;
+ GST_BUFFER_DTS (buf) = dts;
+ GST_BUFFER_PTS (buf) = pts;
GST_BUFFER_DURATION (buf) = duration;
GST_BUFFER_OFFSET (buf) = -1;
GST_BUFFER_OFFSET_END (buf) = -1;
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
GST_LOG_OBJECT (qtdemux,
- "Pushing buffer with time %" GST_TIME_FORMAT ", duration %"
- GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
+ "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));
ret = gst_pad_push (stream->pad, buf);
QtDemuxStream *stream;
guint64 min_time;
guint64 offset = 0;
- guint64 timestamp = GST_CLOCK_TIME_NONE;
+ guint64 dts = GST_CLOCK_TIME_NONE;
+ guint64 pts = GST_CLOCK_TIME_NONE;
guint64 duration = 0;
gboolean keyframe = FALSE;
guint size = 0;
/* fetch info for the current sample of this stream */
if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset,
- &size, ×tamp, &duration, &keyframe)))
+ &size, &dts, &pts, &duration, &keyframe)))
goto eos_stream;
GST_LOG_OBJECT (qtdemux,
"pushing from stream %d, offset %" G_GUINT64_FORMAT
- ", size %d, timestamp=%" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
- index, offset, size, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
+ ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
+ ", duration %" GST_TIME_FORMAT, index, offset, size,
+ GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
/* hmm, empty sample, skip and move to next sample */
if (G_UNLIKELY (size <= 0))
GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
offset);
+ if (stream->use_allocator) {
+ /* if we have a per-stream allocator, use it */
+ buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
+ }
+
ret = gst_qtdemux_pull_atom (qtdemux, offset, size, &buf);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto beach;
ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
- timestamp, duration, keyframe, min_time, offset);
+ dts, pts, duration, keyframe, min_time, offset);
/* combine flows */
ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
GST_FORMAT_TIME, stop));
+ gst_qtdemux_push_event (qtdemux,
+ gst_event_new_segment_done (GST_FORMAT_TIME, stop));
} else {
/* For Reverse Playback */
GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
GST_FORMAT_TIME, qtdemux->segment.start));
+ gst_qtdemux_push_event (qtdemux,
+ gst_event_new_segment_done (GST_FORMAT_TIME,
+ qtdemux->segment.start));
}
} else {
GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
demux->state = QTDEMUX_STATE_MOVIE;
demux->neededbytes = next_entry_size (demux);
demux->mdatleft = size;
-
- /* Only post, event on pads is done after newsegment */
- qtdemux_post_global_tags (demux);
-
} else {
/* no headers yet, try to get them */
guint bs;
extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
&fourcc);
if (fourcc == FOURCC_moov) {
- GST_DEBUG_OBJECT (demux, "Parsing [moov]");
+ /* in usual fragmented setup we could try to scan for more
+ * and end up at the the moov (after mdat) again */
+ if (demux->got_moov && demux->n_streams > 0) {
+ GST_DEBUG_OBJECT (demux,
+ "Skipping moov atom as we have one already");
+ } else {
+ GST_DEBUG_OBJECT (demux, "Parsing [moov]");
- demux->got_moov = TRUE;
+ demux->got_moov = TRUE;
- /* prepare newsegment to send when streaming actually starts */
- if (!demux->pending_newsegment)
- demux->pending_newsegment = gst_event_new_segment (&demux->segment);
+ /* prepare newsegment to send when streaming actually starts */
+ if (!demux->pending_newsegment)
+ demux->pending_newsegment =
+ gst_event_new_segment (&demux->segment);
- qtdemux_parse_moov (demux, data, demux->neededbytes);
- qtdemux_node_dump (demux, demux->moov_node);
- qtdemux_parse_tree (demux);
- qtdemux_expose_streams (demux);
+ qtdemux_parse_moov (demux, data, demux->neededbytes);
+ qtdemux_node_dump (demux, demux->moov_node);
+ qtdemux_parse_tree (demux);
+ qtdemux_expose_streams (demux);
- g_node_destroy (demux->moov_node);
- demux->moov_node = NULL;
- GST_DEBUG_OBJECT (demux, "Finished parsing the header");
+ g_node_destroy (demux->moov_node);
+ demux->moov_node = NULL;
+ GST_DEBUG_OBJECT (demux, "Finished parsing the header");
+ }
} else if (fourcc == FOURCC_moof) {
if (demux->got_moov && demux->fragmented) {
GST_DEBUG_OBJECT (demux, "Parsing [moof]");
demux->neededbytes = next_entry_size (demux);
demux->state = QTDEMUX_STATE_MOVIE;
demux->mdatleft = gst_adapter_available (demux->adapter);
-
- /* Only post, event on pads is done after newsegment */
- qtdemux_post_global_tags (demux);
-
} else {
GST_DEBUG_OBJECT (demux, "Carrying on normally");
gst_adapter_flush (demux->adapter, demux->neededbytes);
GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
if (demux->mdatbuffer)
- demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
+ demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
else
demux->mdatbuffer = buf;
demux->offset += demux->neededbytes;
QtDemuxStream *stream = NULL;
QtDemuxSample *sample;
int i = -1;
- guint64 timestamp, duration, position;
+ guint64 dts, pts, duration;
gboolean keyframe;
GST_DEBUG_OBJECT (demux,
sample = &stream->samples[stream->sample_index];
- position = QTSAMPLE_DTS (stream, sample);
- timestamp = QTSAMPLE_PTS (stream, sample);
- duration = QTSAMPLE_DUR_DTS (stream, sample, position);
+ dts = QTSAMPLE_DTS (stream, sample);
+ pts = QTSAMPLE_PTS (stream, sample);
+ duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
keyframe = QTSAMPLE_KEYFRAME (stream, sample);
ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
- timestamp, duration, keyframe, position, demux->offset);
+ dts, pts, duration, keyframe, dts, demux->offset);
/* combine flows */
ret = gst_qtdemux_combine_flows (demux, stream, ret);
goto activate_push;
}
- pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
+ pull_mode = gst_query_has_scheduling_mode_with_flags (query,
+ GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
gst_query_unref (query);
if (!pull_mode)
if (active) {
demux->pullbased = TRUE;
res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
- sinkpad);
+ sinkpad, NULL);
} else {
res = gst_pad_stop_task (sinkpad);
}
switch (type) {
case FOURCC_tCtH:
buffer = gst_buffer_new_and_alloc (size);
- _gst_buffer_copy_into_mem (buffer, buf, 0, size);
+ _gst_buffer_copy_into_mem (buffer, 0, buf, size);
stream->buffers = g_slist_append (stream->buffers, buffer);
GST_LOG_OBJECT (qtdemux, "parsing theora header");
break;
case FOURCC_tCt_:
buffer = gst_buffer_new_and_alloc (size);
- _gst_buffer_copy_into_mem (buffer, buf, 0, size);
+ _gst_buffer_copy_into_mem (buffer, 0, buf, size);
stream->buffers = g_slist_append (stream->buffers, buffer);
GST_LOG_OBJECT (qtdemux, "parsing theora comment");
break;
case FOURCC_tCtC:
buffer = gst_buffer_new_and_alloc (size);
- _gst_buffer_copy_into_mem (buffer, buf, 0, size);
+ _gst_buffer_copy_into_mem (buffer, 0, buf, size);
stream->buffers = g_slist_append (stream->buffers, buffer);
GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
break;
case FOURCC_MP4V:
case FOURCC_fmp4:
case FOURCC_FMP4:
+ case FOURCC_apcs:
+ case FOURCC_apch:
+ case FOURCC_apcn:
+ case FOURCC_apco:
+ case FOURCC_ap4h:
{
const guint8 *buf;
guint32 version;
return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
}
+static void
+qtdemux_do_allocation (GstQTDemux * qtdemux, QtDemuxStream * stream)
+{
+ GstQuery *query;
+
+ query = gst_query_new_allocation (stream->caps, FALSE);
+
+ if (!gst_pad_peer_query (stream->pad, query)) {
+ /* not a problem, just debug a little */
+ GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
+ }
+
+ if (stream->allocator)
+ gst_object_unref (stream->allocator);
+
+ if (gst_query_get_n_allocation_params (query) > 0) {
+ /* try the allocator */
+ gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
+ &stream->params);
+ stream->use_allocator = TRUE;
+ } else {
+ stream->allocator = NULL;
+ gst_allocation_params_init (&stream->params);
+ stream->use_allocator = FALSE;
+ }
+ gst_query_unref (query);
+}
+
static gboolean
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
QtDemuxStream * stream, GstTagList * list)
/* make sure it's not writable. We leave MALLOCDATA to NULL so that we
* don't free any of the buffer data. */
palette = _gst_buffer_new_wrapped ((gpointer) palette_data,
- palette_count, NULL);
+ palette_count * 4, NULL);
gst_caps_set_simple (stream->caps, "palette_data",
GST_TYPE_BUFFER, palette, NULL);
gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
g_free (name);
if (stream->caps) {
- /* FIXME: Need to set channel-mask here and maybe reorder */
gst_caps_set_simple (stream->caps,
"rate", G_TYPE_INT, (int) stream->rate,
"channels", G_TYPE_INT, stream->n_channels, NULL);
+
+ if (stream->n_channels > 2) {
+ /* FIXME: Need to parse the 'chan' atom to get channel layouts
+ * correctly; this is just the minimum we can do - assume
+ * we don't actually have any channel positions. */
+ gst_caps_set_simple (stream->caps,
+ "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
+ }
}
qtdemux->n_audio_streams++;
} else if (stream->subtype == FOURCC_strm) {
gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
g_free (name);
qtdemux->n_sub_streams++;
+ } else if (stream->caps) {
+ gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
+
+ stream->pad =
+ gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
+ g_free (name);
+ qtdemux->n_video_streams++;
} else {
GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
goto done;
}
if (stream->pad) {
+ gchar *stream_id;
+
GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
gst_pad_use_fixed_caps (stream->pad);
gst_pad_set_active (stream->pad, TRUE);
GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
+ stream_id =
+ gst_pad_create_stream_id_printf (stream->pad,
+ GST_ELEMENT_CAST (qtdemux), "%u", stream->track_id);
+ gst_pad_push_event (stream->pad, gst_event_new_stream_start (stream_id));
+ g_free (stream_id);
gst_pad_set_caps (stream->pad, stream->caps);
GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
if (stream->pending_tags)
- gst_tag_list_free (stream->pending_tags);
+ gst_tag_list_unref (stream->pending_tags);
stream->pending_tags = list;
/* global tags go on each pad anyway */
stream->send_global_tags = TRUE;
+
+ qtdemux_do_allocation (qtdemux, stream);
}
done:
return TRUE;
G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
while (TRUE) {
- guint8 *bdata;
- gsize bsize;
+ GstMapInfo map;
+ buf = NULL;
ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto locate_failed;
gst_buffer_unref (buf);
goto locate_failed;
}
- bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
- extract_initial_length_and_fourcc (bdata, 16, length, &lfourcc);
- gst_buffer_unmap (buf, bdata, bsize);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
+ gst_buffer_unmap (buf, &map);
gst_buffer_unref (buf);
if (G_UNLIKELY (*length == 0)) {
GstBuffer *buf = NULL;
GstFlowReturn ret = GST_FLOW_OK;
GstFlowReturn res = GST_FLOW_OK;
- guint8 *bdata;
- gsize bsize;
+ GstMapInfo map;
offset = qtdemux->moof_offset;
GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto flow_failed;
- bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
- if (!qtdemux_parse_moof (qtdemux, bdata, bsize, offset, NULL)) {
- gst_buffer_unmap (buf, bdata, bsize);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
+ gst_buffer_unmap (buf, &map);
gst_buffer_unref (buf);
buf = NULL;
goto parse_failed;
}
- gst_buffer_unmap (buf, bdata, bsize);
+ gst_buffer_unmap (buf, &map);
gst_buffer_unref (buf);
buf = NULL;
flow_failed:
{
/* maybe upstream temporarily flushing */
- if (ret != GST_FLOW_WRONG_STATE) {
+ if (ret != GST_FLOW_FLUSHING) {
GST_DEBUG_OBJECT (qtdemux, "no next moof");
offset = 0;
} else {
GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
/* make sure there's enough data */
- if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 2 * 4))
- goto corrupt_file;
+ if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
+ stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
+ GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
+ stream->n_sample_times);
+ if (!stream->n_sample_times)
+ goto corrupt_file;
+ }
/* sync sample atom */
stream->stps_present = FALSE;
seqh_size = QT_UINT32 (data + 4);
if (seqh_size > 0) {
_seqh = gst_buffer_new_and_alloc (seqh_size);
- _gst_buffer_copy_into_mem (_seqh, data + 8, 0, seqh_size);
+ _gst_buffer_copy_into_mem (_seqh, 0, data + 8, seqh_size);
}
}
}
break;
}
/* skipping to the next entry */
- gst_byte_reader_skip (&dref, atom_size - 8);
+ if (!gst_byte_reader_skip (&dref, atom_size - 8))
+ break;
} else {
GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
"atom header");
break;
}
/* skip to the next entry */
- gst_byte_reader_skip (&dref, size - 8);
+ if (!gst_byte_reader_skip (&dref, size - 8))
+ break;
} else {
GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
}
static const guint wb_bitrates[] = {
6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
};
- guint8 *data;
- gsize size, max_mode;
+ GstMapInfo map;
+ gsize max_mode;
guint16 mode_set;
- data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
- if (size != 0x11) {
- GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, size);
+ if (map.size != 0x11) {
+ GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
goto bad_data;
}
- if (QT_FOURCC (data + 4) != GST_MAKE_FOURCC ('d', 'a', 'm', 'r')) {
+ if (QT_FOURCC (map.data + 4) != GST_MAKE_FOURCC ('d', 'a', 'm', 'r')) {
GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (QT_UINT32 (data + 4)));
+ GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
goto bad_data;
}
- mode_set = QT_UINT16 (data + 13);
+ mode_set = QT_UINT16 (map.data + 13);
if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
max_mode = 7 + (wb ? 1 : 0);
goto bad_data;
}
- gst_buffer_unmap (buf, data, size);
+ gst_buffer_unmap (buf, &map);
return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
bad_data:
- gst_buffer_unmap (buf, data, size);
+ gst_buffer_unmap (buf, &map);
return 0;
}
avc_data + 8 + 1, size - 1);
buf = gst_buffer_new_and_alloc (size);
- _gst_buffer_copy_into_mem (buf, avc_data + 0x8, 0, size);
+ _gst_buffer_copy_into_mem (buf, 0, avc_data + 0x8, size);
gst_caps_set_simple (stream->caps,
"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
if (len > 0x8) {
len -= 0x8;
buf = gst_buffer_new_and_alloc (len);
- _gst_buffer_copy_into_mem (buf, data + 8, 0, len);
+ _gst_buffer_copy_into_mem (buf, 0, data + 8, len);
gst_caps_set_simple (stream->caps,
"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
if (len > 0x8) {
len -= 0x8;
buf = gst_buffer_new_and_alloc (len);
- _gst_buffer_copy_into_mem (buf, data + 8, 0, len);
+ _gst_buffer_copy_into_mem (buf, 0, data + 8, len);
gst_caps_set_simple (stream->caps,
"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
buf = gst_buffer_new_and_alloc (len);
- _gst_buffer_copy_into_mem (buf, stsd_data, 0, len);
+ _gst_buffer_copy_into_mem (buf, 0, stsd_data, len);
gst_caps_set_simple (stream->caps,
"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
break;
}
buf = gst_buffer_new_and_alloc (ovc1_len - 198);
- _gst_buffer_copy_into_mem (buf, ovc1_data + 198, 0, ovc1_len - 198);
+ _gst_buffer_copy_into_mem (buf, 0, ovc1_data + 198, ovc1_len - 198);
gst_caps_set_simple (stream->caps,
"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
}
if (enda) {
gst_caps_set_simple (stream->caps,
- "format", G_TYPE_STRING, "S24_3LE", NULL);
+ "format", G_TYPE_STRING, "S24LE", NULL);
}
break;
}
}
wfex = (WAVEFORMATEX *) (owma_data + 36);
buf = gst_buffer_new_and_alloc (owma_len - 54);
- _gst_buffer_copy_into_mem (buf, owma_data + 54, 0, owma_len - 54);
+ _gst_buffer_copy_into_mem (buf, 0, owma_data + 54, owma_len - 54);
if (wfex->wFormatTag == 0x0161) {
codec_name = "Windows Media Audio";
version = 2;
headerlen -= 8;
headerbuf = gst_buffer_new_and_alloc (headerlen);
- _gst_buffer_copy_into_mem (headerbuf, waveheader, 0, headerlen);
+ _gst_buffer_copy_into_mem (headerbuf, 0, waveheader, headerlen);
if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
headerbuf, &header, &extra)) {
if (extra)
gst_buffer_unref (extra);
+ g_free (header);
}
}
} else
if (len > 0x4C) {
GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);
- _gst_buffer_copy_into_mem (buf, stsd_data + 0x4C, 0, len - 0x4C);
+ _gst_buffer_copy_into_mem (buf, 0, stsd_data + 0x4C, len - 0x4C);
gst_caps_set_simple (stream->caps,
"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
/* codec-data contains alac atom size and prefix,
* ffmpeg likes it that way, not quite gst-ish though ...*/
buf = gst_buffer_new_and_alloc (len);
- _gst_buffer_copy_into_mem (buf, alac->data, 0, len);
+ _gst_buffer_copy_into_mem (buf, 0, alac->data, len);
gst_caps_set_simple (stream->caps,
"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
guint bitrate;
- _gst_buffer_copy_into_mem (buf, stsd_data + 0x34, 0, len - 0x34);
+ _gst_buffer_copy_into_mem (buf, 0, stsd_data + 0x34, len - 0x34);
/* If we have enough data, let's try to get the 'damr' atom. See
* the 3GPP container spec (26.244) for more details. */
break;
}
} else {
- goto unknown_stream;
+ /* everything in 1 sample */
+ stream->sampled = TRUE;
+
+ stream->caps =
+ qtdemux_generic_caps (qtdemux, stream, fourcc, stsd_data, &codec);
+
+ if (stream->caps == NULL)
+ goto unknown_stream;
+
+ if (codec) {
+ list = gst_tag_list_new_empty ();
+ gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_SUBTITLE_CODEC, codec, NULL);
+ g_free (codec);
+ codec = NULL;
+ }
}
/* promote to sampled format */
GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
- if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)) {
+ if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
+ || size <= 0) {
GST_DEBUG_OBJECT (qtdemux,
"Size in bytes of the stream not known - bailing");
return;
/* Subtract the header size */
GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
size, qtdemux->header_size);
- g_assert (size >= qtdemux->header_size);
+
+ if (size < qtdemux->header_size)
+ return;
+
size = size - qtdemux->header_size;
if (!gst_qtdemux_get_duration (qtdemux, &duration) ||
if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
GstMessage *m;
- qtdemux_post_global_tags (qtdemux);
-
GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
"an external content");
m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
GST_MAKE_FOURCC ('3', 'g', 0, 0));
} else if (qtdemux->comp_brands != NULL) {
+ GstMapInfo map;
guint8 *data;
gsize size;
gboolean res = FALSE;
- data = gst_buffer_map (qtdemux->comp_brands, &size, NULL, GST_MAP_READ);
+ gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
+ data = map.data;
+ size = map.size;
while (size >= 4) {
res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
GST_MAKE_FOURCC ('3', 'g', 0, 0));
data += 4;
size -= 4;
}
- gst_buffer_unmap (qtdemux->comp_brands, data, size);
+ gst_buffer_unmap (qtdemux->comp_brands, &map);
return res;
} else {
return FALSE;
if (len < 12 + 2)
return;
- buf = gst_buffer_new_allocate (NULL, len - 14, 0);
+ buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
gst_buffer_fill (buf, 0, data + 14, len - 14);
taglist = gst_tag_list_from_id3v2_tag (buf);
}
if (taglist)
- gst_tag_list_free (taglist);
+ gst_tag_list_unref (taglist);
gst_buffer_unref (buf);
}
GstBuffer *buf;
gchar *media_type;
const gchar *style;
- GstCaps *caps;
+ GstSample *sample;
+ GstStructure *s;
guint i;
guint8 ndata[4];
data = node->data;
len = QT_UINT32 (data);
buf = gst_buffer_new_and_alloc (len);
- _gst_buffer_copy_into_mem (buf, data, 0, len);
+ _gst_buffer_copy_into_mem (buf, 0, data, len);
/* heuristic to determine style of tag */
if (QT_FOURCC (data + 4) == FOURCC_____ ||
ndata[0], ndata[1], ndata[2], ndata[3]);
GST_DEBUG_OBJECT (demux, "media type %s", media_type);
- caps = gst_caps_new_simple (media_type, "style", G_TYPE_STRING, style, NULL);
- // TODO conver to metadata or ???
-// gst_buffer_set_caps (buf, caps);
- gst_caps_unref (caps);
+ s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
+ sample = gst_sample_new (buf, NULL, NULL, s);
+ gst_buffer_unref (buf);
g_free (media_type);
- GST_DEBUG_OBJECT (demux, "adding private tag; size %d, caps %" GST_PTR_FORMAT,
- len, caps);
+ GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
+ len, s);
gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_APPEND,
- GST_QT_DEMUX_PRIVATE_TAG, buf, NULL);
- gst_buffer_unref (buf);
+ GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
}
static void
}
GST_DEBUG_OBJECT (qtdemux, "new tag list");
- if (!qtdemux->tag_list)
+ if (!qtdemux->tag_list) {
qtdemux->tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
+ }
i = 0;
while (i < G_N_ELEMENTS (add_funcs)) {
{
const gchar *fmt;
- if (tags == NULL)
+ if (tags == NULL) {
tags = gst_tag_list_new_empty ();
+ gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
+ }
if (qtdemux->major_brand == FOURCC_mjp2)
fmt = "Motion JPEG 2000";
}
}
if (datetime) {
- if (!qtdemux->tag_list)
+ if (!qtdemux->tag_list) {
qtdemux->tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
+ }
/* Use KEEP as explicit tags should have a higher priority than mvhd tag */
gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
GstBuffer *buffer;
buffer = gst_buffer_new_and_alloc (data_len);
- _gst_buffer_copy_into_mem (buffer, data_ptr, 0, data_len);
+ _gst_buffer_copy_into_mem (buffer, 0, data_ptr, data_len);
GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
caps = gst_caps_new_simple ("video/x-msmpeg",
"msmpegversion", G_TYPE_INT, 43, NULL);
break;
- case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
- case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
- _codec ("3ivX video");
- caps = gst_caps_new_empty_simple ("video/x-3ivx");
- break;
case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
_codec ("DivX 3");
caps = gst_caps_new_simple ("video/x-divx",
caps = gst_caps_new_simple ("video/x-divx",
"divxversion", G_TYPE_INT, 5, NULL);
break;
+
+ case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
+ case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
- _codec ("XVID MPEG-4");
- caps = gst_caps_new_empty_simple ("video/x-xvid");
- break;
-
case GST_MAKE_FOURCC ('F', 'M', 'P', '4'):
case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
caps = gst_caps_new_simple ("video/mpeg",
"mpegversion", G_TYPE_INT, 4, NULL);
if (codec_name)
- *codec_name = g_strdup ("FFmpeg MPEG-4");
+ *codec_name = g_strdup ("MPEG-4");
break;
case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
_codec ("On2 VP8");
caps = gst_caps_from_string ("video/x-vp8");
break;
+ case GST_MAKE_FOURCC ('a', 'p', 'c', 's'):
+ _codec ("Apple ProRes LT");
+ caps =
+ gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
+ NULL);
+ break;
+ case GST_MAKE_FOURCC ('a', 'p', 'c', 'h'):
+ _codec ("Apple ProRes HQ");
+ caps =
+ gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
+ NULL);
+ break;
+ case GST_MAKE_FOURCC ('a', 'p', 'c', 'n'):
+ _codec ("Apple ProRes");
+ caps =
+ gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
+ "standard", NULL);
+ break;
+ case GST_MAKE_FOURCC ('a', 'p', 'c', 'o'):
+ _codec ("Apple ProRes Proxy");
+ caps =
+ gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
+ "proxy", NULL);
+ break;
+ case GST_MAKE_FOURCC ('a', 'p', '4', 'h'):
+ _codec ("Apple ProRes 4444");
+ caps =
+ gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
+ "4444", NULL);
+ break;
case FOURCC_ovc1:
_codec ("VC-1");
caps = gst_caps_new_simple ("video/x-wmv",
case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
default:
{
- char *s;
+ char *s, fourstr[5];
- s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (fourcc));
+ g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+ s = g_strdup_printf ("video/x-gst-fourcc-%s", g_strstrip (fourstr));
caps = gst_caps_new_empty_simple (s);
break;
}
case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
_codec ("Raw 8-bit PCM audio");
caps = gst_caps_new_simple ("audio/x-raw",
- "format", G_TYPE_STRING, "U8", NULL);
+ "format", G_TYPE_STRING, "U8",
+ "layout", G_TYPE_STRING, "interleaved", NULL);
break;
case GST_MAKE_FOURCC ('t', 'w', 'o', 's'):
endian = G_BIG_ENDIAN;
g_free (str);
caps = gst_caps_new_simple ("audio/x-raw",
- "format", G_TYPE_STRING, gst_audio_format_to_string (format), NULL);
+ "format", G_TYPE_STRING, gst_audio_format_to_string (format),
+ "layout", G_TYPE_STRING, "interleaved", NULL);
break;
}
case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
_codec ("Raw 64-bit floating-point audio");
caps = gst_caps_new_simple ("audio/x-raw",
- "format", G_TYPE_STRING, "F64BE", NULL);
+ "format", G_TYPE_STRING, "F64BE",
+ "layout", G_TYPE_STRING, "interleaved", NULL);
break;
case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
_codec ("Raw 32-bit floating-point audio");
caps = gst_caps_new_simple ("audio/x-raw",
- "format", G_TYPE_STRING, "F32BE", NULL);
+ "format", G_TYPE_STRING, "F32BE",
+ "layout", G_TYPE_STRING, "interleaved", NULL);
break;
case FOURCC_in24:
_codec ("Raw 24-bit PCM audio");
/* we assume BIG ENDIAN, an enda box will tell us to change this to little
* endian later */
caps = gst_caps_new_simple ("audio/x-raw",
- "format", G_TYPE_STRING, "S24BE", NULL);
+ "format", G_TYPE_STRING, "S24BE",
+ "layout", G_TYPE_STRING, "interleaved", NULL);
break;
case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
_codec ("Raw 32-bit PCM audio");
caps = gst_caps_new_simple ("audio/x-raw",
- "format", G_TYPE_STRING, "S32BE", NULL);
+ "format", G_TYPE_STRING, "S32BE",
+ "layout", G_TYPE_STRING, "interleaved", NULL);
break;
case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'):
_codec ("Mu-law audio");
/* ? */
default:
{
- char *s;
+ char *s, fourstr[5];
- s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (fourcc));
+ g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+ s = g_strdup_printf ("audio/x-gst-fourcc-%s", g_strstrip (fourstr));
caps = gst_caps_new_empty_simple (s);
break;
}
switch (fourcc) {
case GST_MAKE_FOURCC ('m', 'p', '4', 's'):
_codec ("DVD subtitle");
- caps = gst_caps_new_empty_simple ("video/x-dvd-subpicture");
+ caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
break;
case GST_MAKE_FOURCC ('t', 'e', 'x', 't'):
_codec ("Quicktime timed text");
case GST_MAKE_FOURCC ('t', 'x', '3', 'g'):
_codec ("3GPP timed text");
text:
- caps = gst_caps_new_empty_simple ("text/plain");
+ caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
+ "utf8", NULL);
/* actual text piece needs to be extracted */
stream->need_process = TRUE;
break;
default:
{
- char *s;
+ char *s, fourstr[5];
- s = g_strdup_printf ("text/x-gst-fourcc-%" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (fourcc));
+ g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+ s = g_strdup_printf ("text/x-gst-fourcc-%s", g_strstrip (fourstr));
caps = gst_caps_new_empty_simple (s);
break;
}
}
return caps;
}
+
+static GstCaps *
+qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
+ guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
+{
+ GstCaps *caps;
+
+ switch (fourcc) {
+ case GST_MAKE_FOURCC ('m', '1', 'v', ' '):
+ _codec ("MPEG 1 video");
+ caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
+ "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+ break;
+ default:
+ caps = NULL;
+ break;
+ }
+ return caps;
+}