tizen 2.0 init
[framework/multimedia/gst-plugins-ext0.10.git] / piffdemux / src / piffdemux.c
1 \r
2 \r
3 #ifdef HAVE_CONFIG_H\r
4 #include "config.h"\r
5 #endif\r
6 \r
7 #include "piffdemux.h"\r
8 #include <glib/gprintf.h>\r
9 #include <gst/tag/tag.h>\r
10 #include <stdio.h>\r
11 #include <stdlib.h>\r
12 #include <string.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
18 \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
25 \r
26 #define PIFF_DEFAULT_WIDTH 16\r
27 #define PIFF_DEFAULT_HEIGHT 16\r
28 #define PIFF_DEFAULT_BPS 16\r
29 \r
30 #undef DEC_OUT_FRAME_DUMP\r
31 \r
32 #ifdef DEC_OUT_FRAME_DUMP\r
33 #include <stdio.h>\r
34 FILE *piffdump = NULL;\r
35 #endif\r
36 \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
40 \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
43 \r
44 GST_DEBUG_CATEGORY (piffdemux_debug);\r
45 \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
50 \r
51 enum\r
52 {\r
53   PROR_PIFF_0,\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
58   PROP_PIFF_IS_LIVE,\r
59   PROP_PIFF_LOOKAHEAD_COUNT,\r
60   PROP_PIFF_AVG_FRAME_DUR,\r
61 };\r
62 \r
63 enum\r
64 {\r
65   SIGNAL_LIVE_PARAM,\r
66   LAST_SIGNAL\r
67 };\r
68 \r
69 static guint gst_piffdemux_signals[LAST_SIGNAL] = { 0 };\r
70 \r
71 struct _PiffDemuxSubSampleEntryInfo\r
72 {\r
73   guint16 LenofClearData;\r
74   guint32 LenofEncryptData;\r
75 };\r
76 \r
77 struct _PiffDemuxSubSampleEncryption\r
78 {\r
79   guint16 n_entries;\r
80   PiffDemuxSubSampleEntryInfo *sub_entry;\r
81 };\r
82 \r
83 struct _PiffDemuxSample\r
84 {\r
85   guint32 size;\r
86   gint32 pts_offset;            /* Add this value to timestamp to get the pts */\r
87   guint64 offset;\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
93 };\r
94 \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
107 \r
108 #define PIFFSAMPLE_KEYFRAME(stream,sample) ((sample)->keyframe);\r
109 \r
110 typedef char uuid_t[16];\r
111 \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
116 \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
121 \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
126 \r
127 #define SE_OVERRIDE_TE_FLAGS 0x000001\r
128 #define SE_USE_SUBSAMPLE_ENCRYPTION 0x000002\r
129 \r
130 typedef enum\r
131 {\r
132   UUID_UNKNOWN = -1,\r
133   UUID_TFXD,\r
134   UUID_TFRF,\r
135   UUID_SAMPLE_ENCRYPT,\r
136 }uuid_type_t;\r
137 \r
138 struct _PiffDemuxSegment\r
139 {\r
140   /* global time and duration, all gst time */\r
141   guint64 time;\r
142   guint64 stop_time;\r
143   guint64 duration;\r
144   /* media time of trak, all gst time */\r
145   guint64 media_start;\r
146   guint64 media_stop;\r
147   gdouble rate;\r
148 };\r
149 \r
150 \r
151 struct _PiffDemuxStream\r
152 {\r
153   /* stream type */\r
154   guint32 subtype;\r
155   GstCaps *caps;\r
156   guint32 fourcc;\r
157 \r
158   /* duration/scale */\r
159   guint64 duration;             /* in timescale */\r
160   guint32 timescale;\r
161 \r
162   /* our samples */\r
163   guint32 n_samples;\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
167 \r
168   /* if we use chunks or samples */\r
169   gboolean sampled;\r
170   guint padding;\r
171 \r
172   /* when a discontinuity is pending */\r
173   gboolean discont;\r
174 \r
175   /* list of buffers to push first */\r
176   GSList *buffers;\r
177 \r
178   /* buffer needs some custom processing, e.g. subtitles */\r
179   gboolean need_process;\r
180 \r
181     /* current position */\r
182   guint32 segment_index;\r
183   guint32 sample_index;\r
184   guint64 time_position;        /* in gst time */\r
185 \r
186   /* the Gst segment we are processing out, used for clipping */\r
187   GstSegment segment;\r
188 \r
189   /* last GstFlowReturn */\r
190   GstFlowReturn last_ret;\r
191 \r
192 \r
193   /* quicktime segments */\r
194   guint32 n_segments;\r
195   PiffDemuxSegment *segments;\r
196   guint32 from_sample;\r
197   guint32 to_sample;\r
198 \r
199   gboolean sent_eos;\r
200   GstTagList *pending_tags;\r
201   gboolean send_global_tags;\r
202 \r
203   GstEvent *pending_event;\r
204 \r
205   gboolean sent_nsevent;\r
206 \r
207   guint64 start_ts;\r
208 \r
209   guint64 avg_dur; /* average frame duration */\r
210 };\r
211 \r
212 \r
213 enum PiffDemuxState\r
214 {\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
219 };\r
220 \r
221 \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
228 \r
229 static GstStaticPadTemplate gst_piffdemux_sink_template =\r
230     GST_STATIC_PAD_TEMPLATE ("sink",\r
231     GST_PAD_SINK,\r
232     GST_PAD_ALWAYS,\r
233     GST_STATIC_CAPS ("application/x-piff")\r
234     );\r
235 \r
236 static GstStaticPadTemplate gst_piffdemux_src_template =\r
237 GST_STATIC_PAD_TEMPLATE ("src",\r
238     GST_PAD_SRC,\r
239     GST_PAD_ALWAYS,\r
240     GST_STATIC_CAPS_ANY);\r
241 \r
242 \r
243 GST_BOILERPLATE (GstPiffDemux, gst_piffdemux, GstPiffDemux, GST_TYPE_ELEMENT);\r
244 \r
245 static void gst_piffdemux_dispose (GObject * object);\r
246 \r
247 static GstStateChangeReturn gst_piffdemux_change_state (GstElement * element,\r
248     GstStateChange transition);\r
249 static void\r
250 gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);\r
251 static void\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
261 \r
262 \r
263 static gboolean\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
265 void\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
272 \r
273 static void\r
274 gst_piffdemux_base_init (gpointer klass)\r
275 {\r
276   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);\r
277 \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
283       "Codec/Parser",\r
284       "Parser for PIFF file format",\r
285       "naveen ch <naveen.ch@samsung.com>");\r
286 \r
287   GST_DEBUG_CATEGORY_INIT (piffdemux_debug, "piffdemux", 0, "piffdemux plugin");\r
288 }\r
289 \r
290 static void\r
291 gst_piffdemux_class_init (GstPiffDemuxClass * klass)\r
292 {\r
293   GObjectClass *gobject_class;\r
294   GstElementClass *gstelement_class;\r
295 \r
296   gobject_class = (GObjectClass *) klass;\r
297   gstelement_class = (GstElementClass *) klass;\r
298 \r
299   parent_class = g_type_class_peek_parent (klass);\r
300 \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
304 \r
305   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_piffdemux_change_state);\r
306 \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
311  \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
318 \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
324 \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
330 \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
334       FALSE,\r
335       G_PARAM_READWRITE));\r
336 \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
340       0,\r
341       G_PARAM_READWRITE));\r
342 \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
346       G_MAXUINT64,\r
347       G_PARAM_READABLE));\r
348 \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
353 \r
354 }\r
355 \r
356 static void\r
357 gst_piffdemux_init (GstPiffDemux * piffdemux, GstPiffDemuxClass * klass)\r
358 {\r
359   /* sink pad */\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
364 \r
365   /* source pad */\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
372 \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
387 \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
399 \r
400 #ifdef DEC_OUT_FRAME_DUMP\r
401     piffdump = fopen ("/opt/media/piff_out_dump.dmp", "w+");\r
402     if (piffdump == NULL)\r
403     {\r
404         g_print ("\nNot able to create frame dump file\n");\r
405     }\r
406 #endif\r
407 \r
408   gst_segment_init (&piffdemux->segment, GST_FORMAT_TIME);\r
409 }\r
410 \r
411 static void\r
412 gst_piffdemux_dispose (GObject * object)\r
413 {\r
414   GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);\r
415 \r
416   if (piffdemux->adapter) {\r
417     g_object_unref (G_OBJECT (piffdemux->adapter));\r
418     piffdemux->adapter = NULL;\r
419   }\r
420 \r
421 #ifdef DEC_OUT_FRAME_DUMP\r
422     {\r
423         fclose (piffdump);\r
424         piffdump = NULL;\r
425     }\r
426 #endif\r
427   G_OBJECT_CLASS (parent_class)->dispose (object);\r
428 }\r
429 \r
430 \r
431 static void\r
432 gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)\r
433 {\r
434   GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);\r
435 \r
436   switch (prop_id) {\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
444          }\r
445         break;\r
446       }\r
447     case PROP_PIFF_MEDIA_TIMESCALE:\r
448       piffdemux->stream->timescale = g_value_get_uint64(value);\r
449       break;\r
450     case PROP_PIFF_MEDIA_DURATION:\r
451       piffdemux->stream->duration = g_value_get_int64(value);\r
452       break;\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
456       break;\r
457     case PROP_PIFF_IS_LIVE:\r
458       piffdemux->is_live = g_value_get_boolean(value);\r
459       break;\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
463       break;\r
464     default:\r
465       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);\r
466       break;\r
467     }\r
468 }\r
469 \r
470 \r
471 static void\r
472 gst_piffdemux_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)\r
473 {\r
474   GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);\r
475 \r
476   switch (prop_id) {\r
477      case PROP_PIFF_MEDIA_CAPS:\r
478       gst_value_set_caps (value, piffdemux->stream->caps);\r
479       break;\r
480     case PROP_PIFF_MEDIA_TIMESCALE:\r
481       g_value_set_uint64 (value, piffdemux->stream->timescale);\r
482       break;\r
483     case PROP_PIFF_MEDIA_DURATION:\r
484       g_value_set_int64 (value, piffdemux->stream->duration);\r
485       break;\r
486     case PROP_PIFF_MEDIA_START_TIMESTAMP:\r
487       g_value_set_uint64 (value, piffdemux->stream->start_ts);\r
488       break;\r
489     case PROP_PIFF_IS_LIVE:\r
490       g_value_set_boolean(value, piffdemux->is_live);\r
491       break;\r
492     case PROP_PIFF_LOOKAHEAD_COUNT:\r
493       g_value_set_uint (value, piffdemux->lookahead_cnt);\r
494       break;\r
495     case PROP_PIFF_AVG_FRAME_DUR:\r
496       g_value_set_uint64 (value, piffdemux->stream->avg_dur);\r
497       break;\r
498     default:\r
499       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);\r
500       break;\r
501     }\r
502 }\r
503 \r
504 \r
505 static void\r
506 gst_piffdemux_post_no_playable_stream_error (GstPiffDemux * piffdemux)\r
507 {\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
512   } else {\r
513     GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,\r
514         ("This file contains no playable streams."),\r
515         ("no known streams found"));\r
516   }\r
517 \r
518 }\r
519 \r
520 static gboolean\r
521 gst_piffdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,\r
522     GstFormat dest_format, gint64 * dest_value)\r
523 {\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
527 \r
528   if (stream->subtype != FOURCC_vide) {\r
529     res = FALSE;\r
530     goto done;\r
531   }\r
532 \r
533   switch (src_format) {\r
534     case GST_FORMAT_TIME:\r
535       switch (dest_format) {\r
536         case GST_FORMAT_BYTES:{\r
537 \r
538           break;\r
539         }\r
540         default:\r
541           res = FALSE;\r
542           break;\r
543       }\r
544       break;\r
545     case GST_FORMAT_BYTES:\r
546       switch (dest_format) {\r
547         case GST_FORMAT_TIME:{\r
548 \r
549           break;\r
550         }\r
551         default:\r
552           res = FALSE;\r
553           break;\r
554       }\r
555       break;\r
556     default:\r
557       res = FALSE;\r
558   }\r
559 \r
560 done:\r
561   gst_object_unref (piffdemux);\r
562 \r
563   return res;\r
564 }\r
565 \r
566 static const GstQueryType *\r
567 gst_piffdemux_get_src_query_types (GstPad * pad)\r
568 {\r
569   static const GstQueryType src_types[] = {\r
570     GST_QUERY_POSITION,\r
571     GST_QUERY_DURATION,\r
572     GST_QUERY_CONVERT,\r
573     GST_QUERY_FORMATS,\r
574     GST_QUERY_SEEKING,\r
575     0\r
576   };\r
577 \r
578   return src_types;\r
579 }\r
580 \r
581 static gboolean\r
582 gst_piffdemux_get_duration (GstPiffDemux * piffdemux, gint64 * duration)\r
583 {\r
584   gboolean res = TRUE;\r
585 \r
586   *duration = GST_CLOCK_TIME_NONE;\r
587 \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
592     }\r
593   }\r
594   return res;\r
595 }\r
596 \r
597 static gboolean\r
598 gst_piffdemux_handle_src_query (GstPad * pad, GstQuery * query)\r
599 {\r
600   gboolean res = FALSE;\r
601   GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));\r
602 \r
603   GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));\r
604 \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
611         res = TRUE;\r
612       }\r
613       break;\r
614     case GST_QUERY_DURATION:{\r
615       GstFormat fmt;\r
616       GST_ERROR ("Querying DURATION from piffdemux....");\r
617 \r
618       gst_query_parse_duration (query, &fmt, NULL);\r
619       if (fmt == GST_FORMAT_TIME) {\r
620         gint64 duration = -1;\r
621 \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
625           res = TRUE;\r
626         }\r
627       }\r
628       break;\r
629     }\r
630     case GST_QUERY_CONVERT:{\r
631       GstFormat src_fmt, dest_fmt;\r
632       gint64 src_value, dest_value = 0;\r
633 \r
634       gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);\r
635 \r
636       res = gst_piffdemux_src_convert (pad,\r
637           src_fmt, src_value, dest_fmt, &dest_value);\r
638       if (res) {\r
639         gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);\r
640         res = TRUE;\r
641       }\r
642       break;\r
643     }\r
644     case GST_QUERY_FORMATS:\r
645       gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);\r
646       res = TRUE;\r
647       break;\r
648     case GST_QUERY_SEEKING:{\r
649 \r
650       break;\r
651     }\r
652     default:\r
653       res = gst_pad_query_default (pad, query);\r
654       break;\r
655   }\r
656 \r
657   gst_object_unref (piffdemux);\r
658 \r
659   return res;\r
660 }\r
661 \r
662 \r
663 static void\r
664 gst_piffdemux_push_tags (GstPiffDemux * piffdemux, PiffDemuxStream * stream)\r
665 {\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
672   }\r
673 \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
680   }\r
681 }\r
682 \r
683 \r
684 static void\r
685 gst_piffdemux_push_event (GstPiffDemux * piffdemux, GstEvent * event)\r
686 {\r
687   GstEventType etype = GST_EVENT_TYPE (event);\r
688 \r
689   GST_DEBUG_OBJECT (piffdemux, "pushing %s event on source pad",\r
690       GST_EVENT_TYPE_NAME (event));\r
691 \r
692   if (piffdemux->stream->sent_eos) {\r
693     GST_INFO_OBJECT (piffdemux, "already sent eos");\r
694     return;\r
695   }\r
696 \r
697   if (!gst_pad_push_event (piffdemux->srcpad, event)) {\r
698     GST_ERROR_OBJECT (piffdemux, "error in sending event to srcpad...");\r
699   }\r
700 \r
701   if (etype == GST_EVENT_EOS)\r
702     piffdemux->stream->sent_eos = TRUE;\r
703 }\r
704 \r
705 \r
706 /* find the segment for @time_position for @stream\r
707  *\r
708  * Returns -1 if the segment cannot be found.\r
709  */\r
710 static guint32\r
711 gst_piffdemux_find_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,\r
712     guint64 time_position)\r
713 {\r
714   gint i;\r
715   guint32 seg_idx;\r
716 \r
717   GST_LOG_OBJECT (piffdemux, "finding segment for %" GST_TIME_FORMAT,\r
718       GST_TIME_ARGS (time_position));\r
719 \r
720   /* find segment corresponding to time_position if we are looking\r
721    * for a segment. */\r
722   seg_idx = -1;\r
723   for (i = 0; i < stream->n_segments; i++) {\r
724     PiffDemuxSegment *segment = &stream->segments[i];\r
725 \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
729 \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
734         seg_idx = i;\r
735         break;\r
736       }\r
737     } else {\r
738       if (segment->time <= time_position && time_position <= segment->stop_time) {\r
739         GST_LOG_OBJECT (piffdemux, "segment %d matches", i);\r
740         seg_idx = i;\r
741         break;\r
742       }\r
743     }\r
744   }\r
745   return seg_idx;\r
746 }\r
747 \r
748 \r
749 static gboolean\r
750 gst_piffdemux_handle_src_event (GstPad * pad, GstEvent * event)\r
751 {\r
752   gboolean res = TRUE;\r
753   GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));\r
754 \r
755   switch (GST_EVENT_TYPE (event)) {\r
756     case GST_EVENT_QOS:\r
757     case GST_EVENT_NAVIGATION:\r
758       res = FALSE;\r
759       gst_event_unref (event);\r
760       break;\r
761     case GST_EVENT_SEEK:\r
762     default:\r
763       res = gst_pad_event_default (pad, event);\r
764       break;\r
765   }\r
766 \r
767   gst_object_unref (piffdemux);\r
768 \r
769   return res;\r
770 }\r
771 \r
772 \r
773 static void\r
774 gst_piffdemux_move_stream (GstPiffDemux * piffdemux, PiffDemuxStream * str,\r
775     guint32 index)\r
776 {\r
777   /* no change needed */\r
778   if (index == str->sample_index)\r
779     return;\r
780 \r
781   GST_DEBUG_OBJECT (piffdemux, "moving to sample %u of %u", index,\r
782       str->n_samples);\r
783 \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
787    * starting from */\r
788   str->from_sample = index;\r
789   str->discont = TRUE;\r
790 }\r
791 \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
796 static void\r
797 gst_piffdemux_find_sample (GstPiffDemux * piffdemux, gint64 byte_pos, gboolean fw,\r
798     gboolean set, PiffDemuxStream ** _stream, gint * _index, gint64 * _time)\r
799 {\r
800   gint i, index;\r
801   gint64 time, min_time;\r
802   PiffDemuxStream *stream;\r
803   PiffDemuxStream *str = piffdemux->stream;\r
804   gint inc;\r
805   gboolean set_sample;\r
806 \r
807   min_time = -1;\r
808   stream = NULL;\r
809   index = -1;\r
810 \r
811   set_sample = !set;\r
812   if (fw) {\r
813     i = 0;\r
814     inc = 1;\r
815   } else {\r
816     i = str->n_samples - 1;\r
817     inc = -1;\r
818   }\r
819 \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
823     (!fw &&\r
824     (str->samples[i].offset + str->samples[i].size <=\r
825     byte_pos)))) {\r
826       /* move stream to first available sample */\r
827       if (set) {\r
828         gst_piffdemux_move_stream (piffdemux, str, i);\r
829         set_sample = TRUE;\r
830       }\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
836         min_time = time;\r
837       }\r
838       index = i;\r
839       break;\r
840     }\r
841   }\r
842   /* no sample for this stream, mark eos */\r
843   if (!set_sample)\r
844     gst_piffdemux_move_stream (piffdemux, str, str->n_samples);\r
845 \r
846   if (_time)\r
847     *_time = min_time;\r
848   if (_stream)\r
849     *_stream = str;\r
850   if (_index)\r
851     *_index = index;\r
852 }\r
853 \r
854 \r
855 static gboolean\r
856 gst_piffdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)\r
857 {\r
858   GstPiffDemux *demux = GST_PIFFDEMUX (GST_PAD_PARENT (sinkpad));\r
859   gboolean res;\r
860 \r
861   GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));\r
862 \r
863   switch (GST_EVENT_TYPE (event)) {\r
864     case GST_EVENT_NEWSEGMENT:\r
865     {\r
866       GstFormat format;\r
867       gdouble rate, arate;\r
868       gint64 start, stop, time, offset = 0;\r
869       PiffDemuxStream *stream;\r
870       gint idx;\r
871       gboolean update;\r
872       GstSegment segment;\r
873 \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
882           &segment);\r
883 \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
887         goto exit;\r
888       }\r
889 \r
890       /* we only expect a BYTE segment, e.g. following a seek */\r
891       if (format == GST_FORMAT_BYTES) {\r
892         if (start > 0) {\r
893           gint64 requested_seek_time;\r
894           guint64 seek_offset;\r
895 \r
896           offset = start;\r
897 \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
904 \r
905           if (offset == seek_offset) {\r
906             start = requested_seek_time;\r
907           } else {\r
908             gst_piffdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL,\r
909                 &start);\r
910             start = MAX (start, 0);\r
911           }\r
912         }\r
913         if (stop > 0) {\r
914           gst_piffdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL,\r
915               &stop);\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
919         }\r
920       }\r
921 #if 0\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
926         int i = -1;\r
927 \r
928           demux->neededbytes = 16;\r
929           demux->state = PIFFDEMUX_STATE_INITIAL;\r
930           demux->offset = 0;\r
931 \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
938         }\r
939 \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
947 \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
950 \r
951            /* clear leftover in current segment, if any */\r
952           gst_adapter_clear (demux->adapter);\r
953 \r
954           goto exit;\r
955       }\r
956 #endif\r
957       else {\r
958         GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");\r
959         goto exit;\r
960       }\r
961 \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
969 \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
973 \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
979       if (stream) {\r
980         demux->todrop = stream->samples[idx].offset - offset;\r
981         demux->neededbytes = demux->todrop + stream->samples[idx].size;\r
982       } else {\r
983         /* set up for EOS */\r
984         demux->neededbytes = -1;\r
985         demux->todrop = 0;\r
986       }\r
987     exit:\r
988       gst_event_unref (event);\r
989       res = TRUE;\r
990       goto drop;\r
991       break;\r
992     }\r
993     case GST_EVENT_FLUSH_STOP:\r
994     {\r
995       /* clean up, force EOS if no more info follows */\r
996       gst_adapter_clear (demux->adapter);\r
997       demux->offset = 0;\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
1002       break;\r
1003     }\r
1004     case GST_EVENT_EOS:\r
1005       break;\r
1006     default:\r
1007       break;\r
1008   }\r
1009 \r
1010   res = gst_pad_event_default (demux->sinkpad, event);\r
1011 \r
1012 drop:\r
1013   return res;\r
1014 }\r
1015 \r
1016 \r
1017 static void\r
1018 gst_piffdemux_stream_free (GstPiffDemux * piffdemux, PiffDemuxStream * stream)\r
1019 {\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
1023   }\r
1024   g_free (stream->samples);\r
1025   if (stream->caps)\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
1030   g_free (stream);\r
1031 }\r
1032 \r
1033 \r
1034 static GstStateChangeReturn\r
1035 gst_piffdemux_change_state (GstElement * element, GstStateChange transition)\r
1036 {\r
1037   GstPiffDemux *piffdemux = GST_PIFFDEMUX (element);\r
1038   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;\r
1039 \r
1040   switch (transition) {\r
1041     case GST_STATE_CHANGE_PAUSED_TO_READY:\r
1042       break;\r
1043     default:\r
1044       break;\r
1045   }\r
1046 \r
1047   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);\r
1048 \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
1066       break;\r
1067     }\r
1068     default:\r
1069       break;\r
1070   }\r
1071 \r
1072   return result;\r
1073 }\r
1074 \r
1075 static void\r
1076 piffdemux_post_global_tags (GstPiffDemux * piffdemux)\r
1077 {\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
1086   }\r
1087 }\r
1088 \r
1089 \r
1090 /* caller verifies at least 8 bytes in buf */\r
1091 static void\r
1092 extract_initial_length_and_fourcc (const guint8 * data, guint size,\r
1093     guint64 * plength, guint32 * pfourcc)\r
1094 {\r
1095   guint64 length;\r
1096   guint32 fourcc;\r
1097 \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
1102 \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
1110   }\r
1111 \r
1112   if (plength)\r
1113     *plength = length;\r
1114   if (pfourcc)\r
1115     *pfourcc = fourcc;\r
1116 }\r
1117 \r
1118 static gboolean\r
1119 piffdemux_update_sample_offset (GstPiffDemux * piffdemu, PiffDemuxStream * stream, gint64 uuid_offset)\r
1120 {\r
1121   PiffDemuxSample *sample;\r
1122   gint i;\r
1123 \r
1124   sample = stream->samples ;\r
1125   for (i = 0; i < stream->n_samples; i++)\r
1126   {\r
1127     sample->offset = sample->offset + uuid_offset;\r
1128     sample++;\r
1129   }\r
1130   return TRUE;\r
1131 }\r
1132 \r
1133 static uuid_type_t\r
1134 piffdemux_get_uuid_type(GstPiffDemux * piffdemux, GstByteReader *uuid_data, gint64 *uuid_offset)\r
1135 {\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
1140   int i = 0;\r
1141 \r
1142   if (!gst_byte_reader_get_uint32_be (uuid_data, &box_len))\r
1143     goto invalid_uuid;\r
1144 \r
1145   /* Skipping fourcc */\r
1146   if (!gst_byte_reader_skip (uuid_data, 4))\r
1147     goto invalid_uuid;\r
1148 \r
1149   if (box_len == 1)\r
1150   {\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
1155 \r
1156     *uuid_offset = box_long_len;\r
1157   }\r
1158   else\r
1159   {\r
1160     GST_DEBUG ("Box Len = %d", box_len);\r
1161     *uuid_offset = box_len;\r
1162   }\r
1163 \r
1164   //g_print ("\n\n\n 0x");\r
1165   for (i = 0; i < sizeof (uuid); i++)\r
1166   {\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
1170   }\r
1171   //g_print ("\n\n\n");\r
1172 \r
1173   if (!memcmp(uuid, tfxd_uuid, sizeof (uuid_t)))\r
1174   {\r
1175     GST_INFO ("Found TFXD box");\r
1176     return UUID_TFXD;\r
1177   }\r
1178   else if (!memcmp(uuid, tfrf_uuid, sizeof (uuid_t)))\r
1179   {\r
1180     GST_INFO ("Found TFRF box");\r
1181     return UUID_TFRF;\r
1182   }\r
1183   else if (!memcmp(uuid, encrypt_uuid, sizeof (uuid_t)))\r
1184   {\r
1185     GST_INFO ("Found sample encryption box");\r
1186     return UUID_SAMPLE_ENCRYPT;\r
1187   }\r
1188   else\r
1189   {\r
1190     GST_WARNING ("Not an valid UUID box..");\r
1191     goto invalid_uuid;\r
1192   }\r
1193   return uuid_type;\r
1194 \r
1195 invalid_uuid:\r
1196   GST_ERROR ("Error in parsing UUID atom...");\r
1197   return UUID_UNKNOWN;\r
1198 }\r
1199 \r
1200 static gboolean\r
1201 piffdemux_parse_sample_encryption(GstPiffDemux * piffdemux, GstByteReader *sample_encrypt, PiffDemuxStream * stream)\r
1202 {\r
1203   guint32 flags = 0;\r
1204   guint32 sample_count = 0;\r
1205   guint32 i = 0;\r
1206   guint32 algo_id;\r
1207   guint8 iv_size = 0;\r
1208 \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
1212 \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
1217 \r
1218     /* get IV size */\r
1219     if (!gst_byte_reader_get_uint8 (sample_encrypt, &iv_size))\r
1220       goto invalid_encryption;\r
1221 \r
1222     // TODO: need to add reading of KID\r
1223     } else {\r
1224     GST_INFO_OBJECT (piffdemux, "Override flags are not present... taking default IV_Size = 8");\r
1225     iv_size = 8;\r
1226   }\r
1227 \r
1228   /* Get sample count*/\r
1229   if (!gst_byte_reader_get_uint32_be (sample_encrypt, &sample_count))\r
1230     goto invalid_encryption;\r
1231 \r
1232   GST_INFO_OBJECT (piffdemux, "Sample count = %d", sample_count);\r
1233 \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
1238   }\r
1239 \r
1240   for (i = 0; i < stream->n_samples; i++) {\r
1241     guint8 iv_idx = iv_size;\r
1242 \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
1248     }\r
1249 \r
1250     memset (stream->samples[i].iv, 0x00, iv_size);\r
1251 \r
1252     iv_idx = 0;\r
1253     while (iv_idx < iv_size) {\r
1254       /* get IV byte */\r
1255       if (!gst_byte_reader_get_uint8 (sample_encrypt, &(stream->samples[i].iv[iv_idx])))\r
1256         goto invalid_encryption;\r
1257 \r
1258       iv_idx++;\r
1259     }\r
1260 \r
1261 #ifdef DEBUG_IV\r
1262   {\r
1263     guint8 tmp_idx = 0;\r
1264     g_print ("sample[%d] : 0x ", i);\r
1265 \r
1266     while (tmp_idx < iv_size ) {\r
1267       g_print ("%02x ", stream->samples[i].iv[tmp_idx]);\r
1268       tmp_idx++;\r
1269     }\r
1270     g_print ("\n");\r
1271   }\r
1272 #endif\r
1273 \r
1274     if (flags & SE_USE_SUBSAMPLE_ENCRYPTION) {\r
1275       guint16 n_entries;\r
1276       guint16 n_idx;\r
1277 \r
1278       /* NumberofEntries in SubSampleEncryption */\r
1279       if (!gst_byte_reader_get_uint16_be (sample_encrypt, &n_entries))\r
1280         goto invalid_encryption;\r
1281 \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
1286       }\r
1287 \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
1290       {\r
1291         GST_ERROR_OBJECT (piffdemux, "Failed to allocate memory...");\r
1292         goto invalid_encryption;\r
1293       }\r
1294 \r
1295       stream->samples[i].sub_encry->n_entries = n_entries;\r
1296 \r
1297       GST_DEBUG_OBJECT (piffdemux,"No. of subsample entries = %d", stream->samples[i].sub_encry->n_entries);\r
1298 \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
1302 \r
1303        GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofClearData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData);\r
1304 \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
1307 \r
1308         GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofEncryptData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData);\r
1309       }\r
1310     }\r
1311   }\r
1312 \r
1313   return TRUE;\r
1314 \r
1315 invalid_encryption:\r
1316   {\r
1317     GST_WARNING_OBJECT (piffdemux, "invalid sample encryption header");\r
1318     return FALSE;\r
1319   }\r
1320 }\r
1321 \r
1322 \r
1323 static gboolean\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
1328 {\r
1329   guint64 timestamp;\r
1330   gint32 data_offset = 0;\r
1331   guint32 flags = 0, first_flags = 0, samples_count = 0;\r
1332   gint i;\r
1333   guint8 *data;\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
1338 \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
1342       *base_offset);\r
1343 \r
1344   //Resetting the samples\r
1345   stream->n_samples = 0;\r
1346 \r
1347   if (!gst_byte_reader_skip (trun, 1) ||\r
1348       !gst_byte_reader_get_uint24_be (trun, &flags))\r
1349     goto fail;\r
1350 \r
1351   if (!gst_byte_reader_get_uint32_be (trun, &samples_count))\r
1352     goto fail;\r
1353 \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
1357       goto fail;\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
1363     }\r
1364     *running_offset = *base_offset + data_offset;\r
1365   } else {\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
1372       ismv = TRUE;\r
1373     }\r
1374     if (*running_offset == -1)\r
1375       *running_offset = *base_offset;\r
1376   }\r
1377 \r
1378   GST_LOG_OBJECT (piffdemux, "running offset now %" G_GINT64_FORMAT,\r
1379       *running_offset);\r
1380   GST_LOG_OBJECT (piffdemux, "trun offset %d, flags 0x%x, entries %d",\r
1381       data_offset, flags, samples_count);\r
1382 \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
1388     } else {\r
1389       if (!gst_byte_reader_get_uint32_be (trun, &first_flags))\r
1390         goto fail;\r
1391       GST_LOG_OBJECT (piffdemux, "first flags: 0x%x", first_flags);\r
1392     }\r
1393   }\r
1394 \r
1395   /* FIXME ? spec says other bits should also be checked to determine\r
1396    * entry size (and prefix size for that matter) */\r
1397   entry_size = 0;\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
1402     entry_size += 4;\r
1403   }\r
1404   if (flags & TR_SAMPLE_SIZE) {\r
1405     GST_LOG_OBJECT (piffdemux, "entry size present");\r
1406     size_offset = entry_size;\r
1407     entry_size += 4;\r
1408   }\r
1409   if (flags & TR_SAMPLE_FLAGS) {\r
1410     GST_LOG_OBJECT (piffdemux, "entry flags present");\r
1411     flags_offset = entry_size;\r
1412     entry_size += 4;\r
1413   }\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
1417     entry_size += 4;\r
1418   }\r
1419 \r
1420   if (!piff_atom_parser_has_chunks (trun, samples_count, entry_size))\r
1421     goto fail;\r
1422   data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);\r
1423 \r
1424   if (stream->n_samples >=\r
1425       PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (PiffDemuxSample))\r
1426     goto index_too_big;\r
1427 \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
1431 \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
1436   else\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
1441 \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
1445     timestamp = 0;\r
1446   } else {\r
1447     /* subsequent fragments extend stream */\r
1448     timestamp =\r
1449         stream->samples[stream->n_samples - 1].timestamp +\r
1450         stream->samples[stream->n_samples - 1].duration;\r
1451   }\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
1455 \r
1456     /* first read sample data */\r
1457     if (flags & TR_SAMPLE_DURATION) {\r
1458       dur = PIFF_UINT32 (data + dur_offset);\r
1459     } else {\r
1460       dur = d_sample_duration;\r
1461     }\r
1462     if (flags & TR_SAMPLE_SIZE) {\r
1463       size = PIFF_UINT32 (data + size_offset);\r
1464     } else {\r
1465       size = d_sample_size;\r
1466     }\r
1467 \r
1468     GST_DEBUG_OBJECT(piffdemux,"Size of sample %d is %d", i, size);\r
1469 \r
1470     if (flags & TR_FIRST_SAMPLE_FLAGS) {\r
1471       if (i == 0) {\r
1472         sflags = first_flags;\r
1473       } else {\r
1474         sflags = d_sample_flags;\r
1475       }\r
1476     } else if (flags & TR_SAMPLE_FLAGS) {\r
1477       sflags = PIFF_UINT32 (data + flags_offset);\r
1478     } else {\r
1479       sflags = d_sample_flags;\r
1480     }\r
1481     if (flags & TR_COMPOSITION_TIME_OFFSETS) {\r
1482       ct = PIFF_UINT32 (data + ct_offset);\r
1483     } else {\r
1484       ct = 0;\r
1485     }\r
1486     data += entry_size;\r
1487 \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
1500 \r
1501     stream->samples[i] = *sample;\r
1502 \r
1503     *running_offset += size;\r
1504     timestamp += dur;\r
1505     sample++;\r
1506 \r
1507     /* calculate total duration of the present fragment */\r
1508     total_duration += gst_util_uint64_scale (dur, GST_SECOND, stream->timescale);\r
1509   }\r
1510 \r
1511   stream->sample_index = 0;\r
1512 \r
1513   stream->n_samples += samples_count;\r
1514 \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
1519 \r
1520   return TRUE;\r
1521 \r
1522 fail:\r
1523   {\r
1524     GST_WARNING_OBJECT (piffdemux, "failed to parse trun");\r
1525     return FALSE;\r
1526   }\r
1527 out_of_memory:\r
1528   {\r
1529     GST_WARNING_OBJECT (piffdemux, "failed to allocate %d samples",\r
1530         stream->n_samples);\r
1531     return FALSE;\r
1532   }\r
1533 index_too_big:\r
1534   {\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
1538     return FALSE;\r
1539   }\r
1540 }\r
1541 \r
1542 static gboolean\r
1543 piffdemux_parse_mfhd (GstPiffDemux * piffdemux, GstByteReader * mfhd)\r
1544 {\r
1545   guint32 seq_num = 0;\r
1546 \r
1547   if (!gst_byte_reader_skip (mfhd, 4))\r
1548     goto invalid_mfhd;\r
1549 \r
1550   if (!gst_byte_reader_get_uint32_be (mfhd, &seq_num))\r
1551     goto invalid_mfhd;\r
1552 \r
1553   GST_DEBUG_OBJECT (piffdemux, "sequence number present in mfhd = %d", seq_num);\r
1554 \r
1555   return TRUE;\r
1556 \r
1557 invalid_mfhd:\r
1558   {\r
1559     GST_WARNING_OBJECT (piffdemux, "invalid movie fragment header");\r
1560     return FALSE;\r
1561   }\r
1562 }\r
1563 \r
1564 \r
1565 static gboolean\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
1570 {\r
1571   guint32 flags = 0;\r
1572   guint32 track_id = 0;\r
1573 \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
1577 \r
1578   if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))\r
1579     goto invalid_track;\r
1580 \r
1581   GST_DEBUG_OBJECT (piffdemux, "trackID = %d", track_id);\r
1582 \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
1587   }\r
1588 \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
1593 \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
1597 \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
1601 \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
1605 \r
1606   return TRUE;\r
1607 \r
1608 invalid_track:\r
1609   {\r
1610     GST_WARNING_OBJECT (piffdemux, "invalid track fragment header");\r
1611     return FALSE;\r
1612   }\r
1613 }\r
1614 \r
1615 static gboolean\r
1616 piffdemux_parse_tfxd (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfxd)\r
1617 {\r
1618   guint8 version = 0;\r
1619 \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
1621 \r
1622   if (!gst_byte_reader_get_uint8 (tfxd, &version))\r
1623     goto invalid_tfxd;\r
1624 \r
1625   if (!gst_byte_reader_skip (tfxd, 3))\r
1626     goto invalid_tfxd;\r
1627 \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
1632       return FALSE;\r
1633     }\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
1638 \r
1639     // TODO: presentation will be ended based on timeout in souphttpsrc in lookaheadcnt = 0 case\r
1640   }\r
1641 \r
1642   if (version == 1) {\r
1643     guint64 duration = 0;\r
1644     guint64 timestamp = 0;\r
1645 \r
1646     GST_LOG_OBJECT (piffdemux, "Time and Duration are in 64-bit format...");\r
1647     if (!gst_byte_reader_get_uint64_be (tfxd, &timestamp))\r
1648       goto invalid_tfxd;\r
1649     if (!gst_byte_reader_get_uint64_be (tfxd, &duration))\r
1650       goto invalid_tfxd;\r
1651 \r
1652     GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT64_FORMAT" and duration of fragment = %"G_GUINT64_FORMAT,\r
1653         timestamp, duration);\r
1654 \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
1659         return FALSE;\r
1660       }\r
1661 \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
1665     }\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
1670 \r
1671     if (!gst_byte_reader_get_uint32_be (tfxd, &timestamp))\r
1672       goto invalid_tfxd;\r
1673 \r
1674     if (!gst_byte_reader_get_uint32_be (tfxd, &duration))\r
1675       goto invalid_tfxd;\r
1676 \r
1677     GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT32_FORMAT" and duration of fragment = %"G_GUINT32_FORMAT,\r
1678         timestamp, duration);\r
1679 \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
1684         return FALSE;\r
1685       }\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
1689     }\r
1690   } else {\r
1691     GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfxd...");\r
1692     return FALSE;\r
1693   }\r
1694 \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
1698   }\r
1699 \r
1700   return TRUE;\r
1701 \r
1702 invalid_tfxd:\r
1703   GST_ERROR ("Invalid TFXD atom...");\r
1704   return FALSE;\r
1705 }\r
1706 \r
1707 \r
1708 static gboolean\r
1709 piffdemux_parse_tfrf (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfrf)\r
1710 {\r
1711   guint8 version = 0;\r
1712   guint8 frag_cnt = 0;\r
1713   guint8 i = 0;\r
1714 \r
1715   /* Getting version info */\r
1716   if (!gst_byte_reader_get_uint8 (tfrf, &version))\r
1717     goto invalid_tfrf;\r
1718 \r
1719   /* skipping reserved flags */\r
1720   if (!gst_byte_reader_skip (tfrf, 3))\r
1721     goto invalid_tfrf;\r
1722 \r
1723   if (!gst_byte_reader_get_uint8 (tfrf, &frag_cnt))\r
1724     goto invalid_tfrf;\r
1725 \r
1726   GST_INFO_OBJECT (piffdemux, "Subsequent fragments info count = %d", frag_cnt);\r
1727 \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
1731     return FALSE;\r
1732   }\r
1733 \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
1738 \r
1739   // TODO: Duration and timestamp values need to be posted to msl using g_signal_emit\r
1740 \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
1745 \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
1749       return FALSE;\r
1750     }\r
1751 \r
1752     for (i = 0; i < frag_cnt; i++) {\r
1753       if (!gst_byte_reader_get_uint64_be (tfrf, &timestamp))\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
1761     }\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
1766 \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
1770       return FALSE;\r
1771     }\r
1772 \r
1773     for (i = 0; i < frag_cnt; i++) {\r
1774       if (!gst_byte_reader_get_uint32_be (tfrf, &timestamp))\r
1775         goto invalid_tfrf;\r
1776       if (!gst_byte_reader_get_uint32_be (tfrf, &duration))\r
1777         goto invalid_tfrf;\r
1778 \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
1783     }\r
1784   } else {\r
1785     GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfrf...");\r
1786     return FALSE;\r
1787   }\r
1788 \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
1791 \r
1792   return TRUE;\r
1793 \r
1794 invalid_tfrf:\r
1795   GST_ERROR_OBJECT (piffdemux, "Invalid TFRF atom...");\r
1796   return FALSE;\r
1797 }\r
1798 \r
1799 \r
1800 static gboolean\r
1801 piffdemux_parse_moof (GstPiffDemux * piffdemux, const guint8 * buffer, guint length,\r
1802     guint64 moof_offset, PiffDemuxStream * stream)\r
1803 {\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
1811 \r
1812   /* NOTE @stream ignored */\r
1813 \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
1817 \r
1818   /* unknown base_offset to start with */\r
1819   base_offset = running_offset = -1;\r
1820 \r
1821   mfhd_node = piffdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);\r
1822   if (!mfhd_node)\r
1823     goto missing_mfhd;\r
1824 \r
1825   if (!piffdemux_parse_mfhd (piffdemux, &mfhd_data))\r
1826     goto missing_mfhd;\r
1827 \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
1831     tfhd_node =\r
1832         piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,\r
1833         &tfhd_data);\r
1834     if (!tfhd_node)\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
1839 \r
1840     if (G_UNLIKELY (base_offset < -1))\r
1841       goto lost_offset;\r
1842 \r
1843     /* Track Run node */\r
1844     trun_node =\r
1845         piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,\r
1846         &trun_data);\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
1850           &running_offset);\r
1851       /* iterate all siblings */\r
1852       trun_node = piffdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,\r
1853           &trun_data);\r
1854     }\r
1855 \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
1860 \r
1861       gst_byte_reader_init (&uuid_data, buffer, PIFF_UINT32 (buffer));\r
1862 \r
1863       uuid_type = piffdemux_get_uuid_type (piffdemux, &uuid_data, &uuid_offset);\r
1864 \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
1869           goto fail;\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
1873           goto fail;\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
1878           goto fail;\r
1879       } else {\r
1880         GST_WARNING_OBJECT (piffdemux, "Ignoring Wrong UUID...");\r
1881       }\r
1882 \r
1883       /* iterate all siblings */\r
1884       uuid_node = piffdemux_tree_get_sibling_by_type (uuid_node, FOURCC_uuid);\r
1885     }\r
1886 \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
1890         goto fail;\r
1891       }\r
1892 \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
1896 \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
1900           goto fail;\r
1901         }\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
1907       }\r
1908     }\r
1909 \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
1914 \r
1915     /* iterate all siblings */\r
1916     traf_node = piffdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);\r
1917   }\r
1918   g_node_destroy (moof_node);\r
1919   return TRUE;\r
1920 \r
1921 missing_mfhd:\r
1922   {\r
1923     GST_DEBUG_OBJECT (piffdemux, "missing mfhd box");\r
1924     goto fail;\r
1925   }\r
1926 \r
1927 missing_tfhd:\r
1928   {\r
1929     GST_DEBUG_OBJECT (piffdemux, "missing tfhd box");\r
1930     goto fail;\r
1931   }\r
1932 lost_offset:\r
1933   {\r
1934     GST_DEBUG_OBJECT (piffdemux, "lost offset");\r
1935     goto fail;\r
1936   }\r
1937 fail:\r
1938   {\r
1939     g_node_destroy (moof_node);\r
1940 \r
1941     GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,\r
1942         ("This file is corrupt and cannot be played."), (NULL));\r
1943 \r
1944     return FALSE;\r
1945   }\r
1946 }\r
1947 \r
1948 \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
1951  *\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
1954  * @offset.\r
1955  */\r
1956 static gboolean\r
1957 gst_piffdemux_activate_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,\r
1958     guint32 seg_idx, guint64 offset)\r
1959 {\r
1960   GstEvent *event;\r
1961   PiffDemuxSegment *segment;\r
1962   guint64 seg_time;\r
1963   guint64 start, stop, time;\r
1964   gdouble rate;\r
1965 \r
1966   GST_LOG_OBJECT (piffdemux, "activate segment %d, offset %" G_GUINT64_FORMAT,\r
1967       seg_idx, offset);\r
1968 \r
1969   /* update the current segment */\r
1970   stream->segment_index = seg_idx;\r
1971 \r
1972   /* get the segment */\r
1973   segment = &stream->segments[seg_idx];\r
1974 \r
1975   if (G_UNLIKELY (offset < segment->time)) {\r
1976     GST_WARNING_OBJECT (piffdemux, "offset < segment->time %" G_GUINT64_FORMAT,\r
1977         segment->time);\r
1978     return FALSE;\r
1979   }\r
1980 \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
1986         segment->time);\r
1987     return FALSE;\r
1988   }\r
1989 \r
1990   /* get time in this segment */\r
1991   seg_time = offset - segment->time;\r
1992 \r
1993   GST_LOG_OBJECT (piffdemux, "seg_time %" GST_TIME_FORMAT,\r
1994       GST_TIME_ARGS (seg_time));\r
1995 \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
1999     return FALSE;\r
2000   }\r
2001 \r
2002   /* piffdemux->segment.stop is in outside-time-realm, whereas\r
2003    * segment->media_stop is in track-time-realm.\r
2004    *\r
2005    * In order to compare the two, we need to bring segment.stop\r
2006    * into the track-time-realm */\r
2007 \r
2008   stop = piffdemux->segment.stop;\r
2009   if (stop == -1)\r
2010     stop = piffdemux->segment.duration;\r
2011   if (stop == -1)\r
2012     stop = segment->media_stop;\r
2013   else\r
2014     stop =\r
2015         MIN (segment->media_stop, stop - segment->time + segment->media_start);\r
2016 \r
2017   if (piffdemux->segment.rate >= 0) {\r
2018     start = MIN (segment->media_start + seg_time, stop);\r
2019     time = offset;\r
2020   } else {\r
2021     if (segment->media_start >= piffdemux->segment.start) {\r
2022       start = segment->media_start;\r
2023       time = segment->time;\r
2024     } else {\r
2025       start = piffdemux->segment.start;\r
2026       time = segment->time + (piffdemux->segment.start - segment->media_start);\r
2027     }\r
2028 \r
2029     start = MAX (segment->media_start, piffdemux->segment.start);\r
2030     stop = MIN (segment->media_start + seg_time, stop);\r
2031   }\r
2032 \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
2036 \r
2037   /* combine global rate with that of the segment */\r
2038   rate = segment->rate * piffdemux->segment.rate;\r
2039 \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
2044 \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
2053 \r
2054   return TRUE;\r
2055 }\r
2056 \r
2057 \r
2058 /* prepare to get the current sample of @stream, getting essential values.\r
2059  *\r
2060  * This function will also prepare and send the segment when needed.\r
2061  *\r
2062  * Return FALSE if the stream is EOS.\r
2063  */\r
2064 static gboolean\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
2068 {\r
2069   PiffDemuxSample *sample;\r
2070   guint64 time_position;\r
2071   guint32 seg_idx;\r
2072 \r
2073   g_return_val_if_fail (stream != NULL, FALSE);\r
2074 \r
2075   time_position = stream->time_position;\r
2076   if (G_UNLIKELY (time_position == -1))\r
2077     goto eos;\r
2078 \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
2084 \r
2085     /* nothing found, we're really eos */\r
2086     if (seg_idx == -1)\r
2087       goto eos;\r
2088   }\r
2089 \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
2093 \r
2094   GST_LOG_OBJECT (piffdemux, "segment active, index = %u of %u",\r
2095       stream->sample_index, stream->n_samples);\r
2096 \r
2097   if (G_UNLIKELY (stream->sample_index >= stream->n_samples))\r
2098     goto eos;\r
2099 \r
2100 \r
2101   /* now get the info for the sample we're at */\r
2102   sample = &stream->samples[stream->sample_index];\r
2103 \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
2109 \r
2110   return TRUE;\r
2111 \r
2112   /* special cases */\r
2113 eos:\r
2114   {\r
2115     stream->time_position = -1;\r
2116     return FALSE;\r
2117   }\r
2118 }\r
2119 \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
2124     GstBuffer * buf)\r
2125 {\r
2126   guint8 *data;\r
2127   guint size, nsize = 0;\r
2128   gchar *str;\r
2129 \r
2130   data = GST_BUFFER_DATA (buf);\r
2131   size = GST_BUFFER_SIZE (buf);\r
2132 \r
2133   if (G_UNLIKELY (stream->subtype != FOURCC_text)) {\r
2134     return buf;\r
2135   }\r
2136 \r
2137   if (G_LIKELY (size >= 2)) {\r
2138     nsize = GST_READ_UINT16_BE (data);\r
2139     nsize = MIN (nsize, size - 2);\r
2140   }\r
2141 \r
2142   GST_LOG_OBJECT (piffdemux, "3GPP timed text subtitle: %d/%d", nsize, size);\r
2143 \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
2147   if (str) {\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
2152   } else {\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
2156   }\r
2157 \r
2158   /* FIXME ? convert optional subsequent style info to markup */\r
2159 \r
2160   return buf;\r
2161 }\r
2162 \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
2166  */\r
2167 static gboolean\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
2172 {\r
2173   GstFlowReturn ret = GST_FLOW_OK;\r
2174 \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
2181       goto exit;\r
2182     }\r
2183   }\r
2184 \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
2190   }\r
2191 \r
2192   /* send out pending buffers */\r
2193   while (stream->buffers) {\r
2194     GstBuffer *buffer = (GstBuffer *) stream->buffers->data;\r
2195 \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
2200     }\r
2201     gst_buffer_set_caps (buffer, stream->caps);\r
2202 \r
2203     gst_pad_push (piffdemux->srcpad, buffer);\r
2204 \r
2205     stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);\r
2206   }\r
2207 \r
2208   /* we're going to modify the metadata */\r
2209   buf = gst_buffer_make_metadata_writable (buf);\r
2210 \r
2211   /* for subtitle processing */\r
2212   if (G_UNLIKELY (stream->need_process))\r
2213     buf = gst_piffdemux_process_buffer (piffdemux, stream, buf);\r
2214 \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
2219 \r
2220   if (G_UNLIKELY (stream->padding)) {\r
2221     GST_BUFFER_DATA (buf) += stream->padding;\r
2222     GST_BUFFER_SIZE (buf) -= stream->padding;\r
2223   }\r
2224 \r
2225   if (G_UNLIKELY (buf == NULL))\r
2226     goto exit;\r
2227 \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
2232   }\r
2233 \r
2234   if (!keyframe)\r
2235     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);\r
2236 \r
2237  //g_print ("\n\npad caps : %s\n\n", gst_caps_to_string (gst_pad_get_caps (stream->pad)));\r
2238 \r
2239   //gst_buffer_set_caps (buf, stream->caps); // commenting to avoid caps by setting properties\r
2240 \r
2241 \r
2242   // TODO: need to see how resolution switch will work\r
2243   gst_buffer_set_caps (buf, stream->caps);\r
2244 \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
2249 \r
2250 #ifdef DEC_OUT_FRAME_DUMP\r
2251     {\r
2252         int written = 0;\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
2256     }\r
2257 #endif\r
2258 \r
2259   ret = gst_pad_push (piffdemux->srcpad, buf);\r
2260 \r
2261 exit:\r
2262   return ret;\r
2263 }\r
2264 \r
2265 \r
2266 /*\r
2267  * next_entry_size\r
2268  *\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
2271  */\r
2272 static guint64\r
2273 next_entry_size (GstPiffDemux * demux)\r
2274 {\r
2275   PiffDemuxStream *stream = demux->stream;\r
2276   PiffDemuxSample *sample;\r
2277 \r
2278   GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,\r
2279       demux->offset);\r
2280 \r
2281   GST_DEBUG_OBJECT (demux, "demux->sample_index = %d", stream->sample_index);\r
2282 \r
2283   if (stream->sample_index == -1)\r
2284     stream->sample_index = 0;\r
2285 \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
2289     return -1;\r
2290   }\r
2291 \r
2292   sample = &stream->samples[stream->sample_index];\r
2293 \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
2298 \r
2299   GST_LOG_OBJECT (demux, "stream : demux->offset :%"G_GUINT64_FORMAT, demux->offset);\r
2300 \r
2301   stream = demux->stream;\r
2302   sample = &stream->samples[stream->sample_index];\r
2303 \r
2304   if (sample->offset >= demux->offset) {\r
2305     demux->todrop = sample->offset - demux->offset;\r
2306     return sample->size + demux->todrop;\r
2307   }\r
2308 \r
2309   GST_DEBUG_OBJECT (demux,\r
2310       "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);\r
2311   return -1;\r
2312 }\r
2313 \r
2314 static void\r
2315 gst_piffdemux_post_progress (GstPiffDemux * demux, gint num, gint denom)\r
2316 {\r
2317   gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);\r
2318 \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
2322 }\r
2323 \r
2324 static gboolean\r
2325 piffdemux_seek_offset (GstPiffDemux * demux, guint64 offset)\r
2326 {\r
2327   GstEvent *event;\r
2328   gboolean res = 0;\r
2329 \r
2330   GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);\r
2331 \r
2332   event =\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
2336 \r
2337   res = gst_pad_push_event (demux->sinkpad, event);\r
2338 \r
2339   return res;\r
2340 }\r
2341 \r
2342 static GstFlowReturn\r
2343 gst_piffdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)\r
2344 {\r
2345   GstPiffDemux *demux;\r
2346   GstFlowReturn ret = GST_FLOW_OK;\r
2347   demux = GST_PIFFDEMUX (gst_pad_get_parent (sinkpad));\r
2348 \r
2349   gst_adapter_push (demux->adapter, inbuf);\r
2350 \r
2351   /* we never really mean to buffer that much */\r
2352   if (demux->neededbytes == -1)\r
2353     goto eos;\r
2354 \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
2357 \r
2358   while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&\r
2359       (ret == GST_FLOW_OK)) {\r
2360 \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
2364 \r
2365     switch (demux->state) {\r
2366       case PIFFDEMUX_STATE_INITIAL:{\r
2367         const guint8 *data;\r
2368         guint32 fourcc;\r
2369         guint64 size;\r
2370 \r
2371         data = gst_adapter_peek (demux->adapter, demux->neededbytes);\r
2372 \r
2373         /* get fourcc/length, set neededbytes */\r
2374         extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,\r
2375             &size, &fourcc);\r
2376         GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "\r
2377             "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);\r
2378         if (size == 0) {\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
2383 \r
2384           ret = GST_FLOW_ERROR;\r
2385           break;\r
2386         }\r
2387 \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
2394 \r
2395             /* Only post, event on pads is done after newsegment */\r
2396             piffdemux_post_global_tags (demux);\r
2397           } else {\r
2398              GST_ERROR_OBJECT (demux, "mdata received before moof.. not handled");\r
2399              goto unknown_stream;\r
2400           }\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
2407           break;\r
2408         } else {\r
2409           demux->neededbytes = size;\r
2410           demux->state = PIFFDEMUX_STATE_HEADER;\r
2411         }\r
2412         break;\r
2413       }\r
2414       case PIFFDEMUX_STATE_HEADER:{\r
2415         const guint8 *data;\r
2416         guint32 fourcc;\r
2417 \r
2418         GST_DEBUG_OBJECT (demux, "In header");\r
2419 \r
2420         data = gst_adapter_peek (demux->adapter, demux->neededbytes);\r
2421 \r
2422         /* parse the header */\r
2423         extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,\r
2424             &fourcc);\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
2430               goto done;\r
2431             }\r
2432             demux->moof_rcvd = TRUE;\r
2433         }  else {\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
2438         }\r
2439 \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
2450 \r
2451           /* Only post, event on pads is done after newsegment */\r
2452           piffdemux_post_global_tags (demux);\r
2453         } else {\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
2459         }\r
2460 \r
2461         break;\r
2462       }\r
2463       case PIFFDEMUX_STATE_BUFFER_MDAT:{\r
2464         GstBuffer *buf;\r
2465 \r
2466         GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,\r
2467             demux->offset);\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
2473         else\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
2479 \r
2480         break;\r
2481       }\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
2488 \r
2489         GST_DEBUG_OBJECT (demux,\r
2490             "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);\r
2491 \r
2492         if (demux->fragmented) {\r
2493           GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,\r
2494               demux->mdatleft);\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
2499 \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
2503 \r
2504               ret = GST_FLOW_ERROR;\r
2505               break;\r
2506             }\r
2507             demux->mdatleft -= demux->neededbytes;\r
2508           } else {\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
2517             break;\r
2518           }\r
2519         }\r
2520 \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
2526         }\r
2527 \r
2528         if ( !stream->sent_nsevent) {\r
2529           //TODO: better to parse sink event function and send that new_segment\r
2530 #if 1\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
2533 #else\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
2536 #endif\r
2537 \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
2540 \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
2544           }\r
2545           stream->sent_nsevent = TRUE;\r
2546         }\r
2547 \r
2548         /* Put data in a buffer, set timestamps, caps, ... */\r
2549         outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);\r
2550 \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
2553 \r
2554         g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);\r
2555 \r
2556         sample = &stream->samples[stream->sample_index];\r
2557 \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
2561 \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
2566 \r
2567         ret = gst_piffdemux_decorate_and_push_buffer (demux, stream, outbuf,\r
2568             timestamp, duration, keyframe, position, demux->offset);\r
2569 \r
2570         stream->sample_index++;\r
2571 \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
2577             demux->offset);\r
2578 \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
2585           //goto eos;\r
2586         }\r
2587         break;\r
2588       }\r
2589       default:\r
2590         goto invalid_state;\r
2591     }\r
2592   }\r
2593 \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
2599   }\r
2600 done:\r
2601   gst_object_unref (demux);\r
2602 \r
2603   return ret;\r
2604 \r
2605   /* ERRORS */\r
2606 unknown_stream:\r
2607   {\r
2608     GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));\r
2609     ret = GST_FLOW_ERROR;\r
2610     goto done;\r
2611   }\r
2612 eos:\r
2613   {\r
2614     GST_DEBUG_OBJECT (demux, "no next entry, EOS");\r
2615     ret = GST_FLOW_UNEXPECTED;\r
2616     goto done;\r
2617   }\r
2618 invalid_state:\r
2619   {\r
2620     GST_ELEMENT_ERROR (demux, STREAM, FAILED,\r
2621         (NULL), ("piffdemuxer invalid state %d", demux->state));\r
2622     ret = GST_FLOW_ERROR;\r
2623     goto done;\r
2624   }\r
2625 newsegment_error:\r
2626   {\r
2627     GST_ELEMENT_ERROR (demux, STREAM, FAILED,\r
2628         (NULL), ("could not send newsegment event"));\r
2629     ret = GST_FLOW_ERROR;\r
2630     goto done;\r
2631   }\r
2632 }\r
2633 \r
2634 static gboolean\r
2635 piffdemux_parse_container (GstPiffDemux * piffdemux, GNode * node, const guint8 * buf,\r
2636     const guint8 * end)\r
2637 {\r
2638   while (G_UNLIKELY (buf < end)) {\r
2639     GNode *child;\r
2640     guint32 len;\r
2641 \r
2642     if (G_UNLIKELY (buf + 4 > end)) {\r
2643       GST_LOG_OBJECT (piffdemux, "buffer overrun");\r
2644       break;\r
2645     }\r
2646     len = PIFF_UINT32 (buf);\r
2647     if (G_UNLIKELY (len == 0)) {\r
2648       GST_LOG_OBJECT (piffdemux, "empty container");\r
2649       break;\r
2650     }\r
2651     if (G_UNLIKELY (len < 8)) {\r
2652       GST_WARNING_OBJECT (piffdemux, "length too short (%d < 8)", len);\r
2653       break;\r
2654     }\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
2658       break;\r
2659     }\r
2660 \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
2665 \r
2666     buf += len;\r
2667   }\r
2668   return TRUE;\r
2669 }\r
2670 \r
2671 \r
2672 static gboolean\r
2673 piffdemux_parse_node (GstPiffDemux * piffdemux, GNode * node, const guint8 * buffer,\r
2674     guint length)\r
2675 {\r
2676   guint32 fourcc = 0;\r
2677   guint32 node_length = 0;\r
2678   const PiffNodeType *type;\r
2679   const guint8 *end;\r
2680 \r
2681   GST_LOG_OBJECT (piffdemux, "piffdemux_parse buffer %p length %u", buffer, length);\r
2682 \r
2683   if (G_UNLIKELY (length < 8))\r
2684     goto not_enough_data;\r
2685 \r
2686   node_length = PIFF_UINT32 (buffer);\r
2687   fourcc = PIFF_FOURCC (buffer + 4);\r
2688 \r
2689   /* ignore empty nodes */\r
2690   if (G_UNLIKELY (fourcc == 0 || node_length == 8))\r
2691     return TRUE;\r
2692 \r
2693   type = piffdemux_type_get (fourcc);\r
2694 \r
2695   end = buffer + length;\r
2696 \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
2700 \r
2701   if (node_length > length)\r
2702     goto broken_atom_size;\r
2703 \r
2704   if (type->flags & PIFF_FLAG_CONTAINER) {\r
2705     piffdemux_parse_container (piffdemux, node, buffer + 8, end);\r
2706   }\r
2707   GST_LOG_OBJECT (piffdemux, "parsed '%" GST_FOURCC_FORMAT "'",\r
2708       GST_FOURCC_ARGS (fourcc));\r
2709   return TRUE;\r
2710 \r
2711 /* ERRORS */\r
2712 not_enough_data:\r
2713   {\r
2714 \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
2718 \r
2719     return FALSE;\r
2720   }\r
2721 broken_atom_size:\r
2722   {\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
2727             length));\r
2728 \r
2729     return FALSE;\r
2730   }\r
2731 }\r
2732 \r
2733 \r
2734 static GNode *\r
2735 piffdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)\r
2736 {\r
2737   GNode *child;\r
2738   guint8 *buffer;\r
2739   guint32 child_fourcc;\r
2740 \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
2744 \r
2745     child_fourcc = PIFF_FOURCC (buffer + 4);\r
2746 \r
2747     if (G_UNLIKELY (child_fourcc == fourcc)) {\r
2748       return child;\r
2749     }\r
2750   }\r
2751   return NULL;\r
2752 }\r
2753 \r
2754 static GNode *\r
2755 piffdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,\r
2756     GstByteReader * parser)\r
2757 {\r
2758   GNode *child;\r
2759   guint8 *buffer;\r
2760   guint32 child_fourcc, child_len;\r
2761 \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
2765 \r
2766     child_len = PIFF_UINT32 (buffer);\r
2767     child_fourcc = PIFF_FOURCC (buffer + 4);\r
2768 \r
2769     if (G_UNLIKELY (child_fourcc == fourcc)) {\r
2770       if (G_UNLIKELY (child_len < (4 + 4)))\r
2771         return NULL;\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
2774       return child;\r
2775     }\r
2776   }\r
2777   return NULL;\r
2778 }\r
2779 \r
2780 static GNode *\r
2781 piffdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,\r
2782     GstByteReader * parser)\r
2783 {\r
2784   GNode *child;\r
2785   guint8 *buffer;\r
2786   guint32 child_fourcc, child_len;\r
2787 \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
2791 \r
2792     child_fourcc = PIFF_FOURCC (buffer + 4);\r
2793 \r
2794     if (child_fourcc == fourcc) {\r
2795       if (parser) {\r
2796         child_len = PIFF_UINT32 (buffer);\r
2797         if (G_UNLIKELY (child_len < (4 + 4)))\r
2798           return NULL;\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
2801       }\r
2802       return child;\r
2803     }\r
2804   }\r
2805   return NULL;\r
2806 }\r
2807 \r
2808 static GNode *\r
2809 piffdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)\r
2810 {\r
2811   return piffdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);\r
2812 }\r
2813 \r
2814 #define _codec(name) \\r
2815   do { \\r
2816     if (codec_name) { \\r
2817       *codec_name = g_strdup (name); \\r
2818     } \\r
2819   } while (0)\r
2820 \r
2821 void\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
2825 {\r
2826   GstCaps *caps = NULL;\r
2827   GstBuffer *dci = NULL;\r
2828 \r
2829   if (codec_data && codec_data_len) {\r
2830     dci = gst_buffer_new_and_alloc (codec_data_len);\r
2831     if (!dci) {\r
2832       GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");\r
2833     } else {\r
2834       memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);\r
2835     }\r
2836   }\r
2837 \r
2838   switch (fourcc) {\r
2839 \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
2848                                                   NULL);\r
2849       break;\r
2850 \r
2851     case FOURCC_ovc1:\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
2859                                                   NULL);\r
2860       break;\r
2861 \r
2862     default: {\r
2863       char *s;\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
2871                                                  NULL);\r
2872       break;\r
2873     }\r
2874   }\r
2875 \r
2876   piffdemux->stream->caps = caps;\r
2877   GST_INFO_OBJECT (piffdemux, "prepared video caps : %s", gst_caps_to_string(caps));\r
2878 }\r
2879 \r
2880 void\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
2883 {\r
2884   GstCaps *caps = NULL;\r
2885   GstBuffer *dci = NULL;\r
2886 \r
2887   if (codec_data && codec_data_len) {\r
2888     dci = gst_buffer_new_and_alloc (codec_data_len);\r
2889     if (!dci) {\r
2890       GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");\r
2891     } else {\r
2892       memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);\r
2893     }\r
2894   }\r
2895 \r
2896   switch (fourcc) {\r
2897 \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
2905                                                   NULL);\r
2906       break;\r
2907 \r
2908     case FOURCC_owma:\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
2912                                                     NULL);\r
2913       break;\r
2914 \r
2915     default: {\r
2916       char *s;\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
2922                                                  NULL);\r
2923       break;\r
2924     }\r
2925   }\r
2926 \r
2927   piffdemux->stream->caps = caps;\r
2928   GST_INFO_OBJECT (piffdemux, "prepared audio caps : %s", gst_caps_to_string(caps));\r
2929 \r
2930 }\r
2931 \r
2932 #define g_marshal_value_peek_object(v)   g_value_get_object (v)\r
2933 \r
2934 void\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
2941 {\r
2942   typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer  data1,\r
2943                                                     gpointer      arg_1,\r
2944                                                     gpointer     data2);\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
2949 \r
2950   g_return_if_fail (return_value != NULL);\r
2951   g_return_if_fail (n_param_values == 2);\r
2952 \r
2953   if (G_CCLOSURE_SWAP_DATA (closure))\r
2954   {\r
2955     data1 = closure->data;\r
2956     data2 = g_value_peek_pointer (param_values + 0);\r
2957   }\r
2958   else\r
2959   {\r
2960     data1 = g_value_peek_pointer (param_values + 0);\r
2961     data2 = closure->data;\r
2962   }\r
2963   callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback);\r
2964 \r
2965   v_return = callback (data1,\r
2966                        g_marshal_value_peek_object (param_values + 1),\r
2967                        data2);\r
2968 \r
2969   g_value_set_boolean (return_value, v_return);\r
2970 }\r
2971 \r
2972 #define PIFFDEMUX_SPSPPS_LENGTH_SIZE     2\r
2973 \r
2974 static gboolean\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
2976 {\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
2985 \r
2986   /* nothing to filter */\r
2987   if ((dci_meta_buf == NULL) || (dci_meta_size < 6))\r
2988   {\r
2989     GST_ERROR ("Insufficient codec data...\n");\r
2990     return FALSE;\r
2991   }\r
2992 \r
2993   /* Removing unnecessary info in meta data */\r
2994   extradata = (unsigned char *)dci_meta_buf + 4;\r
2995 \r
2996   /* retrieve Length of Length*/\r
2997   h264_nal_length_size = (*extradata++ & 0x03) + 1;\r
2998 \r
2999   GST_LOG ("Length Of Length is %d\n", h264_nal_length_size);\r
3000   if (h264_nal_length_size == 3)\r
3001   {\r
3002     GST_ERROR ("LengthOfLength is WRONG...\n");\r
3003     return FALSE;\r
3004   }\r
3005 \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
3009 \r
3010   if (!unit_nb)\r
3011   {\r
3012     GST_ERROR ("SPS is not present....\n");\r
3013     return FALSE;\r
3014   }\r
3015 \r
3016   while (unit_nb--)\r
3017   {\r
3018     /* get SPS/PPS data Length*/\r
3019     unit_size = PIFFDEMUX_RB16(extradata);\r
3020 \r
3021     GST_LOG ("SPS size = %d", unit_size);\r
3022 \r
3023     /* Extra 4 bytes for adding size of the packet */\r
3024     total_size += unit_size + h264_nal_length_size;\r
3025 \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
3028     {\r
3029       GST_ERROR ("SPS Length is wrong in DCI...\n");\r
3030       return FALSE;\r
3031     }\r
3032     out = realloc(out, total_size);\r
3033     if (!out)\r
3034     {\r
3035       GST_ERROR ("realloc FAILED...\n");\r
3036       return FALSE;\r
3037     }\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
3045 \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
3050 \r
3051     /* Copy SPS/PPS Length and data */\r
3052     memcpy(out + total_size - unit_size,  extradata + PIFFDEMUX_SPSPPS_LENGTH_SIZE, unit_size);\r
3053 \r
3054     extradata += (PIFFDEMUX_SPSPPS_LENGTH_SIZE + unit_size);\r
3055 \r
3056     if (!unit_nb && !sps_done++)\r
3057     {\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
3061     }\r
3062   }\r
3063 \r
3064   *dci_3gpp_buf = malloc (total_size);\r
3065   if (NULL == *dci_3gpp_buf)\r
3066   {\r
3067     GST_ERROR ("Memory Allocation FAILED...\n");\r
3068     free (out);\r
3069     return FALSE;\r
3070   }\r
3071 \r
3072   memcpy(*dci_3gpp_buf, out, total_size);\r
3073   *dci_3gpp_size = total_size;\r
3074 \r
3075   GST_DEBUG ("SPS_PPS size = %d\n", total_size);\r
3076 \r
3077   if (out)\r
3078   {\r
3079     free(out);\r
3080   }\r
3081   return TRUE;\r
3082  }\r
3083 \r