7 #include "piffdemux.h"
\r
8 #include <glib/gprintf.h>
\r
9 #include <gst/tag/tag.h>
\r
13 #include <piffatomparser.h>
\r
14 #include <piffdemux_fourcc.h>
\r
15 #include <piffpalette.h>
\r
16 #include <piffdemux_types.h>
\r
17 #include <piffdemux_dump.h>
\r
19 #define PIFF_DEFAULT_TRACKID -1
\r
20 #define PIFF_DEFAULT_FOURCC 0
\r
21 #define PIFF_DEFAULT_TIMESCALE 10000000
\r
22 #define PIFF_DEFAULT_DURATION -1
\r
23 #define PIFF_DEFAULT_START_TS 0
\r
24 #define PIFF_DEFAULT_START_TS 0
\r
26 #define PIFF_DEFAULT_WIDTH 16
\r
27 #define PIFF_DEFAULT_HEIGHT 16
\r
28 #define PIFF_DEFAULT_BPS 16
\r
30 #undef DEC_OUT_FRAME_DUMP
\r
32 #ifdef DEC_OUT_FRAME_DUMP
\r
34 FILE *piffdump = NULL;
\r
37 #define PIFFDEMUX_RB16(x) ((((const unsigned char*)(x))[0] << 8) | ((const unsigned char*)(x))[1])
\r
38 /* max. size considered 'sane' for non-mdat atoms */
\r
39 #define PIFFDEMUX_MAX_ATOM_SIZE (25*1024*1024)
\r
41 /* if the sample index is larger than this, something is likely wrong */
\r
42 #define PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
\r
44 GST_DEBUG_CATEGORY (piffdemux_debug);
\r
46 typedef struct _PiffDemuxSegment PiffDemuxSegment;
\r
47 typedef struct _PiffDemuxSample PiffDemuxSample;
\r
48 typedef struct _PiffDemuxSubSampleEncryption PiffDemuxSubSampleEncryption;
\r
49 typedef struct _PiffDemuxSubSampleEntryInfo PiffDemuxSubSampleEntryInfo;
\r
54 PROP_PIFF_MEDIA_CAPS,
\r
55 PROP_PIFF_MEDIA_TIMESCALE,
\r
56 PROP_PIFF_MEDIA_DURATION,
\r
57 PROP_PIFF_MEDIA_START_TIMESTAMP,
\r
59 PROP_PIFF_LOOKAHEAD_COUNT,
\r
60 PROP_PIFF_AVG_FRAME_DUR,
\r
69 static guint gst_piffdemux_signals[LAST_SIGNAL] = { 0 };
\r
71 struct _PiffDemuxSubSampleEntryInfo
\r
73 guint16 LenofClearData;
\r
74 guint32 LenofEncryptData;
\r
77 struct _PiffDemuxSubSampleEncryption
\r
80 PiffDemuxSubSampleEntryInfo *sub_entry;
\r
83 struct _PiffDemuxSample
\r
86 gint32 pts_offset; /* Add this value to timestamp to get the pts */
\r
88 guint64 timestamp; /* DTS In mov time */
\r
89 guint32 duration; /* In mov time */
\r
90 gboolean keyframe; /* TRUE when this packet is a keyframe */
\r
91 guint8 *iv; /* initialization vector for decryption*/
\r
92 PiffDemuxSubSampleEncryption *sub_encry;
\r
95 /* timestamp is the DTS */
\r
96 #define PIFFSAMPLE_DTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp,\
\r
97 GST_SECOND, (stream)->timescale)
\r
98 /* timestamp + offset is the PTS */
\r
99 #define PIFFSAMPLE_PTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp + \
\r
100 (sample)->pts_offset, GST_SECOND, (stream)->timescale)
\r
101 /* timestamp + duration - dts is the duration */
\r
102 #define PIFFSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \
\r
103 (sample)->duration, GST_SECOND, (stream)->timescale) - (dts));
\r
104 /* timestamp + offset + duration - pts is the duration */
\r
105 #define PIFFSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \
\r
106 (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));
\r
108 #define PIFFSAMPLE_KEYFRAME(stream,sample) ((sample)->keyframe);
\r
110 typedef char uuid_t[16];
\r
112 static const uuid_t tfxd_uuid = { 0x6d, 0x1d, 0x9b, 0x05,
\r
113 0x42, 0xd5, 0x44, 0xe6,
\r
114 0x80, 0xe2, 0x14, 0x1d,
\r
115 0xaf, 0xf7, 0x57, 0xb2 };
\r
117 static const uuid_t tfrf_uuid = { 0xd4, 0x80, 0x7e, 0xf2,
\r
118 0xca, 0x39, 0x46, 0x95,
\r
119 0x8e, 0x54, 0x26, 0xcb,
\r
120 0x9e, 0x46, 0xa7, 0x9f };
\r
122 static const uuid_t encrypt_uuid = { 0xa2, 0x39, 0x4f, 0x52,
\r
123 0x5a, 0x9b, 0x4f, 0x14,
\r
124 0xa2, 0x44, 0x6c, 0x42,
\r
125 0x7c, 0x64, 0x8d, 0xf4 };
\r
127 #define SE_OVERRIDE_TE_FLAGS 0x000001
\r
128 #define SE_USE_SUBSAMPLE_ENCRYPTION 0x000002
\r
135 UUID_SAMPLE_ENCRYPT,
\r
138 struct _PiffDemuxSegment
\r
140 /* global time and duration, all gst time */
\r
144 /* media time of trak, all gst time */
\r
145 guint64 media_start;
\r
146 guint64 media_stop;
\r
151 struct _PiffDemuxStream
\r
158 /* duration/scale */
\r
159 guint64 duration; /* in timescale */
\r
164 PiffDemuxSample *samples;
\r
165 guint32 min_duration; /* duration in timescale of first sample, used for figuring out
\r
166 the framerate, in timescale units */
\r
168 /* if we use chunks or samples */
\r
172 /* when a discontinuity is pending */
\r
175 /* list of buffers to push first */
\r
178 /* buffer needs some custom processing, e.g. subtitles */
\r
179 gboolean need_process;
\r
181 /* current position */
\r
182 guint32 segment_index;
\r
183 guint32 sample_index;
\r
184 guint64 time_position; /* in gst time */
\r
186 /* the Gst segment we are processing out, used for clipping */
\r
187 GstSegment segment;
\r
189 /* last GstFlowReturn */
\r
190 GstFlowReturn last_ret;
\r
193 /* quicktime segments */
\r
194 guint32 n_segments;
\r
195 PiffDemuxSegment *segments;
\r
196 guint32 from_sample;
\r
200 GstTagList *pending_tags;
\r
201 gboolean send_global_tags;
\r
203 GstEvent *pending_event;
\r
205 gboolean sent_nsevent;
\r
209 guint64 avg_dur; /* average frame duration */
\r
213 enum PiffDemuxState
\r
215 PIFFDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
\r
216 PIFFDEMUX_STATE_HEADER, /* Parsing the header */
\r
217 PIFFDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
\r
218 PIFFDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
\r
222 static GNode *piffdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
\r
223 static GNode *piffdemux_tree_get_child_by_type_full (GNode * node,
\r
224 guint32 fourcc, GstByteReader * parser);
\r
225 static GNode *piffdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
\r
226 static GNode *piffdemux_tree_get_sibling_by_type_full (GNode * node,
\r
227 guint32 fourcc, GstByteReader * parser);
\r
229 static GstStaticPadTemplate gst_piffdemux_sink_template =
\r
230 GST_STATIC_PAD_TEMPLATE ("sink",
\r
233 GST_STATIC_CAPS ("application/x-piff")
\r
236 static GstStaticPadTemplate gst_piffdemux_src_template =
\r
237 GST_STATIC_PAD_TEMPLATE ("src",
\r
240 GST_STATIC_CAPS_ANY);
\r
243 GST_BOILERPLATE (GstPiffDemux, gst_piffdemux, GstPiffDemux, GST_TYPE_ELEMENT);
\r
245 static void gst_piffdemux_dispose (GObject * object);
\r
247 static GstStateChangeReturn gst_piffdemux_change_state (GstElement * element,
\r
248 GstStateChange transition);
\r
250 gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
\r
252 gst_piffdemux_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
\r
253 static GstFlowReturn gst_piffdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
\r
254 static gboolean gst_piffdemux_handle_sink_event (GstPad * pad, GstEvent * event);
\r
255 static gboolean piffdemux_parse_node (GstPiffDemux * piffdemux, GNode * node, const guint8 * buffer, guint length);
\r
256 static gboolean piffdemux_parse_sample_encryption(GstPiffDemux * piffdemux, GstByteReader *sample_encrypt, PiffDemuxStream * stream);
\r
257 static gboolean piffdemux_parse_mfhd (GstPiffDemux * piffdemux, GstByteReader * mfhd);
\r
258 static gboolean gst_piffdemux_handle_src_event (GstPad * pad, GstEvent * event);
\r
259 static const GstQueryType *gst_piffdemux_get_src_query_types (GstPad * pad);
\r
260 static gboolean gst_piffdemux_handle_src_query (GstPad * pad, GstQuery * query);
\r
264 ConvertH264_MetaDCI_to_3GPPDCI(unsigned char *dci_meta_buf, unsigned int dci_meta_size, unsigned char **dci_3gpp_buf, unsigned int *dci_3gpp_size);
\r
266 __gst_piffdemux_marshal_BOOLEAN__OBJECT (GClosure *closure,
\r
267 GValue *return_value G_GNUC_UNUSED,
\r
268 guint n_param_values,
\r
269 const GValue *param_values,
\r
270 gpointer invocation_hint G_GNUC_UNUSED,
\r
271 gpointer marshal_data);
\r
274 gst_piffdemux_base_init (gpointer klass)
\r
276 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
\r
278 gst_element_class_add_pad_template (element_class,
\r
279 gst_static_pad_template_get (&gst_piffdemux_sink_template));
\r
280 gst_element_class_add_pad_template (element_class,
\r
281 gst_static_pad_template_get (&gst_piffdemux_src_template));
\r
282 gst_element_class_set_details_simple (element_class, "PIFF demuxer",
\r
284 "Parser for PIFF file format",
\r
285 "naveen ch <naveen.ch@samsung.com>");
\r
287 GST_DEBUG_CATEGORY_INIT (piffdemux_debug, "piffdemux", 0, "piffdemux plugin");
\r
291 gst_piffdemux_class_init (GstPiffDemuxClass * klass)
\r
293 GObjectClass *gobject_class;
\r
294 GstElementClass *gstelement_class;
\r
296 gobject_class = (GObjectClass *) klass;
\r
297 gstelement_class = (GstElementClass *) klass;
\r
299 parent_class = g_type_class_peek_parent (klass);
\r
301 gobject_class->dispose = gst_piffdemux_dispose;
\r
302 gobject_class->set_property = gst_piffdemux_set_property;
\r
303 gobject_class->get_property = gst_piffdemux_get_property;
\r
305 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_piffdemux_change_state);
\r
307 g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_CAPS,
\r
308 g_param_spec_boxed ("caps", "Caps",
\r
309 "The allowed caps for the src pad", GST_TYPE_CAPS,
\r
310 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
\r
312 /* timescale of media to be set by application */
\r
313 g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_TIMESCALE,
\r
314 g_param_spec_uint64 ("timescale", "media timescale",
\r
315 "media timescale in PIFF Manifest", 0, G_MAXUINT64,
\r
316 PIFF_DEFAULT_TIMESCALE,
\r
317 G_PARAM_READWRITE));
\r
319 g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_DURATION,
\r
320 g_param_spec_int64 ("duration", "Duration of media",
\r
321 "Total duration of the content", -1, G_MAXINT64,
\r
322 PIFF_DEFAULT_DURATION,
\r
323 G_PARAM_READWRITE));
\r
325 g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_START_TIMESTAMP,
\r
326 g_param_spec_uint64 ("start-ts", "expected start timestamp",
\r
327 "expected start timestamp to avoid reset", 0, G_MAXUINT64,
\r
328 PIFF_DEFAULT_START_TS,
\r
329 G_PARAM_READWRITE));
\r
331 g_object_class_install_property (gobject_class, PROP_PIFF_IS_LIVE,
\r
332 g_param_spec_boolean ("is-live", "Is presentation is Live or VOD",
\r
333 "If Presentation is Live (true) else VOD (false)",
\r
335 G_PARAM_READWRITE));
\r
337 g_object_class_install_property (gobject_class, PROP_PIFF_LOOKAHEAD_COUNT,
\r
338 g_param_spec_uint ("lookahead-count", "Lookahead count value",
\r
339 "Look ahead count used in case of Live presentation", 0, G_MAXUINT,
\r
341 G_PARAM_READWRITE));
\r
343 g_object_class_install_property (gobject_class, PROP_PIFF_AVG_FRAME_DUR,
\r
344 g_param_spec_uint64 ("frame-dur", "Average frame duration",
\r
345 "Average frame duration", 0, G_MAXUINT64,
\r
347 G_PARAM_READABLE));
\r
349 gst_piffdemux_signals[SIGNAL_LIVE_PARAM] =
\r
350 g_signal_new ("live-param", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
\r
351 G_STRUCT_OFFSET (GstPiffDemuxClass, live_param), NULL, NULL,
\r
352 g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
\r
357 gst_piffdemux_init (GstPiffDemux * piffdemux, GstPiffDemuxClass * klass)
\r
360 piffdemux->sinkpad = gst_pad_new_from_static_template (&gst_piffdemux_sink_template, "sink");
\r
361 gst_pad_set_chain_function (piffdemux->sinkpad, gst_piffdemux_chain);
\r
362 gst_pad_set_event_function (piffdemux->sinkpad, gst_piffdemux_handle_sink_event);
\r
363 gst_element_add_pad (GST_ELEMENT_CAST (piffdemux), piffdemux->sinkpad);
\r
366 piffdemux->srcpad = gst_pad_new_from_static_template (&gst_piffdemux_src_template, "src");
\r
367 gst_pad_set_event_function (piffdemux->srcpad, gst_piffdemux_handle_src_event);
\r
368 gst_pad_use_fixed_caps (piffdemux->srcpad);
\r
369 gst_pad_set_query_type_function (piffdemux->srcpad, gst_piffdemux_get_src_query_types);
\r
370 gst_pad_set_query_function (piffdemux->srcpad, gst_piffdemux_handle_src_query);
\r
371 gst_element_add_pad (GST_ELEMENT_CAST (piffdemux), piffdemux->srcpad);
\r
373 piffdemux->stream = g_new0 (PiffDemuxStream, 1);
\r
374 piffdemux->stream->fourcc = PIFF_DEFAULT_FOURCC;
\r
375 piffdemux->stream->timescale = PIFF_DEFAULT_TIMESCALE;
\r
376 piffdemux->stream->duration = PIFF_DEFAULT_DURATION;
\r
377 piffdemux->stream->caps = NULL;
\r
378 piffdemux->stream->discont = TRUE;
\r
379 piffdemux->stream->need_process = FALSE;
\r
380 piffdemux->stream->segment_index = -1;
\r
381 piffdemux->stream->time_position = 0;
\r
382 piffdemux->stream->sample_index = -1;
\r
383 piffdemux->stream->last_ret = GST_FLOW_OK;
\r
384 piffdemux->stream->sent_nsevent = FALSE;
\r
385 piffdemux->stream->start_ts = PIFF_DEFAULT_START_TS;
\r
386 piffdemux->stream->avg_dur = -1;
\r
388 piffdemux->state = PIFFDEMUX_STATE_INITIAL;
\r
389 piffdemux->neededbytes = 16;
\r
390 piffdemux->todrop = 0;
\r
391 piffdemux->adapter = gst_adapter_new ();
\r
392 piffdemux->offset = 0;
\r
393 piffdemux->first_mdat = -1;
\r
394 piffdemux->mdatoffset = GST_CLOCK_TIME_NONE;
\r
395 piffdemux->mdatbuffer = NULL;
\r
396 piffdemux->moof_rcvd = FALSE;
\r
397 piffdemux->is_live = FALSE;
\r
398 piffdemux->lookahead_cnt = 0;
\r
400 #ifdef DEC_OUT_FRAME_DUMP
\r
401 piffdump = fopen ("/opt/media/piff_out_dump.dmp", "w+");
\r
402 if (piffdump == NULL)
\r
404 g_print ("\nNot able to create frame dump file\n");
\r
408 gst_segment_init (&piffdemux->segment, GST_FORMAT_TIME);
\r
412 gst_piffdemux_dispose (GObject * object)
\r
414 GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);
\r
416 if (piffdemux->adapter) {
\r
417 g_object_unref (G_OBJECT (piffdemux->adapter));
\r
418 piffdemux->adapter = NULL;
\r
421 #ifdef DEC_OUT_FRAME_DUMP
\r
427 G_OBJECT_CLASS (parent_class)->dispose (object);
\r
432 gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
\r
434 GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);
\r
437 case PROP_PIFF_MEDIA_CAPS: {
\r
438 if (piffdemux->stream->caps)
\r
439 gst_caps_unref(piffdemux->stream->caps);
\r
440 piffdemux->stream->caps = gst_caps_copy (gst_value_get_caps (value));
\r
441 GST_DEBUG_OBJECT (piffdemux, "stream caps = %s", gst_caps_to_string(piffdemux->stream->caps));
\r
442 if (!gst_pad_set_caps(piffdemux->srcpad, piffdemux->stream->caps)) {
\r
443 GST_ERROR_OBJECT (piffdemux, "not able to set caps...");
\r
447 case PROP_PIFF_MEDIA_TIMESCALE:
\r
448 piffdemux->stream->timescale = g_value_get_uint64(value);
\r
450 case PROP_PIFF_MEDIA_DURATION:
\r
451 piffdemux->stream->duration = g_value_get_int64(value);
\r
453 case PROP_PIFF_MEDIA_START_TIMESTAMP:
\r
454 piffdemux->stream->start_ts = g_value_get_uint64(value);
\r
455 GST_INFO_OBJECT (piffdemux, "start_ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(piffdemux->stream->start_ts));
\r
457 case PROP_PIFF_IS_LIVE:
\r
458 piffdemux->is_live = g_value_get_boolean(value);
\r
460 case PROP_PIFF_LOOKAHEAD_COUNT:
\r
461 piffdemux->lookahead_cnt = g_value_get_uint(value);
\r
462 GST_DEBUG_OBJECT (piffdemux, "Look ahead count = %d", piffdemux->lookahead_cnt);
\r
465 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
\r
472 gst_piffdemux_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
\r
474 GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);
\r
477 case PROP_PIFF_MEDIA_CAPS:
\r
478 gst_value_set_caps (value, piffdemux->stream->caps);
\r
480 case PROP_PIFF_MEDIA_TIMESCALE:
\r
481 g_value_set_uint64 (value, piffdemux->stream->timescale);
\r
483 case PROP_PIFF_MEDIA_DURATION:
\r
484 g_value_set_int64 (value, piffdemux->stream->duration);
\r
486 case PROP_PIFF_MEDIA_START_TIMESTAMP:
\r
487 g_value_set_uint64 (value, piffdemux->stream->start_ts);
\r
489 case PROP_PIFF_IS_LIVE:
\r
490 g_value_set_boolean(value, piffdemux->is_live);
\r
492 case PROP_PIFF_LOOKAHEAD_COUNT:
\r
493 g_value_set_uint (value, piffdemux->lookahead_cnt);
\r
495 case PROP_PIFF_AVG_FRAME_DUR:
\r
496 g_value_set_uint64 (value, piffdemux->stream->avg_dur);
\r
499 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
\r
506 gst_piffdemux_post_no_playable_stream_error (GstPiffDemux * piffdemux)
\r
508 if (piffdemux->posted_redirect) {
\r
509 GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
\r
510 ("This file contains no playable streams."),
\r
511 ("no known streams found, a redirect message has been posted"));
\r
513 GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
\r
514 ("This file contains no playable streams."),
\r
515 ("no known streams found"));
\r
521 gst_piffdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
\r
522 GstFormat dest_format, gint64 * dest_value)
\r
524 gboolean res = TRUE;
\r
525 PiffDemuxStream *stream = gst_pad_get_element_private (pad);
\r
526 GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));
\r
528 if (stream->subtype != FOURCC_vide) {
\r
533 switch (src_format) {
\r
534 case GST_FORMAT_TIME:
\r
535 switch (dest_format) {
\r
536 case GST_FORMAT_BYTES:{
\r
545 case GST_FORMAT_BYTES:
\r
546 switch (dest_format) {
\r
547 case GST_FORMAT_TIME:{
\r
561 gst_object_unref (piffdemux);
\r
566 static const GstQueryType *
\r
567 gst_piffdemux_get_src_query_types (GstPad * pad)
\r
569 static const GstQueryType src_types[] = {
\r
570 GST_QUERY_POSITION,
\r
571 GST_QUERY_DURATION,
\r
582 gst_piffdemux_get_duration (GstPiffDemux * piffdemux, gint64 * duration)
\r
584 gboolean res = TRUE;
\r
586 *duration = GST_CLOCK_TIME_NONE;
\r
588 if (piffdemux->stream->duration != 0) {
\r
589 if (piffdemux->stream->duration != G_MAXINT64 && piffdemux->stream->timescale != 0) {
\r
590 *duration = gst_util_uint64_scale (piffdemux->stream->duration,
\r
591 GST_SECOND, piffdemux->stream->timescale);
\r
598 gst_piffdemux_handle_src_query (GstPad * pad, GstQuery * query)
\r
600 gboolean res = FALSE;
\r
601 GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));
\r
603 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
\r
605 switch (GST_QUERY_TYPE (query)) {
\r
606 case GST_QUERY_POSITION:
\r
607 GST_ERROR ("Querying POSITION from piffdemux....");
\r
608 if (GST_CLOCK_TIME_IS_VALID (piffdemux->segment.last_stop)) {
\r
609 gst_query_set_position (query, GST_FORMAT_TIME,
\r
610 piffdemux->segment.last_stop);
\r
614 case GST_QUERY_DURATION:{
\r
616 GST_ERROR ("Querying DURATION from piffdemux....");
\r
618 gst_query_parse_duration (query, &fmt, NULL);
\r
619 if (fmt == GST_FORMAT_TIME) {
\r
620 gint64 duration = -1;
\r
622 gst_piffdemux_get_duration (piffdemux, &duration);
\r
623 if (duration > 0) {
\r
624 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
\r
630 case GST_QUERY_CONVERT:{
\r
631 GstFormat src_fmt, dest_fmt;
\r
632 gint64 src_value, dest_value = 0;
\r
634 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
\r
636 res = gst_piffdemux_src_convert (pad,
\r
637 src_fmt, src_value, dest_fmt, &dest_value);
\r
639 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
\r
644 case GST_QUERY_FORMATS:
\r
645 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
\r
648 case GST_QUERY_SEEKING:{
\r
653 res = gst_pad_query_default (pad, query);
\r
657 gst_object_unref (piffdemux);
\r
664 gst_piffdemux_push_tags (GstPiffDemux * piffdemux, PiffDemuxStream * stream)
\r
666 if (G_UNLIKELY (stream->pending_tags)) {
\r
667 GST_DEBUG_OBJECT (piffdemux, "Sending tags %" GST_PTR_FORMAT,
\r
668 stream->pending_tags);
\r
669 gst_pad_push_event (piffdemux->srcpad,
\r
670 gst_event_new_tag (stream->pending_tags));
\r
671 stream->pending_tags = NULL;
\r
674 if (G_UNLIKELY (stream->send_global_tags && piffdemux->tag_list)) {
\r
675 GST_DEBUG_OBJECT (piffdemux, "Sending global tags %" GST_PTR_FORMAT,
\r
676 piffdemux->tag_list);
\r
677 gst_pad_push_event (piffdemux->srcpad,
\r
678 gst_event_new_tag (gst_tag_list_copy (piffdemux->tag_list)));
\r
679 stream->send_global_tags = FALSE;
\r
685 gst_piffdemux_push_event (GstPiffDemux * piffdemux, GstEvent * event)
\r
687 GstEventType etype = GST_EVENT_TYPE (event);
\r
689 GST_DEBUG_OBJECT (piffdemux, "pushing %s event on source pad",
\r
690 GST_EVENT_TYPE_NAME (event));
\r
692 if (piffdemux->stream->sent_eos) {
\r
693 GST_INFO_OBJECT (piffdemux, "already sent eos");
\r
697 if (!gst_pad_push_event (piffdemux->srcpad, event)) {
\r
698 GST_ERROR_OBJECT (piffdemux, "error in sending event to srcpad...");
\r
701 if (etype == GST_EVENT_EOS)
\r
702 piffdemux->stream->sent_eos = TRUE;
\r
706 /* find the segment for @time_position for @stream
\r
708 * Returns -1 if the segment cannot be found.
\r
711 gst_piffdemux_find_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,
\r
712 guint64 time_position)
\r
717 GST_LOG_OBJECT (piffdemux, "finding segment for %" GST_TIME_FORMAT,
\r
718 GST_TIME_ARGS (time_position));
\r
720 /* find segment corresponding to time_position if we are looking
\r
721 * for a segment. */
\r
723 for (i = 0; i < stream->n_segments; i++) {
\r
724 PiffDemuxSegment *segment = &stream->segments[i];
\r
726 GST_LOG_OBJECT (piffdemux,
\r
727 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
\r
728 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
\r
730 /* For the last segment we include stop_time in the last segment */
\r
731 if (i < stream->n_segments - 1) {
\r
732 if (segment->time <= time_position && time_position < segment->stop_time) {
\r
733 GST_LOG_OBJECT (piffdemux, "segment %d matches", i);
\r
738 if (segment->time <= time_position && time_position <= segment->stop_time) {
\r
739 GST_LOG_OBJECT (piffdemux, "segment %d matches", i);
\r
750 gst_piffdemux_handle_src_event (GstPad * pad, GstEvent * event)
\r
752 gboolean res = TRUE;
\r
753 GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));
\r
755 switch (GST_EVENT_TYPE (event)) {
\r
756 case GST_EVENT_QOS:
\r
757 case GST_EVENT_NAVIGATION:
\r
759 gst_event_unref (event);
\r
761 case GST_EVENT_SEEK:
\r
763 res = gst_pad_event_default (pad, event);
\r
767 gst_object_unref (piffdemux);
\r
774 gst_piffdemux_move_stream (GstPiffDemux * piffdemux, PiffDemuxStream * str,
\r
777 /* no change needed */
\r
778 if (index == str->sample_index)
\r
781 GST_DEBUG_OBJECT (piffdemux, "moving to sample %u of %u", index,
\r
784 /* position changed, we have a discont */
\r
785 str->sample_index = index;
\r
786 /* Each time we move in the stream we store the position where we are
\r
788 str->from_sample = index;
\r
789 str->discont = TRUE;
\r
792 // TODO: need to check more on this below function
\r
793 /* stream/index return sample that is min/max w.r.t. byte position,
\r
794 * time is min/max w.r.t. time of samples,
\r
795 * the latter need not be time of the former sample */
\r
797 gst_piffdemux_find_sample (GstPiffDemux * piffdemux, gint64 byte_pos, gboolean fw,
\r
798 gboolean set, PiffDemuxStream ** _stream, gint * _index, gint64 * _time)
\r
801 gint64 time, min_time;
\r
802 PiffDemuxStream *stream;
\r
803 PiffDemuxStream *str = piffdemux->stream;
\r
805 gboolean set_sample;
\r
816 i = str->n_samples - 1;
\r
820 for (; (i >= 0) && (i < str->n_samples); i += inc) {
\r
821 if (str->samples[i].size &&
\r
822 ((fw && (str->samples[i].offset >= byte_pos)) ||
\r
824 (str->samples[i].offset + str->samples[i].size <=
\r
826 /* move stream to first available sample */
\r
828 gst_piffdemux_move_stream (piffdemux, str, i);
\r
831 /* determine min/max time */
\r
832 time = str->samples[i].timestamp + str->samples[i].pts_offset;
\r
833 time = gst_util_uint64_scale (time, GST_SECOND, str->timescale);
\r
834 if (min_time == -1 || (!fw && time > min_time) ||
\r
835 (fw && time < min_time)) {
\r
842 /* no sample for this stream, mark eos */
\r
844 gst_piffdemux_move_stream (piffdemux, str, str->n_samples);
\r
856 gst_piffdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
\r
858 GstPiffDemux *demux = GST_PIFFDEMUX (GST_PAD_PARENT (sinkpad));
\r
861 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
\r
863 switch (GST_EVENT_TYPE (event)) {
\r
864 case GST_EVENT_NEWSEGMENT:
\r
867 gdouble rate, arate;
\r
868 gint64 start, stop, time, offset = 0;
\r
869 PiffDemuxStream *stream;
\r
872 GstSegment segment;
\r
874 /* some debug output */
\r
875 gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
\r
876 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
\r
877 &start, &stop, &time);
\r
878 gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
\r
879 start, stop, time);
\r
880 GST_ERROR_OBJECT (demux,
\r
881 "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
\r
884 /* chain will send initial newsegment after pads have been added */
\r
885 if (demux->state != PIFFDEMUX_STATE_MOVIE ) {
\r
886 GST_DEBUG_OBJECT (demux, "still starting, eating event");
\r
890 /* we only expect a BYTE segment, e.g. following a seek */
\r
891 if (format == GST_FORMAT_BYTES) {
\r
893 gint64 requested_seek_time;
\r
894 guint64 seek_offset;
\r
898 GST_OBJECT_LOCK (demux);
\r
899 requested_seek_time = demux->requested_seek_time;
\r
900 seek_offset = demux->seek_offset;
\r
901 demux->requested_seek_time = -1;
\r
902 demux->seek_offset = -1;
\r
903 GST_OBJECT_UNLOCK (demux);
\r
905 if (offset == seek_offset) {
\r
906 start = requested_seek_time;
\r
908 gst_piffdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL,
\r
910 start = MAX (start, 0);
\r
914 gst_piffdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL,
\r
916 /* keyframe seeking should already arrange for start >= stop,
\r
917 * but make sure in other rare cases */
\r
918 stop = MAX (stop, start);
\r
922 else if (format == GST_FORMAT_TIME) {
\r
923 // Supporting TIME_FORMAT for new_segment
\r
924 //gst_piffdemux_push_event (demux,event);
\r
925 PiffDemuxStream *stream = NULL;
\r
928 demux->neededbytes = 16;
\r
929 demux->state = PIFFDEMUX_STATE_INITIAL;
\r
932 /* Figure out which stream this is packet belongs to */
\r
933 for (i = 0; i < demux->n_streams; i++) {
\r
934 stream = demux->streams[i];
\r
935 stream->last_ts = start;
\r
936 stream->discont = TRUE;
\r
937 stream->sample_index = stream->n_samples;
\r
940 /* accept upstream's notion of segment and distribute along */
\r
941 gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
\r
942 GST_FORMAT_TIME, start, stop, start);
\r
943 GST_ERROR_OBJECT (demux, "Pushing newseg update %d, rate %g, "
\r
944 "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
\r
945 "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
\r
946 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
\r
948 gst_piffdemux_push_event (demux,
\r
949 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, start, stop, start));
\r
951 /* clear leftover in current segment, if any */
\r
952 gst_adapter_clear (demux->adapter);
\r
958 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
\r
962 /* accept upstream's notion of segment and distribute along */
\r
963 gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
\r
964 GST_FORMAT_TIME, start, stop, start);
\r
965 GST_ERROR_OBJECT (demux, "Pushing newseg update %d, rate %g, "
\r
966 "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
\r
967 "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
\r
968 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
\r
970 gst_piffdemux_push_event (demux,
\r
971 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
\r
972 start, stop, start));
\r
974 /* clear leftover in current segment, if any */
\r
975 gst_adapter_clear (demux->adapter);
\r
976 /* set up streaming thread */
\r
977 gst_piffdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx, NULL);
\r
978 demux->offset = offset;
\r
980 demux->todrop = stream->samples[idx].offset - offset;
\r
981 demux->neededbytes = demux->todrop + stream->samples[idx].size;
\r
983 /* set up for EOS */
\r
984 demux->neededbytes = -1;
\r
988 gst_event_unref (event);
\r
993 case GST_EVENT_FLUSH_STOP:
\r
995 /* clean up, force EOS if no more info follows */
\r
996 gst_adapter_clear (demux->adapter);
\r
998 demux->neededbytes = -1;
\r
999 /* reset flow return, e.g. following seek */
\r
1000 demux->stream->last_ret = GST_FLOW_OK;
\r
1001 demux->stream->sent_eos = FALSE;
\r
1004 case GST_EVENT_EOS:
\r
1010 res = gst_pad_event_default (demux->sinkpad, event);
\r
1018 gst_piffdemux_stream_free (GstPiffDemux * piffdemux, PiffDemuxStream * stream)
\r
1020 while (stream->buffers) {
\r
1021 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
\r
1022 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
\r
1024 g_free (stream->samples);
\r
1026 gst_caps_unref (stream->caps);
\r
1027 g_free (stream->segments);
\r
1028 if (stream->pending_tags)
\r
1029 gst_tag_list_free (stream->pending_tags);
\r
1034 static GstStateChangeReturn
\r
1035 gst_piffdemux_change_state (GstElement * element, GstStateChange transition)
\r
1037 GstPiffDemux *piffdemux = GST_PIFFDEMUX (element);
\r
1038 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
\r
1040 switch (transition) {
\r
1041 case GST_STATE_CHANGE_PAUSED_TO_READY:
\r
1047 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
\r
1049 switch (transition) {
\r
1050 case GST_STATE_CHANGE_PAUSED_TO_READY:{
\r
1051 piffdemux->state = PIFFDEMUX_STATE_INITIAL;
\r
1052 piffdemux->neededbytes = 16;
\r
1053 piffdemux->todrop = 0;
\r
1054 piffdemux->posted_redirect = FALSE;
\r
1055 piffdemux->offset = 0;
\r
1056 piffdemux->first_mdat = -1;
\r
1057 piffdemux->mdatoffset = GST_CLOCK_TIME_NONE;
\r
1058 if (piffdemux->mdatbuffer)
\r
1059 gst_buffer_unref (piffdemux->mdatbuffer);
\r
1060 piffdemux->mdatbuffer = NULL;
\r
1061 if (piffdemux->tag_list)
\r
1062 gst_tag_list_free (piffdemux->tag_list);
\r
1063 piffdemux->tag_list = NULL;
\r
1064 gst_adapter_clear (piffdemux->adapter);
\r
1065 gst_segment_init (&piffdemux->segment, GST_FORMAT_TIME);
\r
1076 piffdemux_post_global_tags (GstPiffDemux * piffdemux)
\r
1078 if (piffdemux->tag_list) {
\r
1079 /* all header tags ready and parsed, push them */
\r
1080 GST_INFO_OBJECT (piffdemux, "posting global tags: %" GST_PTR_FORMAT,
\r
1081 piffdemux->tag_list);
\r
1082 /* post now, send event on pads later */
\r
1083 gst_element_post_message (GST_ELEMENT (piffdemux),
\r
1084 gst_message_new_tag (GST_OBJECT (piffdemux),
\r
1085 gst_tag_list_copy (piffdemux->tag_list)));
\r
1090 /* caller verifies at least 8 bytes in buf */
\r
1092 extract_initial_length_and_fourcc (const guint8 * data, guint size,
\r
1093 guint64 * plength, guint32 * pfourcc)
\r
1098 length = PIFF_UINT32 (data);
\r
1099 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
\r
1100 fourcc = PIFF_FOURCC (data + 4);
\r
1101 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
\r
1103 if (length == 0) {
\r
1104 length = G_MAXUINT32;
\r
1105 } else if (length == 1 && size >= 16) {
\r
1106 /* this means we have an extended size, which is the 64 bit value of
\r
1107 * the next 8 bytes */
\r
1108 length = PIFF_UINT64 (data + 8);
\r
1109 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
\r
1113 *plength = length;
\r
1115 *pfourcc = fourcc;
\r
1119 piffdemux_update_sample_offset (GstPiffDemux * piffdemu, PiffDemuxStream * stream, gint64 uuid_offset)
\r
1121 PiffDemuxSample *sample;
\r
1124 sample = stream->samples ;
\r
1125 for (i = 0; i < stream->n_samples; i++)
\r
1127 sample->offset = sample->offset + uuid_offset;
\r
1133 static uuid_type_t
\r
1134 piffdemux_get_uuid_type(GstPiffDemux * piffdemux, GstByteReader *uuid_data, gint64 *uuid_offset)
\r
1136 uuid_type_t uuid_type = UUID_UNKNOWN;
\r
1137 guint32 box_len = 0;
\r
1138 guint64 box_long_len = 0;
\r
1139 gchar uuid[16] = {0,};
\r
1142 if (!gst_byte_reader_get_uint32_be (uuid_data, &box_len))
\r
1143 goto invalid_uuid;
\r
1145 /* Skipping fourcc */
\r
1146 if (!gst_byte_reader_skip (uuid_data, 4))
\r
1147 goto invalid_uuid;
\r
1151 GST_WARNING ("TfxdBoxLongLength field is present...");
\r
1152 if (!gst_byte_reader_get_uint64_be (uuid_data, &box_long_len))
\r
1153 goto invalid_uuid;
\r
1154 GST_DEBUG ("tfxd long length = %llu", box_long_len);
\r
1156 *uuid_offset = box_long_len;
\r
1160 GST_DEBUG ("Box Len = %d", box_len);
\r
1161 *uuid_offset = box_len;
\r
1164 //g_print ("\n\n\n 0x");
\r
1165 for (i = 0; i < sizeof (uuid); i++)
\r
1167 if (!gst_byte_reader_get_uint8 (uuid_data, &(uuid[i])))
\r
1168 goto invalid_uuid;
\r
1169 //g_print ("%02x", uuid[i]);
\r
1171 //g_print ("\n\n\n");
\r
1173 if (!memcmp(uuid, tfxd_uuid, sizeof (uuid_t)))
\r
1175 GST_INFO ("Found TFXD box");
\r
1178 else if (!memcmp(uuid, tfrf_uuid, sizeof (uuid_t)))
\r
1180 GST_INFO ("Found TFRF box");
\r
1183 else if (!memcmp(uuid, encrypt_uuid, sizeof (uuid_t)))
\r
1185 GST_INFO ("Found sample encryption box");
\r
1186 return UUID_SAMPLE_ENCRYPT;
\r
1190 GST_WARNING ("Not an valid UUID box..");
\r
1191 goto invalid_uuid;
\r
1196 GST_ERROR ("Error in parsing UUID atom...");
\r
1197 return UUID_UNKNOWN;
\r
1201 piffdemux_parse_sample_encryption(GstPiffDemux * piffdemux, GstByteReader *sample_encrypt, PiffDemuxStream * stream)
\r
1203 guint32 flags = 0;
\r
1204 guint32 sample_count = 0;
\r
1207 guint8 iv_size = 0;
\r
1209 if (!gst_byte_reader_skip (sample_encrypt, 1) ||
\r
1210 !gst_byte_reader_get_uint24_be (sample_encrypt, &flags))
\r
1211 goto invalid_encryption;
\r
1213 if (flags & SE_OVERRIDE_TE_FLAGS) {
\r
1214 /* get algorithm id */
\r
1215 if (!gst_byte_reader_get_uint32_be (sample_encrypt, &algo_id))
\r
1216 goto invalid_encryption;
\r
1219 if (!gst_byte_reader_get_uint8 (sample_encrypt, &iv_size))
\r
1220 goto invalid_encryption;
\r
1222 // TODO: need to add reading of KID
\r
1224 GST_INFO_OBJECT (piffdemux, "Override flags are not present... taking default IV_Size = 8");
\r
1228 /* Get sample count*/
\r
1229 if (!gst_byte_reader_get_uint32_be (sample_encrypt, &sample_count))
\r
1230 goto invalid_encryption;
\r
1232 GST_INFO_OBJECT (piffdemux, "Sample count = %d", sample_count);
\r
1234 if (sample_count != stream->n_samples) {
\r
1235 GST_ERROR_OBJECT (piffdemux, "Not all samples has IV vectors... Don't know how to handle. sample_cnt = %d and stream->n_samples = %d",
\r
1236 sample_count, stream->n_samples);
\r
1237 goto invalid_encryption;
\r
1240 for (i = 0; i < stream->n_samples; i++) {
\r
1241 guint8 iv_idx = iv_size;
\r
1243 /* resetting entire IV array */
\r
1244 stream->samples[i].iv = (guint8 *)malloc (iv_size);
\r
1245 if (NULL == stream->samples[i].iv) {
\r
1246 GST_ERROR ("Failed to allocate memory...\n");
\r
1247 goto invalid_encryption;
\r
1250 memset (stream->samples[i].iv, 0x00, iv_size);
\r
1253 while (iv_idx < iv_size) {
\r
1255 if (!gst_byte_reader_get_uint8 (sample_encrypt, &(stream->samples[i].iv[iv_idx])))
\r
1256 goto invalid_encryption;
\r
1263 guint8 tmp_idx = 0;
\r
1264 g_print ("sample[%d] : 0x ", i);
\r
1266 while (tmp_idx < iv_size ) {
\r
1267 g_print ("%02x ", stream->samples[i].iv[tmp_idx]);
\r
1274 if (flags & SE_USE_SUBSAMPLE_ENCRYPTION) {
\r
1275 guint16 n_entries;
\r
1278 /* NumberofEntries in SubSampleEncryption */
\r
1279 if (!gst_byte_reader_get_uint16_be (sample_encrypt, &n_entries))
\r
1280 goto invalid_encryption;
\r
1282 stream->samples[i].sub_encry = (PiffDemuxSubSampleEncryption *)malloc (sizeof (PiffDemuxSubSampleEncryption));
\r
1283 if (NULL == stream->samples[i].sub_encry) {
\r
1284 GST_ERROR ("Failed to allocate memory...\n");
\r
1285 goto invalid_encryption;
\r
1288 stream->samples[i].sub_encry->sub_entry = g_try_new0 (PiffDemuxSubSampleEntryInfo, n_entries);
\r
1289 if (NULL == stream->samples[i].sub_encry->sub_entry)
\r
1291 GST_ERROR_OBJECT (piffdemux, "Failed to allocate memory...");
\r
1292 goto invalid_encryption;
\r
1295 stream->samples[i].sub_encry->n_entries = n_entries;
\r
1297 GST_DEBUG_OBJECT (piffdemux,"No. of subsample entries = %d", stream->samples[i].sub_encry->n_entries);
\r
1299 for (n_idx = 0; n_idx < n_entries; n_idx++) {
\r
1300 if (!gst_byte_reader_get_uint16_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData)))
\r
1301 goto invalid_encryption;
\r
1303 GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofClearData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData);
\r
1305 if (!gst_byte_reader_get_uint32_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData)))
\r
1306 goto invalid_encryption;
\r
1308 GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofEncryptData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData);
\r
1315 invalid_encryption:
\r
1317 GST_WARNING_OBJECT (piffdemux, "invalid sample encryption header");
\r
1324 piffdemux_parse_trun (GstPiffDemux * piffdemux, GstByteReader * trun,
\r
1325 PiffDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
\r
1326 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
\r
1327 gint64 * base_offset, gint64 * running_offset)
\r
1329 guint64 timestamp;
\r
1330 gint32 data_offset = 0;
\r
1331 guint32 flags = 0, first_flags = 0, samples_count = 0;
\r
1334 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
\r
1335 PiffDemuxSample *sample;
\r
1336 gboolean ismv = FALSE;
\r
1337 guint64 total_duration = 0;
\r
1339 GST_LOG_OBJECT (piffdemux, "parsing trun stream ; "
\r
1340 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT,
\r
1341 d_sample_duration, d_sample_size, d_sample_flags,
\r
1344 //Resetting the samples
\r
1345 stream->n_samples = 0;
\r
1347 if (!gst_byte_reader_skip (trun, 1) ||
\r
1348 !gst_byte_reader_get_uint24_be (trun, &flags))
\r
1351 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
\r
1354 if (flags & TR_DATA_OFFSET) {
\r
1355 /* note this is really signed */
\r
1356 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
\r
1358 GST_LOG_OBJECT (piffdemux, "trun data offset %d", data_offset);
\r
1359 /* default base offset = first byte of moof */
\r
1360 if (*base_offset == -1) {
\r
1361 GST_LOG_OBJECT (piffdemux, "base_offset at moof and moof_offset = %"G_GINT64_FORMAT, moof_offset);
\r
1362 *base_offset = moof_offset;
\r
1364 *running_offset = *base_offset + data_offset;
\r
1366 /* if no offset at all, that would mean data starts at moof start,
\r
1367 * which is a bit wrong and is ismv crappy way, so compensate
\r
1368 * assuming data is in mdat following moof */
\r
1369 if (*base_offset == -1) {
\r
1370 *base_offset = moof_offset + moof_length + 8;
\r
1371 GST_LOG_OBJECT (piffdemux, "base_offset assumed in mdat after moof");
\r
1374 if (*running_offset == -1)
\r
1375 *running_offset = *base_offset;
\r
1378 GST_LOG_OBJECT (piffdemux, "running offset now %" G_GINT64_FORMAT,
\r
1380 GST_LOG_OBJECT (piffdemux, "trun offset %d, flags 0x%x, entries %d",
\r
1381 data_offset, flags, samples_count);
\r
1383 if (flags & TR_FIRST_SAMPLE_FLAGS) {
\r
1384 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
\r
1385 GST_DEBUG_OBJECT (piffdemux,
\r
1386 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
\r
1387 flags ^= TR_FIRST_SAMPLE_FLAGS;
\r
1389 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
\r
1391 GST_LOG_OBJECT (piffdemux, "first flags: 0x%x", first_flags);
\r
1395 /* FIXME ? spec says other bits should also be checked to determine
\r
1396 * entry size (and prefix size for that matter) */
\r
1398 dur_offset = size_offset = 0;
\r
1399 if (flags & TR_SAMPLE_DURATION) {
\r
1400 GST_LOG_OBJECT (piffdemux, "entry duration present");
\r
1401 dur_offset = entry_size;
\r
1404 if (flags & TR_SAMPLE_SIZE) {
\r
1405 GST_LOG_OBJECT (piffdemux, "entry size present");
\r
1406 size_offset = entry_size;
\r
1409 if (flags & TR_SAMPLE_FLAGS) {
\r
1410 GST_LOG_OBJECT (piffdemux, "entry flags present");
\r
1411 flags_offset = entry_size;
\r
1414 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
\r
1415 GST_LOG_OBJECT (piffdemux, "entry ct offset present");
\r
1416 ct_offset = entry_size;
\r
1420 if (!piff_atom_parser_has_chunks (trun, samples_count, entry_size))
\r
1422 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
\r
1424 if (stream->n_samples >=
\r
1425 PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (PiffDemuxSample))
\r
1426 goto index_too_big;
\r
1428 GST_DEBUG_OBJECT (piffdemux, "allocating n_samples %u * %u (%.2f MB)",
\r
1429 stream->n_samples, (guint) sizeof (PiffDemuxSample),
\r
1430 stream->n_samples * sizeof (PiffDemuxSample) / (1024.0 * 1024.0));
\r
1432 /* create a new array of samples if it's the first sample parsed */
\r
1433 if (stream->n_samples == 0)
\r
1434 stream->samples = g_try_new0 (PiffDemuxSample, samples_count);
\r
1435 /* or try to reallocate it with space enough to insert the new samples */
\r
1437 stream->samples = g_try_renew (PiffDemuxSample, stream->samples,
\r
1438 stream->n_samples + samples_count);
\r
1439 if (stream->samples == NULL)
\r
1440 goto out_of_memory;
\r
1442 if (G_UNLIKELY (stream->n_samples == 0)) {
\r
1443 /* the timestamp of the first sample is also provided by the tfra entry
\r
1444 * but we shouldn't rely on it as it is at the end of files */
\r
1447 /* subsequent fragments extend stream */
\r
1449 stream->samples[stream->n_samples - 1].timestamp +
\r
1450 stream->samples[stream->n_samples - 1].duration;
\r
1452 sample = stream->samples + stream->n_samples;
\r
1453 for (i = 0; i < samples_count; i++) {
\r
1454 guint32 dur, size, sflags, ct;
\r
1456 /* first read sample data */
\r
1457 if (flags & TR_SAMPLE_DURATION) {
\r
1458 dur = PIFF_UINT32 (data + dur_offset);
\r
1460 dur = d_sample_duration;
\r
1462 if (flags & TR_SAMPLE_SIZE) {
\r
1463 size = PIFF_UINT32 (data + size_offset);
\r
1465 size = d_sample_size;
\r
1468 GST_DEBUG_OBJECT(piffdemux,"Size of sample %d is %d", i, size);
\r
1470 if (flags & TR_FIRST_SAMPLE_FLAGS) {
\r
1472 sflags = first_flags;
\r
1474 sflags = d_sample_flags;
\r
1476 } else if (flags & TR_SAMPLE_FLAGS) {
\r
1477 sflags = PIFF_UINT32 (data + flags_offset);
\r
1479 sflags = d_sample_flags;
\r
1481 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
\r
1482 ct = PIFF_UINT32 (data + ct_offset);
\r
1486 data += entry_size;
\r
1488 /* fill the sample information */
\r
1489 sample->offset = *running_offset;
\r
1490 sample->pts_offset = ct;
\r
1491 sample->size = size;
\r
1492 sample->timestamp = timestamp;
\r
1493 sample->duration = dur;
\r
1494 /* sample-is-difference-sample */
\r
1495 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
\r
1496 * now idea how it relates to bitfield other than massive LE/BE confusion */
\r
1497 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
\r
1498 sample->iv = NULL;
\r
1499 sample->sub_encry = NULL;
\r
1501 stream->samples[i] = *sample;
\r
1503 *running_offset += size;
\r
1507 /* calculate total duration of the present fragment */
\r
1508 total_duration += gst_util_uint64_scale (dur, GST_SECOND, stream->timescale);
\r
1511 stream->sample_index = 0;
\r
1513 stream->n_samples += samples_count;
\r
1515 /* calculate avg fps based on avg frame duration */
\r
1516 stream->avg_dur = total_duration/samples_count;
\r
1517 g_print ("total dur = %"GST_TIME_FORMAT", avg_dur = %"GST_TIME_FORMAT"count = %d\n",
\r
1518 GST_TIME_ARGS(total_duration), GST_TIME_ARGS(stream->avg_dur), samples_count);
\r
1524 GST_WARNING_OBJECT (piffdemux, "failed to parse trun");
\r
1529 GST_WARNING_OBJECT (piffdemux, "failed to allocate %d samples",
\r
1530 stream->n_samples);
\r
1535 GST_WARNING_OBJECT (piffdemux, "not allocating index of %d samples, would "
\r
1536 "be larger than %uMB (broken file?)", stream->n_samples,
\r
1537 PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
\r
1543 piffdemux_parse_mfhd (GstPiffDemux * piffdemux, GstByteReader * mfhd)
\r
1545 guint32 seq_num = 0;
\r
1547 if (!gst_byte_reader_skip (mfhd, 4))
\r
1548 goto invalid_mfhd;
\r
1550 if (!gst_byte_reader_get_uint32_be (mfhd, &seq_num))
\r
1551 goto invalid_mfhd;
\r
1553 GST_DEBUG_OBJECT (piffdemux, "sequence number present in mfhd = %d", seq_num);
\r
1559 GST_WARNING_OBJECT (piffdemux, "invalid movie fragment header");
\r
1566 piffdemux_parse_tfhd (GstPiffDemux * piffdemux, GstByteReader * tfhd,
\r
1567 guint32 * default_sample_duration,
\r
1568 guint32 * default_sample_size, guint32 * default_sample_flags,
\r
1569 gint64 * base_offset)
\r
1571 guint32 flags = 0;
\r
1572 guint32 track_id = 0;
\r
1574 if (!gst_byte_reader_skip (tfhd, 1) ||
\r
1575 !gst_byte_reader_get_uint24_be (tfhd, &flags))
\r
1576 goto invalid_track;
\r
1578 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
\r
1579 goto invalid_track;
\r
1581 GST_DEBUG_OBJECT (piffdemux, "trackID = %d", track_id);
\r
1583 if (flags & TF_BASE_DATA_OFFSET) {
\r
1584 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
\r
1585 goto invalid_track;
\r
1586 GST_DEBUG ("BaseData Offset = %"G_GUINT64_FORMAT, base_offset);
\r
1589 /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
\r
1590 if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
\r
1591 if (!gst_byte_reader_skip (tfhd, 4))
\r
1592 goto invalid_track;
\r
1594 if (flags & TF_DEFAULT_SAMPLE_DURATION)
\r
1595 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
\r
1596 goto invalid_track;
\r
1598 if (flags & TF_DEFAULT_SAMPLE_SIZE)
\r
1599 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
\r
1600 goto invalid_track;
\r
1602 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
\r
1603 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
\r
1604 goto invalid_track;
\r
1610 GST_WARNING_OBJECT (piffdemux, "invalid track fragment header");
\r
1616 piffdemux_parse_tfxd (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfxd)
\r
1618 guint8 version = 0;
\r
1620 // TODO: In my opinion, tfxd will be mainly useful when lookahead count = 0. In this case, based on this duration, next fragment timstamp can be calculted.. Need to test this using our server
\r
1622 if (!gst_byte_reader_get_uint8 (tfxd, &version))
\r
1623 goto invalid_tfxd;
\r
1625 if (!gst_byte_reader_skip (tfxd, 3))
\r
1626 goto invalid_tfxd;
\r
1628 if (!piffdemux->lookahead_cnt) {
\r
1629 piffdemux->param = (piff_live_param_t *)malloc (sizeof (piff_live_param_t));
\r
1630 if (NULL == piffdemux->param) {
\r
1631 GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
\r
1634 piffdemux->param->count = 1;
\r
1635 piffdemux->param->long_info = NULL;
\r
1636 piffdemux->param->info = NULL;
\r
1637 piffdemux->param->is_eos = FALSE;
\r
1639 // TODO: presentation will be ended based on timeout in souphttpsrc in lookaheadcnt = 0 case
\r
1642 if (version == 1) {
\r
1643 guint64 duration = 0;
\r
1644 guint64 timestamp = 0;
\r
1646 GST_LOG_OBJECT (piffdemux, "Time and Duration are in 64-bit format...");
\r
1647 if (!gst_byte_reader_get_uint64_be (tfxd, ×tamp))
\r
1648 goto invalid_tfxd;
\r
1649 if (!gst_byte_reader_get_uint64_be (tfxd, &duration))
\r
1650 goto invalid_tfxd;
\r
1652 GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT64_FORMAT" and duration of fragment = %"G_GUINT64_FORMAT,
\r
1653 timestamp, duration);
\r
1655 if (!piffdemux->lookahead_cnt) {
\r
1656 piffdemux->param->long_info = (piff_fragment_longtime_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_longtime_info));
\r
1657 if (NULL == piffdemux->param->long_info) {
\r
1658 GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
\r
1662 /* Calculate next fragment's timestamp using current fragment's timestamp + duration */
\r
1663 piffdemux->param->long_info->duration = GST_CLOCK_TIME_NONE;
\r
1664 piffdemux->param->long_info->ts = timestamp +duration;
\r
1666 } else if (version == 0) {
\r
1667 guint32 duration = 0;
\r
1668 guint32 timestamp = 0;
\r
1669 GST_LOG_OBJECT (piffdemux, "Time and Duration are in 32-bit format...");
\r
1671 if (!gst_byte_reader_get_uint32_be (tfxd, ×tamp))
\r
1672 goto invalid_tfxd;
\r
1674 if (!gst_byte_reader_get_uint32_be (tfxd, &duration))
\r
1675 goto invalid_tfxd;
\r
1677 GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT32_FORMAT" and duration of fragment = %"G_GUINT32_FORMAT,
\r
1678 timestamp, duration);
\r
1680 if (!piffdemux->lookahead_cnt) {
\r
1681 piffdemux->param->info = (piff_fragment_time_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_time_info));
\r
1682 if (NULL == piffdemux->param->info) {
\r
1683 GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
\r
1686 /* Calculate next fragment's timestamp using current fragment's timestamp + duration */
\r
1687 piffdemux->param->info->duration = GST_CLOCK_TIME_NONE;
\r
1688 piffdemux->param->info->ts = timestamp +duration;
\r
1691 GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfxd...");
\r
1695 if (!piffdemux->lookahead_cnt) {
\r
1696 GST_DEBUG_OBJECT (piffdemux, "Emitting live-param signal...");
\r
1697 g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);
\r
1703 GST_ERROR ("Invalid TFXD atom...");
\r
1709 piffdemux_parse_tfrf (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfrf)
\r
1711 guint8 version = 0;
\r
1712 guint8 frag_cnt = 0;
\r
1715 /* Getting version info */
\r
1716 if (!gst_byte_reader_get_uint8 (tfrf, &version))
\r
1717 goto invalid_tfrf;
\r
1719 /* skipping reserved flags */
\r
1720 if (!gst_byte_reader_skip (tfrf, 3))
\r
1721 goto invalid_tfrf;
\r
1723 if (!gst_byte_reader_get_uint8 (tfrf, &frag_cnt))
\r
1724 goto invalid_tfrf;
\r
1726 GST_INFO_OBJECT (piffdemux, "Subsequent fragments info count = %d", frag_cnt);
\r
1728 piffdemux->param = (piff_live_param_t *)malloc(sizeof (piff_live_param_t));
\r
1729 if (NULL == piffdemux->param) {
\r
1730 GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
\r
1734 piffdemux->param->count = frag_cnt;
\r
1735 piffdemux->param->long_info = NULL;
\r
1736 piffdemux->param->info = NULL;
\r
1737 piffdemux->param->is_eos = FALSE;
\r
1739 // TODO: Duration and timestamp values need to be posted to msl using g_signal_emit
\r
1741 if (version == 1) {
\r
1742 guint64 duration = 0;
\r
1743 guint64 timestamp = 0;
\r
1744 GST_LOG_OBJECT (piffdemux, "Time and Duration are in 64-bit format...");
\r
1746 piffdemux->param->long_info = (piff_fragment_longtime_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_longtime_info));
\r
1747 if (NULL == piffdemux->param->long_info) {
\r
1748 GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
\r
1752 for (i = 0; i < frag_cnt; i++) {
\r
1753 if (!gst_byte_reader_get_uint64_be (tfrf, ×tamp))
\r
1754 goto invalid_tfrf;
\r
1755 if (!gst_byte_reader_get_uint64_be (tfrf, &duration))
\r
1756 goto invalid_tfrf;
\r
1757 GST_DEBUG_OBJECT (piffdemux, "tfrf long: absolute timestamp = %"G_GUINT64_FORMAT" and duration of fragment = %"G_GUINT64_FORMAT"\n",
\r
1758 timestamp, duration);
\r
1759 (piffdemux->param->long_info[i]).ts = timestamp;
\r
1760 (piffdemux->param->long_info[i]).duration = duration;
\r
1762 } else if (version == 0) {
\r
1763 guint32 duration = 0;
\r
1764 guint32 timestamp = 0;
\r
1765 GST_LOG_OBJECT (piffdemux, "Time and Duration are in 32-bit format...");
\r
1767 piffdemux->param->info = (piff_fragment_time_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_time_info));
\r
1768 if (NULL == piffdemux->param->info) {
\r
1769 GST_ERROR ("Memory not available...\n");
\r
1773 for (i = 0; i < frag_cnt; i++) {
\r
1774 if (!gst_byte_reader_get_uint32_be (tfrf, ×tamp))
\r
1775 goto invalid_tfrf;
\r
1776 if (!gst_byte_reader_get_uint32_be (tfrf, &duration))
\r
1777 goto invalid_tfrf;
\r
1779 GST_DEBUG_OBJECT (piffdemux, "tfrf int: absolute timestamp = %"G_GUINT32_FORMAT" and duration of fragment = %"G_GUINT32_FORMAT,
\r
1780 timestamp, duration);
\r
1781 (piffdemux->param->info[i]).ts = timestamp;
\r
1782 (piffdemux->param->info[i]).duration = duration;
\r
1785 GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfrf...");
\r
1789 g_print ("Signalling from TFRF box..\n");
\r
1790 g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);
\r
1795 GST_ERROR_OBJECT (piffdemux, "Invalid TFRF atom...");
\r
1801 piffdemux_parse_moof (GstPiffDemux * piffdemux, const guint8 * buffer, guint length,
\r
1802 guint64 moof_offset, PiffDemuxStream * stream)
\r
1804 GNode *moof_node, *mfhd_node, *traf_node, *tfhd_node, *trun_node, *uuid_node;
\r
1805 GstByteReader mfhd_data, trun_data, tfhd_data, uuid_data;
\r
1806 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
\r
1807 gint64 base_offset, running_offset;
\r
1808 gint64 uuid_offset = 0;
\r
1809 gboolean found_tfxd = FALSE;
\r
1810 gboolean found_tfrf = FALSE;
\r
1812 /* NOTE @stream ignored */
\r
1814 moof_node = g_node_new ((guint8 *) buffer);
\r
1815 piffdemux_parse_node (piffdemux, moof_node, buffer, length);
\r
1816 //piffdemux_node_dump (piffdemux, moof_node);
\r
1818 /* unknown base_offset to start with */
\r
1819 base_offset = running_offset = -1;
\r
1821 mfhd_node = piffdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
\r
1823 goto missing_mfhd;
\r
1825 if (!piffdemux_parse_mfhd (piffdemux, &mfhd_data))
\r
1826 goto missing_mfhd;
\r
1828 traf_node = piffdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
\r
1829 while (traf_node) {
\r
1830 /* Fragment Header node */
\r
1832 piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
\r
1835 goto missing_tfhd;
\r
1836 if (!piffdemux_parse_tfhd (piffdemux, &tfhd_data, &ds_duration,
\r
1837 &ds_size, &ds_flags, &base_offset))
\r
1838 goto missing_tfhd;
\r
1840 if (G_UNLIKELY (base_offset < -1))
\r
1843 /* Track Run node */
\r
1845 piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
\r
1847 while (trun_node) {
\r
1848 piffdemux_parse_trun (piffdemux, &trun_data, stream,
\r
1849 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
\r
1851 /* iterate all siblings */
\r
1852 trun_node = piffdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
\r
1856 uuid_node = piffdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
\r
1857 while (uuid_node) {
\r
1858 uuid_type_t uuid_type;
\r
1859 guint8 *buffer = (guint8 *) uuid_node->data;
\r
1861 gst_byte_reader_init (&uuid_data, buffer, PIFF_UINT32 (buffer));
\r
1863 uuid_type = piffdemux_get_uuid_type (piffdemux, &uuid_data, &uuid_offset);
\r
1865 if ((UUID_TFXD == uuid_type) && piffdemux->is_live) {
\r
1866 // TODO: Dont know, why we should not consider tfxd offset...if we use tfxd offset also, not working.. PIFF doc does not say anything :(
\r
1867 found_tfxd = TRUE;
\r
1868 if (!piffdemux_parse_tfxd (piffdemux, stream, &uuid_data))
\r
1870 } else if ((UUID_TFRF == uuid_type) && piffdemux->is_live && piffdemux->lookahead_cnt) {
\r
1871 found_tfrf = TRUE;
\r
1872 if (!piffdemux_parse_tfrf (piffdemux, stream, &uuid_data))
\r
1874 piffdemux_update_sample_offset (piffdemux, stream, uuid_offset);
\r
1875 running_offset += uuid_offset;
\r
1876 } else if (UUID_SAMPLE_ENCRYPT == uuid_type) {
\r
1877 if (!piffdemux_parse_sample_encryption (piffdemux, &uuid_data, stream))
\r
1880 GST_WARNING_OBJECT (piffdemux, "Ignoring Wrong UUID...");
\r
1883 /* iterate all siblings */
\r
1884 uuid_node = piffdemux_tree_get_sibling_by_type (uuid_node, FOURCC_uuid);
\r
1887 if (piffdemux->is_live) {
\r
1888 if (!found_tfxd) {
\r
1889 GST_ERROR_OBJECT (piffdemux, "TFXD box is not present for live stream");
\r
1893 if (!found_tfrf && piffdemux->lookahead_cnt) {
\r
1894 /* when lookahead count is non-zero in manifest & if tfrf box is not present., means EOS */
\r
1895 GST_INFO_OBJECT (piffdemux, "Reached Endof Live presentation..");
\r
1897 piffdemux->param = (piff_live_param_t *)malloc (sizeof (piff_live_param_t));
\r
1898 if (NULL == piffdemux->param) {
\r
1899 GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
\r
1902 piffdemux->param->count = 0;
\r
1903 piffdemux->param->long_info = NULL;
\r
1904 piffdemux->param->info = NULL;
\r
1905 piffdemux->param->is_eos = TRUE; /* marking EOS */
\r
1906 g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);
\r
1910 /* if no new base_offset provided for next traf,
\r
1911 * base is end of current traf */
\r
1912 base_offset = running_offset;
\r
1913 running_offset = -1;
\r
1915 /* iterate all siblings */
\r
1916 traf_node = piffdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
\r
1918 g_node_destroy (moof_node);
\r
1923 GST_DEBUG_OBJECT (piffdemux, "missing mfhd box");
\r
1929 GST_DEBUG_OBJECT (piffdemux, "missing tfhd box");
\r
1934 GST_DEBUG_OBJECT (piffdemux, "lost offset");
\r
1939 g_node_destroy (moof_node);
\r
1941 GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
\r
1942 ("This file is corrupt and cannot be played."), (NULL));
\r
1949 /* activate the given segment number @seg_idx of @stream at time @offset.
\r
1950 * @offset is an absolute global position over all the segments.
\r
1952 * This will push out a NEWSEGMENT event with the right values and
\r
1953 * position the stream index to the first decodable sample before
\r
1957 gst_piffdemux_activate_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,
\r
1958 guint32 seg_idx, guint64 offset)
\r
1961 PiffDemuxSegment *segment;
\r
1963 guint64 start, stop, time;
\r
1966 GST_LOG_OBJECT (piffdemux, "activate segment %d, offset %" G_GUINT64_FORMAT,
\r
1969 /* update the current segment */
\r
1970 stream->segment_index = seg_idx;
\r
1972 /* get the segment */
\r
1973 segment = &stream->segments[seg_idx];
\r
1975 if (G_UNLIKELY (offset < segment->time)) {
\r
1976 GST_WARNING_OBJECT (piffdemux, "offset < segment->time %" G_GUINT64_FORMAT,
\r
1981 /* segment lies beyond total indicated duration */
\r
1982 if (G_UNLIKELY (piffdemux->segment.duration != -1 &&
\r
1983 segment->time > piffdemux->segment.duration)) {
\r
1984 GST_WARNING_OBJECT (piffdemux, "file duration %" G_GINT64_FORMAT
\r
1985 " < segment->time %" G_GUINT64_FORMAT, piffdemux->segment.duration,
\r
1990 /* get time in this segment */
\r
1991 seg_time = offset - segment->time;
\r
1993 GST_LOG_OBJECT (piffdemux, "seg_time %" GST_TIME_FORMAT,
\r
1994 GST_TIME_ARGS (seg_time));
\r
1996 if (G_UNLIKELY (seg_time > segment->duration)) {
\r
1997 GST_LOG_OBJECT (piffdemux, "seg_time > segment->duration %" GST_TIME_FORMAT,
\r
1998 GST_TIME_ARGS (segment->duration));
\r
2002 /* piffdemux->segment.stop is in outside-time-realm, whereas
\r
2003 * segment->media_stop is in track-time-realm.
\r
2005 * In order to compare the two, we need to bring segment.stop
\r
2006 * into the track-time-realm */
\r
2008 stop = piffdemux->segment.stop;
\r
2010 stop = piffdemux->segment.duration;
\r
2012 stop = segment->media_stop;
\r
2015 MIN (segment->media_stop, stop - segment->time + segment->media_start);
\r
2017 if (piffdemux->segment.rate >= 0) {
\r
2018 start = MIN (segment->media_start + seg_time, stop);
\r
2021 if (segment->media_start >= piffdemux->segment.start) {
\r
2022 start = segment->media_start;
\r
2023 time = segment->time;
\r
2025 start = piffdemux->segment.start;
\r
2026 time = segment->time + (piffdemux->segment.start - segment->media_start);
\r
2029 start = MAX (segment->media_start, piffdemux->segment.start);
\r
2030 stop = MIN (segment->media_start + seg_time, stop);
\r
2033 GST_DEBUG_OBJECT (piffdemux, "newsegment %d from %" GST_TIME_FORMAT
\r
2034 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
\r
2035 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
\r
2037 /* combine global rate with that of the segment */
\r
2038 rate = segment->rate * piffdemux->segment.rate;
\r
2040 /* update the segment values used for clipping */
\r
2041 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
\r
2042 gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME,
\r
2043 start, stop, time);
\r
2045 /* now prepare and send the segment */
\r
2046 event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,
\r
2047 start, stop, time);
\r
2048 gst_pad_push_event (piffdemux->srcpad, event);
\r
2049 /* assume we can send more data now */
\r
2050 stream->last_ret = GST_FLOW_OK;
\r
2051 /* clear to send tags on this pad now */
\r
2052 gst_piffdemux_push_tags (piffdemux, stream);
\r
2058 /* prepare to get the current sample of @stream, getting essential values.
\r
2060 * This function will also prepare and send the segment when needed.
\r
2062 * Return FALSE if the stream is EOS.
\r
2065 gst_piffdemux_prepare_current_sample (GstPiffDemux * piffdemux,
\r
2066 PiffDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
\r
2067 guint64 * duration, gboolean * keyframe)
\r
2069 PiffDemuxSample *sample;
\r
2070 guint64 time_position;
\r
2073 g_return_val_if_fail (stream != NULL, FALSE);
\r
2075 time_position = stream->time_position;
\r
2076 if (G_UNLIKELY (time_position == -1))
\r
2079 seg_idx = stream->segment_index;
\r
2080 if (G_UNLIKELY (seg_idx == -1)) {
\r
2081 /* find segment corresponding to time_position if we are looking
\r
2082 * for a segment. */
\r
2083 seg_idx = gst_piffdemux_find_segment (piffdemux, stream, time_position);
\r
2085 /* nothing found, we're really eos */
\r
2086 if (seg_idx == -1)
\r
2090 /* different segment, activate it, sample_index will be set. */
\r
2091 if (G_UNLIKELY (stream->segment_index != seg_idx))
\r
2092 gst_piffdemux_activate_segment (piffdemux, stream, seg_idx, time_position);
\r
2094 GST_LOG_OBJECT (piffdemux, "segment active, index = %u of %u",
\r
2095 stream->sample_index, stream->n_samples);
\r
2097 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
\r
2101 /* now get the info for the sample we're at */
\r
2102 sample = &stream->samples[stream->sample_index];
\r
2104 *timestamp = PIFFSAMPLE_PTS (stream, sample);
\r
2105 *offset = sample->offset;
\r
2106 *size = sample->size;
\r
2107 *duration = PIFFSAMPLE_DUR_PTS (stream, sample, *timestamp);
\r
2108 *keyframe = PIFFSAMPLE_KEYFRAME (stream, sample);
\r
2112 /* special cases */
\r
2115 stream->time_position = -1;
\r
2120 /* the input buffer metadata must be writable,
\r
2121 * but time/duration etc not yet set and need not be preserved */
\r
2122 static GstBuffer *
\r
2123 gst_piffdemux_process_buffer (GstPiffDemux * piffdemux, PiffDemuxStream * stream,
\r
2127 guint size, nsize = 0;
\r
2130 data = GST_BUFFER_DATA (buf);
\r
2131 size = GST_BUFFER_SIZE (buf);
\r
2133 if (G_UNLIKELY (stream->subtype != FOURCC_text)) {
\r
2137 if (G_LIKELY (size >= 2)) {
\r
2138 nsize = GST_READ_UINT16_BE (data);
\r
2139 nsize = MIN (nsize, size - 2);
\r
2142 GST_LOG_OBJECT (piffdemux, "3GPP timed text subtitle: %d/%d", nsize, size);
\r
2144 /* takes care of UTF-8 validation or UTF-16 recognition,
\r
2145 * no other encoding expected */
\r
2146 str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);
\r
2148 gst_buffer_unref (buf);
\r
2149 buf = gst_buffer_new ();
\r
2150 GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = (guint8 *) str;
\r
2151 GST_BUFFER_SIZE (buf) = strlen (str);
\r
2153 /* may be 0-size subtitle, which is also sent to keep pipeline going */
\r
2154 GST_BUFFER_DATA (buf) = data + 2;
\r
2155 GST_BUFFER_SIZE (buf) = nsize;
\r
2158 /* FIXME ? convert optional subsequent style info to markup */
\r
2163 /* Sets a buffer's attributes properly and pushes it downstream.
\r
2164 * Also checks for additional actions and custom processing that may
\r
2165 * need to be done first.
\r
2168 gst_piffdemux_decorate_and_push_buffer (GstPiffDemux * piffdemux,
\r
2169 PiffDemuxStream * stream, GstBuffer * buf,
\r
2170 guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,
\r
2171 guint64 byte_position)
\r
2173 GstFlowReturn ret = GST_FLOW_OK;
\r
2175 if (!stream->caps) {
\r
2176 GST_WARNING_OBJECT (piffdemux, "caps are empty...creat any caps");
\r
2177 stream->caps = gst_caps_new_any();
\r
2178 if (!stream->caps) {
\r
2179 GST_ERROR_OBJECT (piffdemux, "failed to create caps...");
\r
2180 ret = GST_FLOW_ERROR;
\r
2185 /* position reporting */
\r
2186 if (piffdemux->segment.rate >= 0) {
\r
2187 // TODO: Segment fault is coming here for Audio stream.. need to check
\r
2188 gst_segment_set_last_stop (&piffdemux->segment, GST_FORMAT_TIME, position);
\r
2189 //gst_piffdemux_sync_streams (piffdemux);
\r
2192 /* send out pending buffers */
\r
2193 while (stream->buffers) {
\r
2194 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
\r
2196 if (G_UNLIKELY (stream->discont)) {
\r
2197 GST_LOG_OBJECT (piffdemux, "marking discont buffer");
\r
2198 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
\r
2199 stream->discont = FALSE;
\r
2201 gst_buffer_set_caps (buffer, stream->caps);
\r
2203 gst_pad_push (piffdemux->srcpad, buffer);
\r
2205 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
\r
2208 /* we're going to modify the metadata */
\r
2209 buf = gst_buffer_make_metadata_writable (buf);
\r
2211 /* for subtitle processing */
\r
2212 if (G_UNLIKELY (stream->need_process))
\r
2213 buf = gst_piffdemux_process_buffer (piffdemux, stream, buf);
\r
2215 GST_BUFFER_TIMESTAMP (buf) = timestamp;
\r
2216 GST_BUFFER_DURATION (buf) = duration;
\r
2217 GST_BUFFER_OFFSET (buf) = -1;
\r
2218 GST_BUFFER_OFFSET_END (buf) = -1;
\r
2220 if (G_UNLIKELY (stream->padding)) {
\r
2221 GST_BUFFER_DATA (buf) += stream->padding;
\r
2222 GST_BUFFER_SIZE (buf) -= stream->padding;
\r
2225 if (G_UNLIKELY (buf == NULL))
\r
2228 if (G_UNLIKELY (stream->discont)) {
\r
2229 GST_LOG_OBJECT (piffdemux, "marking discont buffer");
\r
2230 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
\r
2231 stream->discont = FALSE;
\r
2235 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
\r
2237 //g_print ("\n\npad caps : %s\n\n", gst_caps_to_string (gst_pad_get_caps (stream->pad)));
\r
2239 //gst_buffer_set_caps (buf, stream->caps); // commenting to avoid caps by setting properties
\r
2242 // TODO: need to see how resolution switch will work
\r
2243 gst_buffer_set_caps (buf, stream->caps);
\r
2245 GST_LOG_OBJECT (piffdemux,
\r
2246 "Pushing buffer of size = %d with time %" GST_TIME_FORMAT ", duration %"
\r
2247 GST_TIME_FORMAT, GST_BUFFER_SIZE(buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
\r
2248 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
\r
2250 #ifdef DEC_OUT_FRAME_DUMP
\r
2253 written = fwrite (GST_BUFFER_DATA (buf), sizeof (unsigned char), GST_BUFFER_SIZE (buf), piffdump);
\r
2254 g_print ("PIFFDEMUX: written = %d\n", written);
\r
2255 fflush (piffdump);
\r
2259 ret = gst_pad_push (piffdemux->srcpad, buf);
\r
2269 * Returns the size of the first entry at the current offset.
\r
2270 * If -1, there are none (which means EOS or empty file).
\r
2273 next_entry_size (GstPiffDemux * demux)
\r
2275 PiffDemuxStream *stream = demux->stream;
\r
2276 PiffDemuxSample *sample;
\r
2278 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
\r
2281 GST_DEBUG_OBJECT (demux, "demux->sample_index = %d", stream->sample_index);
\r
2283 if (stream->sample_index == -1)
\r
2284 stream->sample_index = 0;
\r
2286 if (stream->sample_index >= stream->n_samples) {
\r
2287 GST_LOG_OBJECT (demux, "stream %d samples exhausted n_samples = %d",
\r
2288 stream->sample_index, stream->n_samples);
\r
2292 sample = &stream->samples[stream->sample_index];
\r
2294 GST_LOG_OBJECT (demux,
\r
2295 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
\r
2296 " / size:%" G_GUINT32_FORMAT ")", stream->sample_index, stream->sample_index,
\r
2297 sample->offset, sample->size);
\r
2299 GST_LOG_OBJECT (demux, "stream : demux->offset :%"G_GUINT64_FORMAT, demux->offset);
\r
2301 stream = demux->stream;
\r
2302 sample = &stream->samples[stream->sample_index];
\r
2304 if (sample->offset >= demux->offset) {
\r
2305 demux->todrop = sample->offset - demux->offset;
\r
2306 return sample->size + demux->todrop;
\r
2309 GST_DEBUG_OBJECT (demux,
\r
2310 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
\r
2315 gst_piffdemux_post_progress (GstPiffDemux * demux, gint num, gint denom)
\r
2317 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
\r
2319 gst_element_post_message (GST_ELEMENT_CAST (demux),
\r
2320 gst_message_new_element (GST_OBJECT_CAST (demux),
\r
2321 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
\r
2325 piffdemux_seek_offset (GstPiffDemux * demux, guint64 offset)
\r
2330 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
\r
2333 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
\r
2334 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
\r
2335 GST_SEEK_TYPE_NONE, -1);
\r
2337 res = gst_pad_push_event (demux->sinkpad, event);
\r
2342 static GstFlowReturn
\r
2343 gst_piffdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
\r
2345 GstPiffDemux *demux;
\r
2346 GstFlowReturn ret = GST_FLOW_OK;
\r
2347 demux = GST_PIFFDEMUX (gst_pad_get_parent (sinkpad));
\r
2349 gst_adapter_push (demux->adapter, inbuf);
\r
2351 /* we never really mean to buffer that much */
\r
2352 if (demux->neededbytes == -1)
\r
2355 GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
\r
2356 inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));
\r
2358 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
\r
2359 (ret == GST_FLOW_OK)) {
\r
2361 GST_DEBUG_OBJECT (demux,
\r
2362 "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
\r
2363 demux->state, demux->neededbytes, demux->offset);
\r
2365 switch (demux->state) {
\r
2366 case PIFFDEMUX_STATE_INITIAL:{
\r
2367 const guint8 *data;
\r
2371 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
\r
2373 /* get fourcc/length, set neededbytes */
\r
2374 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
\r
2376 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
\r
2377 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
\r
2379 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
\r
2380 ("This file is invalid and cannot be played."),
\r
2381 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
\r
2382 GST_FOURCC_ARGS (fourcc)));
\r
2384 ret = GST_FLOW_ERROR;
\r
2388 if (fourcc == FOURCC_mdat) {
\r
2389 if (demux->moof_rcvd) {
\r
2390 /* we have the headers, start playback */
\r
2391 demux->state = PIFFDEMUX_STATE_MOVIE;
\r
2392 demux->neededbytes = next_entry_size (demux);
\r
2393 demux->mdatleft = size;
\r
2395 /* Only post, event on pads is done after newsegment */
\r
2396 piffdemux_post_global_tags (demux);
\r
2398 GST_ERROR_OBJECT (demux, "mdata received before moof.. not handled");
\r
2399 goto unknown_stream;
\r
2401 } else if (G_UNLIKELY (size > PIFFDEMUX_MAX_ATOM_SIZE)) {
\r
2402 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
\r
2403 ("This file is invalid and cannot be played."),
\r
2404 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
\r
2405 GST_FOURCC_ARGS (fourcc), size));
\r
2406 ret = GST_FLOW_ERROR;
\r
2409 demux->neededbytes = size;
\r
2410 demux->state = PIFFDEMUX_STATE_HEADER;
\r
2414 case PIFFDEMUX_STATE_HEADER:{
\r
2415 const guint8 *data;
\r
2418 GST_DEBUG_OBJECT (demux, "In header");
\r
2420 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
\r
2422 /* parse the header */
\r
2423 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
\r
2425 if (fourcc == FOURCC_moof) {
\r
2426 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
\r
2427 if (!piffdemux_parse_moof (demux, data, demux->neededbytes,
\r
2428 demux->offset, demux->stream)) {
\r
2429 ret = GST_FLOW_ERROR;
\r
2432 demux->moof_rcvd = TRUE;
\r
2434 GST_WARNING_OBJECT (demux,
\r
2435 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
\r
2436 GST_FOURCC_ARGS (fourcc));
\r
2437 /* Let's jump that one and go back to initial state */
\r
2440 if (demux->mdatbuffer) {
\r
2441 /* the mdat was before the header */
\r
2442 GST_DEBUG_OBJECT (demux, "We have mdatbuffer:%p",
\r
2443 demux->mdatbuffer);
\r
2444 gst_adapter_clear (demux->adapter);
\r
2445 demux->mdatbuffer = NULL;
\r
2446 demux->offset = demux->mdatoffset;
\r
2447 demux->neededbytes = next_entry_size (demux);
\r
2448 demux->state = PIFFDEMUX_STATE_MOVIE;
\r
2449 demux->mdatleft = gst_adapter_available (demux->adapter);
\r
2451 /* Only post, event on pads is done after newsegment */
\r
2452 piffdemux_post_global_tags (demux);
\r
2454 GST_DEBUG_OBJECT (demux, "Carrying on normally");
\r
2455 gst_adapter_flush (demux->adapter, demux->neededbytes);
\r
2456 demux->offset += demux->neededbytes;
\r
2457 demux->neededbytes = 16;
\r
2458 demux->state = PIFFDEMUX_STATE_INITIAL;
\r
2463 case PIFFDEMUX_STATE_BUFFER_MDAT:{
\r
2466 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
\r
2468 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
\r
2469 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
\r
2470 GST_FOURCC_ARGS (PIFF_FOURCC (GST_BUFFER_DATA (buf) + 4)));
\r
2471 if (demux->mdatbuffer)
\r
2472 demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
\r
2474 demux->mdatbuffer = buf;
\r
2475 demux->offset += demux->neededbytes;
\r
2476 demux->neededbytes = 16;
\r
2477 demux->state = PIFFDEMUX_STATE_INITIAL;
\r
2478 gst_piffdemux_post_progress (demux, 1, 1);
\r
2482 case PIFFDEMUX_STATE_MOVIE:{
\r
2483 GstBuffer *outbuf;
\r
2484 PiffDemuxStream *stream = demux->stream;
\r
2485 PiffDemuxSample *sample;
\r
2486 guint64 timestamp, duration, position;
\r
2487 gboolean keyframe;
\r
2489 GST_DEBUG_OBJECT (demux,
\r
2490 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
\r
2492 if (demux->fragmented) {
\r
2493 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
\r
2495 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
\r
2496 /* if needed data starts within this atom,
\r
2497 * then it should not exceed this atom */
\r
2498 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
\r
2500 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
\r
2501 ("This file is invalid and cannot be played."),
\r
2502 ("sample data crosses atom boundary"));
\r
2504 ret = GST_FLOW_ERROR;
\r
2507 demux->mdatleft -= demux->neededbytes;
\r
2509 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
\r
2510 /* so we are dropping more than left in this atom */
\r
2511 demux->todrop -= demux->mdatleft;
\r
2512 demux->neededbytes -= demux->mdatleft;
\r
2513 demux->mdatleft = 0;
\r
2514 /* need to resume atom parsing so we do not miss any other pieces */
\r
2515 demux->state = PIFFDEMUX_STATE_INITIAL;
\r
2516 demux->neededbytes = 16;
\r
2521 if (demux->todrop) {
\r
2522 GST_LOG_OBJECT (demux, "Dropping %d bytes", demux->todrop);
\r
2523 gst_adapter_flush (demux->adapter, demux->todrop);
\r
2524 demux->neededbytes -= demux->todrop;
\r
2525 demux->offset += demux->todrop;
\r
2528 if ( !stream->sent_nsevent) {
\r
2529 //TODO: better to parse sink event function and send that new_segment
\r
2531 demux->pending_newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
\r
2532 demux->stream->start_ts, -1, demux->stream->start_ts);
\r
2534 demux->pending_newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
\r
2535 0, gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale), 0);
\r
2538 GST_INFO_OBJECT (demux, "New segment event : start = %"GST_TIME_FORMAT", stop = %" GST_TIME_FORMAT,
\r
2539 GST_TIME_ARGS (demux->stream->start_ts), GST_TIME_ARGS(gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale)));
\r
2541 if (!gst_pad_push_event (demux->srcpad, demux->pending_newsegment)) {
\r
2542 GST_ERROR_OBJECT (demux, "failding to send new segment...");
\r
2543 goto newsegment_error;
\r
2545 stream->sent_nsevent = TRUE;
\r
2548 /* Put data in a buffer, set timestamps, caps, ... */
\r
2549 outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
\r
2551 GST_DEBUG_OBJECT (demux, "Taken %d size buffer from adapter...", outbuf ? GST_BUFFER_SIZE (outbuf) : 0);
\r
2552 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (stream->fourcc));
\r
2554 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
\r
2556 sample = &stream->samples[stream->sample_index];
\r
2558 GST_DEBUG_OBJECT (demux, "start_ts = %"GST_TIME_FORMAT" ts : %"GST_TIME_FORMAT" ts = %llu, pts_offset = %u, scale = %d\n",
\r
2559 GST_TIME_ARGS(stream->start_ts),GST_TIME_ARGS(sample->timestamp), sample->timestamp,
\r
2560 sample->pts_offset,stream->timescale);
\r
2562 position = PIFFSAMPLE_DTS (stream, sample);
\r
2563 timestamp = PIFFSAMPLE_PTS (stream, sample) + stream->start_ts; // Adding to avoid resetting of timestamp
\r
2564 duration = PIFFSAMPLE_DUR_DTS (stream, sample, position);
\r
2565 keyframe = PIFFSAMPLE_KEYFRAME (stream, sample);
\r
2567 ret = gst_piffdemux_decorate_and_push_buffer (demux, stream, outbuf,
\r
2568 timestamp, duration, keyframe, position, demux->offset);
\r
2570 stream->sample_index++;
\r
2572 /* update current offset and figure out size of next buffer */
\r
2573 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
\r
2574 demux->offset, demux->neededbytes);
\r
2575 demux->offset += demux->neededbytes;
\r
2576 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
\r
2579 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
\r
2580 GST_DEBUG_OBJECT (demux, "finished parsing mdat, need to search next moof atom");
\r
2581 demux->neededbytes = 16;
\r
2582 demux->state = PIFFDEMUX_STATE_INITIAL;
\r
2583 GST_DEBUG ("\n\n Storing %s last_ts %"GST_TIME_FORMAT"\n\n", stream->subtype == FOURCC_vide ? "video" : "audio", GST_TIME_ARGS(timestamp));
\r
2584 stream->start_ts = timestamp + duration;
\r
2590 goto invalid_state;
\r
2594 /* when buffering movie data, at least show user something is happening */
\r
2595 if (ret == GST_FLOW_OK && demux->state == PIFFDEMUX_STATE_BUFFER_MDAT &&
\r
2596 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
\r
2597 gst_piffdemux_post_progress (demux, gst_adapter_available (demux->adapter),
\r
2598 demux->neededbytes);
\r
2601 gst_object_unref (demux);
\r
2608 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
\r
2609 ret = GST_FLOW_ERROR;
\r
2614 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
\r
2615 ret = GST_FLOW_UNEXPECTED;
\r
2620 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
\r
2621 (NULL), ("piffdemuxer invalid state %d", demux->state));
\r
2622 ret = GST_FLOW_ERROR;
\r
2627 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
\r
2628 (NULL), ("could not send newsegment event"));
\r
2629 ret = GST_FLOW_ERROR;
\r
2635 piffdemux_parse_container (GstPiffDemux * piffdemux, GNode * node, const guint8 * buf,
\r
2636 const guint8 * end)
\r
2638 while (G_UNLIKELY (buf < end)) {
\r
2642 if (G_UNLIKELY (buf + 4 > end)) {
\r
2643 GST_LOG_OBJECT (piffdemux, "buffer overrun");
\r
2646 len = PIFF_UINT32 (buf);
\r
2647 if (G_UNLIKELY (len == 0)) {
\r
2648 GST_LOG_OBJECT (piffdemux, "empty container");
\r
2651 if (G_UNLIKELY (len < 8)) {
\r
2652 GST_WARNING_OBJECT (piffdemux, "length too short (%d < 8)", len);
\r
2655 if (G_UNLIKELY (len > (end - buf))) {
\r
2656 GST_WARNING_OBJECT (piffdemux, "length too long (%d > %d)", len,
\r
2657 (gint) (end - buf));
\r
2661 child = g_node_new ((guint8 *) buf);
\r
2662 g_node_append (node, child);
\r
2663 GST_LOG_OBJECT (piffdemux, "adding new node of len %d", len);
\r
2664 piffdemux_parse_node (piffdemux, child, buf, len);
\r
2673 piffdemux_parse_node (GstPiffDemux * piffdemux, GNode * node, const guint8 * buffer,
\r
2676 guint32 fourcc = 0;
\r
2677 guint32 node_length = 0;
\r
2678 const PiffNodeType *type;
\r
2679 const guint8 *end;
\r
2681 GST_LOG_OBJECT (piffdemux, "piffdemux_parse buffer %p length %u", buffer, length);
\r
2683 if (G_UNLIKELY (length < 8))
\r
2684 goto not_enough_data;
\r
2686 node_length = PIFF_UINT32 (buffer);
\r
2687 fourcc = PIFF_FOURCC (buffer + 4);
\r
2689 /* ignore empty nodes */
\r
2690 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
\r
2693 type = piffdemux_type_get (fourcc);
\r
2695 end = buffer + length;
\r
2697 GST_LOG_OBJECT (piffdemux,
\r
2698 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
\r
2699 GST_FOURCC_ARGS (fourcc), node_length, type->name);
\r
2701 if (node_length > length)
\r
2702 goto broken_atom_size;
\r
2704 if (type->flags & PIFF_FLAG_CONTAINER) {
\r
2705 piffdemux_parse_container (piffdemux, node, buffer + 8, end);
\r
2707 GST_LOG_OBJECT (piffdemux, "parsed '%" GST_FOURCC_FORMAT "'",
\r
2708 GST_FOURCC_ARGS (fourcc));
\r
2715 GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
\r
2716 ("This file is corrupt and cannot be played."),
\r
2717 ("Not enough data for an atom header, got only %u bytes", length));
\r
2723 GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
\r
2724 ("This file is corrupt and cannot be played."),
\r
2725 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
\r
2726 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
\r
2735 piffdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
\r
2739 guint32 child_fourcc;
\r
2741 for (child = g_node_first_child (node); child;
\r
2742 child = g_node_next_sibling (child)) {
\r
2743 buffer = (guint8 *) child->data;
\r
2745 child_fourcc = PIFF_FOURCC (buffer + 4);
\r
2747 if (G_UNLIKELY (child_fourcc == fourcc)) {
\r
2755 piffdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
\r
2756 GstByteReader * parser)
\r
2760 guint32 child_fourcc, child_len;
\r
2762 for (child = g_node_first_child (node); child;
\r
2763 child = g_node_next_sibling (child)) {
\r
2764 buffer = (guint8 *) child->data;
\r
2766 child_len = PIFF_UINT32 (buffer);
\r
2767 child_fourcc = PIFF_FOURCC (buffer + 4);
\r
2769 if (G_UNLIKELY (child_fourcc == fourcc)) {
\r
2770 if (G_UNLIKELY (child_len < (4 + 4)))
\r
2772 /* FIXME: must verify if atom length < parent atom length */
\r
2773 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
\r
2781 piffdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
\r
2782 GstByteReader * parser)
\r
2786 guint32 child_fourcc, child_len;
\r
2788 for (child = g_node_next_sibling (node); child;
\r
2789 child = g_node_next_sibling (child)) {
\r
2790 buffer = (guint8 *) child->data;
\r
2792 child_fourcc = PIFF_FOURCC (buffer + 4);
\r
2794 if (child_fourcc == fourcc) {
\r
2796 child_len = PIFF_UINT32 (buffer);
\r
2797 if (G_UNLIKELY (child_len < (4 + 4)))
\r
2799 /* FIXME: must verify if atom length < parent atom length */
\r
2800 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
\r
2809 piffdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
\r
2811 return piffdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
\r
2814 #define _codec(name) \
\r
2816 if (codec_name) { \
\r
2817 *codec_name = g_strdup (name); \
\r
2822 gst_piffdemux_set_video_params (GstPiffDemux * piffdemux, guint fourcc,
\r
2823 guint width, guint height,
\r
2824 guint fps_n, guint fps_d, unsigned char *codec_data, unsigned int codec_data_len)
\r
2826 GstCaps *caps = NULL;
\r
2827 GstBuffer *dci = NULL;
\r
2829 if (codec_data && codec_data_len) {
\r
2830 dci = gst_buffer_new_and_alloc (codec_data_len);
\r
2832 GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");
\r
2834 memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);
\r
2840 case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
\r
2841 caps = gst_caps_new_simple ("video/x-h264",
\r
2842 "width", G_TYPE_INT, width,
\r
2843 "height", G_TYPE_INT, height,
\r
2844 "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
\r
2845 "stream-format", G_TYPE_STRING, "avc",
\r
2846 "alignment", G_TYPE_STRING, "au",
\r
2847 "codec_data", GST_TYPE_BUFFER, dci,
\r
2852 caps = gst_caps_new_simple ("video/x-wmv",
\r
2853 "width", G_TYPE_INT, width,
\r
2854 "height", G_TYPE_INT, height,
\r
2855 "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
\r
2856 "wmvversion", G_TYPE_INT, 3,
\r
2857 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
\r
2858 "codec_data", GST_TYPE_BUFFER, dci,
\r
2864 s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,
\r
2865 GST_FOURCC_ARGS (fourcc));
\r
2866 caps = gst_caps_new_simple (s,
\r
2867 "width", G_TYPE_INT, width,
\r
2868 "height", G_TYPE_INT, height,
\r
2869 "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
\r
2870 "codec_data", GST_TYPE_BUFFER, dci,
\r
2876 piffdemux->stream->caps = caps;
\r
2877 GST_INFO_OBJECT (piffdemux, "prepared video caps : %s", gst_caps_to_string(caps));
\r
2881 gst_piffdemux_set_audio_params (GstPiffDemux * piffdemux, guint fourcc,
\r
2882 guint sampling_rate, guint bps, guint channels, unsigned char *codec_data, unsigned int codec_data_len)
\r
2884 GstCaps *caps = NULL;
\r
2885 GstBuffer *dci = NULL;
\r
2887 if (codec_data && codec_data_len) {
\r
2888 dci = gst_buffer_new_and_alloc (codec_data_len);
\r
2890 GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");
\r
2892 memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);
\r
2898 case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
\r
2899 caps = gst_caps_new_simple ("audio/mpeg",
\r
2900 "mpegversion", G_TYPE_INT, 4,
\r
2901 "framed", G_TYPE_BOOLEAN, TRUE,
\r
2902 "stream-format", G_TYPE_STRING, "raw",
\r
2903 "rate", G_TYPE_INT, (int) sampling_rate,
\r
2904 "channels", G_TYPE_INT, channels,
\r
2909 caps = gst_caps_new_simple ("audio/x-wma",
\r
2910 "rate", G_TYPE_INT, (int) sampling_rate,
\r
2911 "channels", G_TYPE_INT, channels,
\r
2917 s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,
\r
2918 GST_FOURCC_ARGS (fourcc));
\r
2919 caps = gst_caps_new_simple (s,
\r
2920 "rate", G_TYPE_INT, (int) sampling_rate,
\r
2921 "channels", G_TYPE_INT, channels,
\r
2927 piffdemux->stream->caps = caps;
\r
2928 GST_INFO_OBJECT (piffdemux, "prepared audio caps : %s", gst_caps_to_string(caps));
\r
2932 #define g_marshal_value_peek_object(v) g_value_get_object (v)
\r
2935 __gst_piffdemux_marshal_BOOLEAN__OBJECT (GClosure *closure,
\r
2936 GValue *return_value G_GNUC_UNUSED,
\r
2937 guint n_param_values,
\r
2938 const GValue *param_values,
\r
2939 gpointer invocation_hint G_GNUC_UNUSED,
\r
2940 gpointer marshal_data)
\r
2942 typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer data1,
\r
2945 register GMarshalFunc_BOOLEAN__OBJECT callback;
\r
2946 register GCClosure *cc = (GCClosure*) closure;
\r
2947 register gpointer data1, data2;
\r
2948 gboolean v_return;
\r
2950 g_return_if_fail (return_value != NULL);
\r
2951 g_return_if_fail (n_param_values == 2);
\r
2953 if (G_CCLOSURE_SWAP_DATA (closure))
\r
2955 data1 = closure->data;
\r
2956 data2 = g_value_peek_pointer (param_values + 0);
\r
2960 data1 = g_value_peek_pointer (param_values + 0);
\r
2961 data2 = closure->data;
\r
2963 callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback);
\r
2965 v_return = callback (data1,
\r
2966 g_marshal_value_peek_object (param_values + 1),
\r
2969 g_value_set_boolean (return_value, v_return);
\r
2972 #define PIFFDEMUX_SPSPPS_LENGTH_SIZE 2
\r
2975 ConvertH264_MetaDCI_to_3GPPDCI(unsigned char *dci_meta_buf, unsigned int dci_meta_size, unsigned char **dci_3gpp_buf, unsigned int *dci_3gpp_size)
\r
2977 unsigned short unit_size = 0;
\r
2978 unsigned int total_size = 0;
\r
2979 unsigned char unit_nb = 0;
\r
2980 unsigned char sps_done = 0;
\r
2981 const unsigned char *extradata = NULL;
\r
2982 unsigned int h264_nal_length_size = 0;
\r
2983 unsigned char *out = NULL;
\r
2984 //g_print ("\n\nConvertH264_MetaDCI_to_3GPPDCI Entering.............\n");
\r
2986 /* nothing to filter */
\r
2987 if ((dci_meta_buf == NULL) || (dci_meta_size < 6))
\r
2989 GST_ERROR ("Insufficient codec data...\n");
\r
2993 /* Removing unnecessary info in meta data */
\r
2994 extradata = (unsigned char *)dci_meta_buf + 4;
\r
2996 /* retrieve Length of Length*/
\r
2997 h264_nal_length_size = (*extradata++ & 0x03) + 1;
\r
2999 GST_LOG ("Length Of Length is %d\n", h264_nal_length_size);
\r
3000 if (h264_nal_length_size == 3)
\r
3002 GST_ERROR ("LengthOfLength is WRONG...\n");
\r
3006 /* retrieve sps and pps unit(s) */
\r
3007 unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */
\r
3008 GST_LOG ("No. of SPS units = %u\n", unit_nb);
\r
3012 GST_ERROR ("SPS is not present....\n");
\r
3018 /* get SPS/PPS data Length*/
\r
3019 unit_size = PIFFDEMUX_RB16(extradata);
\r
3021 GST_LOG ("SPS size = %d", unit_size);
\r
3023 /* Extra 4 bytes for adding size of the packet */
\r
3024 total_size += unit_size + h264_nal_length_size;
\r
3026 /* Check if SPS/PPS Data Length crossed buffer Length */
\r
3027 if ((extradata + 2 + unit_size) > (dci_meta_buf + dci_meta_size))
\r
3029 GST_ERROR ("SPS Length is wrong in DCI...\n");
\r
3032 out = realloc(out, total_size);
\r
3035 GST_ERROR ("realloc FAILED...\n");
\r
3038 /* Copy length of SPS header */
\r
3039 // tmp = (unsigned int *)(out + total_size - unit_size - h264_nal_length_size);
\r
3040 // *tmp = unit_size;
\r
3041 (out + total_size - unit_size - h264_nal_length_size)[0] = 0;
\r
3042 (out + total_size - unit_size - h264_nal_length_size)[1] = 0;
\r
3043 (out + total_size - unit_size - h264_nal_length_size)[2] = 0;
\r
3044 (out + total_size - unit_size - h264_nal_length_size)[3] = (unsigned char)unit_size;
\r
3046 // memcpy(out + total_size - unit_size - h264_nal_length_size, &unit_size, h264_nal_length_size);
\r
3047 //g_print ("out[0] = %02x, out[1] = %02x, out[2] = %02x = out[3] = %02x\n",
\r
3048 // out[total_size - unit_size - h264_nal_length_size], out[total_size - unit_size - h264_nal_length_size+1],
\r
3049 // out[total_size - unit_size - h264_nal_length_size + 2], out[total_size - unit_size - h264_nal_length_size + 3]);
\r
3051 /* Copy SPS/PPS Length and data */
\r
3052 memcpy(out + total_size - unit_size, extradata + PIFFDEMUX_SPSPPS_LENGTH_SIZE, unit_size);
\r
3054 extradata += (PIFFDEMUX_SPSPPS_LENGTH_SIZE + unit_size);
\r
3056 if (!unit_nb && !sps_done++)
\r
3058 /* Completed reading SPS data, now read PPS data */
\r
3059 unit_nb = *extradata++; /* number of pps unit(s) */
\r
3060 GST_DEBUG ("No. of PPS units = %d\n", unit_nb);
\r
3064 *dci_3gpp_buf = malloc (total_size);
\r
3065 if (NULL == *dci_3gpp_buf)
\r
3067 GST_ERROR ("Memory Allocation FAILED...\n");
\r
3072 memcpy(*dci_3gpp_buf, out, total_size);
\r
3073 *dci_3gpp_size = total_size;
\r
3075 GST_DEBUG ("SPS_PPS size = %d\n", total_size);
\r