]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugins-ugly0-10.git/blob - gst/realmedia/rmdemux.c
58cefd9ec6082dad2d34cb1332640c4e33608adc
[glsdk/gst-plugins-ugly0-10.git] / gst / realmedia / rmdemux.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
4  * Copyright (C) <2004> Stephane Loeuillet <gstreamer@leroutier.net>
5  * Copyright (C) <2005> Owen Fraser-Green <owen@discobabe.net>
6  * Copyright (C) <2005> Michael Smith <fluendo.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 #include "rmdemux.h"
28 #include <string.h>
29 #include <ctype.h>
30 #include <zlib.h>
32 #define RMDEMUX_GUINT32_GET(a)  GST_READ_UINT32_BE(a)
33 #define RMDEMUX_GUINT16_GET(a)  GST_READ_UINT16_BE(a)
34 #define RMDEMUX_FOURCC_GET(a)   GST_READ_UINT32_LE(a)
35 #define HEADER_SIZE 10
36 #define DATA_SIZE 8
38 typedef struct _GstRMDemuxIndex GstRMDemuxIndex;
40 struct _GstRMDemuxStream
41 {
42   guint32 subtype;
43   guint32 fourcc;
44   guint32 subformat;
45   guint32 format;
47   int id;
48   GstCaps *caps;
49   GstPad *pad;
50   int timescale;
52   int sample_index;
53   GstRMDemuxIndex *index;
54   int index_length;
55   double frame_rate;
56   guint32 seek_offset;
58   guint16 width;
59   guint16 height;
60   guint16 flavor;
61   guint16 rate;                 // samplerate
62   guint16 n_channels;           // channels
63   guint16 sample_width;         // bits_per_sample
64   guint16 leaf_size;            // subpacket_size
65   guint32 packet_size;          // coded_frame_size
66   guint16 version;
67   guint32 extra_data_size;      // codec_data_length
68   guint8 *extra_data;           // extras
69 };
71 struct _GstRMDemuxIndex
72 {
73   guint32 offset;
74   GstClockTime timestamp;
75 };
77 static GstElementDetails gst_rmdemux_details = {
78   "RealMedia Demuxer",
79   "Codec/Demuxer",
80   "Demultiplex a RealMedia file into audio and video streams",
81   "David Schleef <ds@schleef.org>"
82 };
84 enum
85 {
86   LAST_SIGNAL
87 };
89 enum
90 {
91   ARG_0
92 };
94 static GstStaticPadTemplate gst_rmdemux_sink_template =
95 GST_STATIC_PAD_TEMPLATE ("sink",
96     GST_PAD_SINK,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS ("application/vnd.rn-realmedia")
99     );
101 static GstStaticPadTemplate gst_rmdemux_videosrc_template =
102 GST_STATIC_PAD_TEMPLATE ("video_%02d",
103     GST_PAD_SRC,
104     GST_PAD_SOMETIMES,
105     GST_STATIC_CAPS_ANY);
107 static GstStaticPadTemplate gst_rmdemux_audiosrc_template =
108 GST_STATIC_PAD_TEMPLATE ("audio_%02d",
109     GST_PAD_SRC,
110     GST_PAD_SOMETIMES,
111     GST_STATIC_CAPS_ANY);
113 GST_DEBUG_CATEGORY_STATIC (rmdemux_debug);
114 #define GST_CAT_DEFAULT rmdemux_debug
116 static GstElementClass *parent_class = NULL;
118 static void gst_rmdemux_class_init (GstRMDemuxClass * klass);
119 static void gst_rmdemux_base_init (GstRMDemuxClass * klass);
120 static void gst_rmdemux_init (GstRMDemux * rmdemux);
121 static void gst_rmdemux_dispose (GObject * object);
122 static GstStateChangeReturn gst_rmdemux_change_state (GstElement * element,
123     GstStateChange transition);
124 static GstFlowReturn gst_rmdemux_chain (GstPad * pad, GstBuffer * buffer);
125 static void gst_rmdemux_loop (GstPad * pad);
126 static gboolean gst_rmdemux_sink_activate (GstPad * sinkpad);
127 static gboolean gst_rmdemux_sink_activate_push (GstPad * sinkpad,
128     gboolean active);
129 static gboolean gst_rmdemux_sink_activate_pull (GstPad * sinkpad,
130     gboolean active);
131 static gboolean gst_rmdemux_sink_event (GstPad * pad, GstEvent * event);
132 static gboolean gst_rmdemux_src_event (GstPad * pad, GstEvent * event);
133 static gboolean gst_rmdemux_send_event (GstRMDemux * rmdemux, GstEvent * event);
134 static const GstQueryType *gst_rmdemux_src_query_types (GstPad * pad);
135 static gboolean gst_rmdemux_src_query (GstPad * pad, GstQuery * query);
136 static gboolean gst_rmdemux_perform_seek (GstRMDemux * rmdemux, gboolean flush);
138 static void gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const void *data,
139     int length);
140 static void gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const void *data,
141     int length);
142 static GstFlowReturn gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux,
143     const void *data, int length);
144 static guint gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const void *data,
145     int length);
146 static void gst_rmdemux_parse_data (GstRMDemux * rmdemux, const void *data,
147     int length);
148 static void gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const void *data,
149     int length);
150 static GstFlowReturn gst_rmdemux_parse_packet (GstRMDemux * rmdemux,
151     const void *data, guint16 version, guint16 length);
152 static void gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const void *data,
153     int length);
155 static GstCaps *gst_rmdemux_src_getcaps (GstPad * pad);
157 static GstRMDemuxStream *gst_rmdemux_get_stream_by_id (GstRMDemux * rmdemux,
158     int id);
160 static GType
161 gst_rmdemux_get_type (void)
163   static GType rmdemux_type = 0;
165   if (!rmdemux_type) {
166     static const GTypeInfo rmdemux_info = {
167       sizeof (GstRMDemuxClass),
168       (GBaseInitFunc) gst_rmdemux_base_init, NULL,
169       (GClassInitFunc) gst_rmdemux_class_init,
170       NULL, NULL, sizeof (GstRMDemux), 0,
171       (GInstanceInitFunc) gst_rmdemux_init,
172     };
174     rmdemux_type =
175         g_type_register_static (GST_TYPE_ELEMENT, "GstRMDemux", &rmdemux_info,
176         0);
177   }
178   return rmdemux_type;
181 static void
182 gst_rmdemux_base_init (GstRMDemuxClass * klass)
184   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
186   gst_element_class_add_pad_template (element_class,
187       gst_static_pad_template_get (&gst_rmdemux_sink_template));
188   gst_element_class_add_pad_template (element_class,
189       gst_static_pad_template_get (&gst_rmdemux_videosrc_template));
190   gst_element_class_add_pad_template (element_class,
191       gst_static_pad_template_get (&gst_rmdemux_audiosrc_template));
192   gst_element_class_set_details (element_class, &gst_rmdemux_details);
195 static void
196 gst_rmdemux_class_init (GstRMDemuxClass * klass)
198   GObjectClass *gobject_class;
199   GstElementClass *gstelement_class;
201   gobject_class = (GObjectClass *) klass;
202   gstelement_class = (GstElementClass *) klass;
204   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
206   gstelement_class->change_state = gst_rmdemux_change_state;
208   GST_DEBUG_CATEGORY_INIT (rmdemux_debug, "rmdemux",
209       0, "Demuxer for Realmedia streams");
211   gobject_class->dispose = gst_rmdemux_dispose;
214 static void
215 gst_rmdemux_dispose (GObject * object)
217   GstRMDemux *rmdemux = GST_RMDEMUX (object);
219   if (rmdemux->adapter) {
220     g_object_unref (rmdemux->adapter);
221     rmdemux->adapter = NULL;
222   }
224   GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
227 static void
228 gst_rmdemux_init (GstRMDemux * rmdemux)
230   rmdemux->sinkpad =
231       gst_pad_new_from_template (gst_static_pad_template_get
232       (&gst_rmdemux_sink_template), "sink");
233   gst_pad_set_event_function (rmdemux->sinkpad, gst_rmdemux_sink_event);
234   gst_pad_set_chain_function (rmdemux->sinkpad, gst_rmdemux_chain);
235   gst_pad_set_activate_function (rmdemux->sinkpad, gst_rmdemux_sink_activate);
236   gst_pad_set_activatepull_function (rmdemux->sinkpad,
237       gst_rmdemux_sink_activate_pull);
238   gst_pad_set_activatepush_function (rmdemux->sinkpad,
239       gst_rmdemux_sink_activate_push);
242   gst_element_add_pad (GST_ELEMENT (rmdemux), rmdemux->sinkpad);
244   rmdemux->adapter = gst_adapter_new ();
247 static gboolean
248 plugin_init (GstPlugin * plugin)
250   return gst_element_register (plugin, "rmdemux",
251       GST_RANK_PRIMARY, GST_TYPE_RMDEMUX);
254 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
255     GST_VERSION_MINOR,
256     "rmdemux",
257     "Realmedia stream demuxer",
258     plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)
261      static gboolean gst_rmdemux_sink_event (GstPad * pad, GstEvent * event)
263   gboolean ret = TRUE;
265   GstRMDemux *rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
267   switch (GST_EVENT_TYPE (event)) {
268     case GST_EVENT_NEWSEGMENT:
269       GST_LOG_OBJECT (rmdemux, "Event on sink: NEWSEGMENT");
270       gst_event_unref (event);
271       break;
272     default:
273       GST_LOG_OBJECT (rmdemux, "Event on sink: type=%d",
274           GST_EVENT_TYPE (event));
275       ret = gst_pad_event_default (pad, event);
276       break;
277   }
279   return ret;
282 static gboolean
283 gst_rmdemux_src_event (GstPad * pad, GstEvent * event)
285   gboolean ret = TRUE;
287   GstRMDemux *rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
289   GST_LOG_OBJECT (rmdemux, "handling src event");
291   switch (GST_EVENT_TYPE (event)) {
292     case GST_EVENT_SEEK:{
293       gboolean running;
294       gboolean flush;
295       GstFormat format;
296       GstSeekFlags flags;
297       GstSeekType cur_type, stop_type;
298       gint64 cur, stop;
300       GST_LOG_OBJECT (rmdemux, "Event on src: SEEK");
301       /* can't seek if we are not seekable, FIXME could pass the
302        * seek query upstream after converting it to bytes using
303        * the average bitrate of the stream. */
304       if (!rmdemux->seekable) {
305         ret = FALSE;
306         GST_DEBUG ("seek on non seekable stream");
307         goto done_unref;
308       }
309       gst_event_parse_seek (event, NULL, &format, &flags,
310           &cur_type, &cur, &stop_type, &stop);
312       /* we can only seek on time */
313       if (format != GST_FORMAT_TIME) {
314         ret = FALSE;
315         GST_DEBUG ("can only seek on TIME");
316         goto done_unref;
317       }
319       GST_LOCK (rmdemux);
321       if (cur_type == GST_SEEK_TYPE_SET)
322         rmdemux->segment_start = cur;
323       else if (cur_type == GST_SEEK_TYPE_CUR)
324         rmdemux->segment_start += cur;
325       else if (cur_type == GST_SEEK_TYPE_END)
326         rmdemux->segment_start = rmdemux->duration + cur;
328       if (stop_type == GST_SEEK_TYPE_SET)
329         rmdemux->segment_stop = stop;
330       else if (cur_type == GST_SEEK_TYPE_CUR)
331         rmdemux->segment_stop += cur;
332       else if (cur_type == GST_SEEK_TYPE_END)
333         rmdemux->segment_stop = rmdemux->duration + cur;
335       /* Now do a sanity-check */
336       if (rmdemux->segment_stop < rmdemux->segment_start) {
337         GST_UNLOCK (rmdemux);
338         ret = FALSE;
339         GST_DEBUG_OBJECT (rmdemux, "Seek had stop " G_GUINT64_FORMAT " < start "
340             G_GUINT64_FORMAT ", cannot perform seek",
341             rmdemux->segment_stop, rmdemux->segment_start);
342         goto done_unref;
343       }
345       rmdemux->segment_play = !!(flags & GST_SEEK_FLAG_SEGMENT);
346       flush = !!(flags & GST_SEEK_FLAG_FLUSH);
347       gst_event_unref (event);
349       GST_DEBUG ("segment positions set to %" GST_TIME_FORMAT "-%"
350           GST_TIME_FORMAT, GST_TIME_ARGS (rmdemux->segment_start),
351           GST_TIME_ARGS (rmdemux->segment_stop));
353       /* check if we can do the seek now */
354       running = rmdemux->running;
355       GST_UNLOCK (rmdemux);
357       /* now do the seek */
358       if (running) {
359         ret = gst_rmdemux_perform_seek (rmdemux, flush);
360       } else
361         ret = TRUE;
362       break;
363     }
364     default:
365       GST_LOG_OBJECT (rmdemux, "Event on src: type=%d", GST_EVENT_TYPE (event));
366       ret = gst_pad_event_default (pad, event);
367       break;
368   }
370   return ret;
372 done_unref:
373   GST_DEBUG ("error handling event");
374   gst_event_unref (event);
375   return ret;
378 /* Validate that this looks like a reasonable point to seek to */
379 static gboolean
380 gst_rmdemux_validate_offset (GstRMDemux * rmdemux)
382   GstBuffer *buffer;
383   GstFlowReturn flowret;
384   guint16 version, length;
385   gboolean ret = TRUE;
387   flowret = gst_pad_pull_range (rmdemux->sinkpad, rmdemux->offset, 4, &buffer);
389   if (flowret != GST_FLOW_OK) {
390     GST_DEBUG_OBJECT (rmdemux, "Failed to pull data at offset %d",
391         rmdemux->offset);
392     return FALSE;
393   }
394   // TODO: Can we also be seeking to a 'DATA' chunk header? Check this.
395   // Also, for the case we currently handle, can we check any more? It's pretty
396   // sucky to not be validating a little more heavily than this...
397   /* This should now be the start of a data packet header. That begins with
398    * a 2-byte 'version' field, which has to be 0 or 1, then a length. I'm not
399    * certain what values are valid for length, but it must always be at least
400    * 4 bytes, and we can check that it won't take us past our known total size
401    */
403   version = RMDEMUX_GUINT16_GET (GST_BUFFER_DATA (buffer));
404   if (version != 0 && version != 1) {
405     GST_DEBUG_OBJECT (rmdemux, "Expected version 0 or 1, got %d",
406         (int) version);
407     ret = FALSE;
408   }
410   length = RMDEMUX_GUINT16_GET (GST_BUFFER_DATA (buffer) + 2);
411   // TODO: Also check against total stream length
412   if (length < 4) {
413     GST_DEBUG_OBJECT (rmdemux, "Expected length >= 4, got %d", (int) length);
414     ret = FALSE;
415   }
417   if (ret) {
418     rmdemux->offset += 4;
419     gst_adapter_clear (rmdemux->adapter);
420     gst_adapter_push (rmdemux->adapter, buffer);
421   } else {
422     GST_WARNING_OBJECT (rmdemux, "Failed to validate seek offset at %d",
423         rmdemux->offset);
424   }
426   return ret;
429 static gboolean
430 find_seek_offset_bytes (GstRMDemux * rmdemux, guint target)
432   int i, n;
433   gboolean ret = FALSE;
435   for (n = 0; n < rmdemux->n_streams; n++) {
436     GstRMDemuxStream *stream;
438     stream = rmdemux->streams[n];
440     /* Search backwards through this stream's index until we find the first
441      * timestamp before our target time */
442     for (i = stream->index_length - 1; i >= 0; i--) {
443       if (stream->index[i].offset <= target) {
444         /* Set the seek_offset for the stream so we don't bother parsing it
445          * until we've passed that point */
446         stream->seek_offset = stream->index[i].offset;
447         rmdemux->offset = stream->index[i].offset;
448         ret = TRUE;
449         break;
450       }
451     }
452   }
453   return ret;
456 static gboolean
457 find_seek_offset_time (GstRMDemux * rmdemux, GstClockTime time)
459   int i, n;
460   gboolean ret = FALSE;
461   GstClockTime earliest = GST_CLOCK_TIME_NONE;
463   for (n = 0; n < rmdemux->n_streams; n++) {
464     GstRMDemuxStream *stream;
466     stream = rmdemux->streams[n];
468     /* Search backwards through this stream's index until we find the first
469      * timestamp before our target time */
470     for (i = stream->index_length - 1; i >= 0; i--) {
471       if (stream->index[i].timestamp <= time) {
472         /* Set the seek_offset for the stream so we don't bother parsing it
473          * until we've passed that point */
474         stream->seek_offset = stream->index[i].offset;
476         /* If it's also the earliest timestamp we've seen of all streams, then
477          * that's our target!
478          */
479         if (earliest == GST_CLOCK_TIME_NONE ||
480             stream->index[i].timestamp < earliest) {
481           earliest = stream->index[i].timestamp;
482           rmdemux->offset = stream->index[i].offset;
483           GST_DEBUG_OBJECT (rmdemux,
484               "We're looking for %" GST_TIME_FORMAT
485               " and we found that stream %d has the latest index at %"
486               GST_TIME_FORMAT, GST_TIME_ARGS (rmdemux->segment_start), n,
487               GST_TIME_ARGS (earliest));
488         }
490         ret = TRUE;
492         break;
493       }
494     }
495   }
496   return ret;
499 static gboolean
500 gst_rmdemux_perform_seek (GstRMDemux * rmdemux, gboolean flush)
502   gboolean validated;
503   gboolean ret = TRUE;
505   /* nothing configured, play complete file */
506   if (rmdemux->segment_start == GST_CLOCK_TIME_NONE)
507     rmdemux->segment_start = 0;
508   if (rmdemux->segment_stop == GST_CLOCK_TIME_NONE)
509     rmdemux->segment_stop = rmdemux->duration;
511   rmdemux->segment_start = CLAMP (rmdemux->segment_start, 0, rmdemux->duration);
512   rmdemux->segment_stop = CLAMP (rmdemux->segment_stop, 0, rmdemux->duration);
514   /* first step is to unlock the streaming thread if it is
515    * blocked in a chain call, we do this by starting the flush. */
516   if (flush) {
517     gboolean res;
519     res = gst_pad_push_event (rmdemux->sinkpad, gst_event_new_flush_start ());
520     if (!res) {
521       GST_WARNING_OBJECT (rmdemux, "Failed to push event upstream!");
522     }
524     res = gst_rmdemux_send_event (rmdemux, gst_event_new_flush_start ());
525     if (!res) {
526       GST_WARNING_OBJECT (rmdemux, "Failed to push event downstream!");
527     }
528   } else {
529     gst_pad_pause_task (rmdemux->sinkpad);
530   }
532   GST_LOG_OBJECT (rmdemux, "Done starting flushes");
534   /* now grab the stream lock so that streaming cannot continue, for
535    * non flushing seeks when the element is in PAUSED this could block
536    * forever. */
537   GST_STREAM_LOCK (rmdemux->sinkpad);
539   GST_LOG_OBJECT (rmdemux, "Took streamlock");
541   /* we need to stop flushing on the sinkpad as we're going to use it
542    * next. We can do this as we have the STREAM lock now. */
543   gst_pad_push_event (rmdemux->sinkpad, gst_event_new_flush_stop ());
545   GST_LOG_OBJECT (rmdemux, "Pushed FLUSH_STOP event");
547   /* For each stream, find the first index offset equal to or before our seek 
548    * target. Of these, find the smallest offset. That's where we seek to.
549    *
550    * Then we pull 4 bytes from that offset, validate that we've seeked to a
551    * DATA chunk (with the DATA fourcc). 
552    * If that fails, restart, with the seek target set to one less than the
553    * offset we just tried. If we run out of places to try, treat that as a fatal
554    * error.
555    */
556   if (!find_seek_offset_time (rmdemux, rmdemux->segment_start)) {
557     GST_LOG_OBJECT (rmdemux, "Failed to find seek offset by time");
558     ret = FALSE;
559     goto done;
560   }
562   GST_LOG_OBJECT (rmdemux, "Validating offset %u", rmdemux->offset);
563   validated = gst_rmdemux_validate_offset (rmdemux);
564   while (!validated) {
565     GST_INFO_OBJECT (rmdemux, "Failed to validate offset at %u",
566         rmdemux->offset);
567     if (!find_seek_offset_bytes (rmdemux, rmdemux->offset - 1)) {
568       ret = FALSE;
569       goto done;
570     }
571     validated = gst_rmdemux_validate_offset (rmdemux);
572   }
574   GST_LOG_OBJECT (rmdemux, "Found final offset. Excellent!");
576   /* now we have a new position, prepare for streaming again */
577   {
578     GstEvent *event;
580     /* Reset the demuxer state */
581     rmdemux->state = RMDEMUX_STATE_DATA_PACKET;
582     rmdemux->cur_timestamp = GST_CLOCK_TIME_NONE;
584     if (flush)
585       gst_rmdemux_send_event (rmdemux, gst_event_new_flush_stop ());
587     /* create the discont event we are going to send out */
588     event = gst_event_new_newsegment (FALSE, 1.0,
589         GST_FORMAT_TIME, (gint64) rmdemux->segment_start,
590         (gint64) rmdemux->segment_stop, rmdemux->segment_start);
592     GST_DEBUG_OBJECT (rmdemux,
593         "sending NEWSEGMENT event to all src pads with segment_start= %"
594         GST_TIME_FORMAT, GST_TIME_ARGS (rmdemux->segment_start));
595     gst_rmdemux_send_event (rmdemux, event);
597     /* notify start of new segment */
598     if (rmdemux->segment_play) {
599       gst_element_post_message (GST_ELEMENT (rmdemux),
600           gst_message_new_segment_start (GST_OBJECT (rmdemux), GST_FORMAT_TIME,
601               rmdemux->segment_start));
602     }
603     /* restart our task since it might have been stopped when we did the 
604      * flush. */
605     gst_pad_start_task (rmdemux->sinkpad, (GstTaskFunction) gst_rmdemux_loop,
606         rmdemux->sinkpad);
607   }
609 done:
611   /* streaming can continue now */
612   GST_STREAM_UNLOCK (rmdemux->sinkpad);
614   return ret;
618 static gboolean
619 gst_rmdemux_src_query (GstPad * pad, GstQuery * query)
621   gboolean res = TRUE;
622   GstRMDemux *rmdemux;
624   rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
626   switch (GST_QUERY_TYPE (query)) {
627     case GST_QUERY_POSITION:
628       GST_DEBUG_OBJECT (rmdemux, "src_query position");
629       gst_query_set_position (query, GST_FORMAT_TIME, -1);      //rmdemux->cur_timestamp, 
630       break;
631     case GST_QUERY_DURATION:
632       GST_DEBUG_OBJECT (rmdemux, "src_query duration");
633       gst_query_set_duration (query, GST_FORMAT_TIME,   //rmdemux->cur_timestamp, 
634           rmdemux->duration);
635       break;
636     default:
637       res = FALSE;
638       break;
639   }
641   return res;
644 static const GstQueryType *
645 gst_rmdemux_src_query_types (GstPad * pad)
647   static const GstQueryType query_types[] = {
648     GST_QUERY_POSITION,
649     GST_QUERY_DURATION,
650     0
651   };
653   return query_types;
656 static GstStateChangeReturn
657 gst_rmdemux_change_state (GstElement * element, GstStateChange transition)
659   GstRMDemux *rmdemux = GST_RMDEMUX (element);
660   GstStateChangeReturn res;
662   switch (transition) {
663     case GST_STATE_CHANGE_NULL_TO_READY:
664       break;
665     case GST_STATE_CHANGE_READY_TO_PAUSED:
666       rmdemux->state = RMDEMUX_STATE_HEADER;
667       rmdemux->have_pads = FALSE;
668       rmdemux->segment_start = GST_CLOCK_TIME_NONE;
669       rmdemux->segment_stop = GST_CLOCK_TIME_NONE;
670       rmdemux->segment_play = FALSE;
671       rmdemux->running = FALSE;
672       rmdemux->cur_timestamp = GST_CLOCK_TIME_NONE;
673       break;
674     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
675       break;
676     default:
677       break;
678   }
680   res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
682   switch (transition) {
683     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
684       break;
685     case GST_STATE_CHANGE_PAUSED_TO_READY:
686       gst_adapter_clear (rmdemux->adapter);
687       GST_LOCK (rmdemux);
688       rmdemux->running = FALSE;
689       GST_UNLOCK (rmdemux);
690       break;
691     case GST_STATE_CHANGE_READY_TO_NULL:
692       break;
693     default:
694       break;
695   }
697   return res;
700 static GstCaps *
701 gst_rmdemux_src_getcaps (GstPad * pad)
703   guint n;
704   GstRMDemux *rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
706   GST_DEBUG_OBJECT (rmdemux, "getcaps");
708   for (n = 0; n < rmdemux->n_streams; n++) {
709     if (rmdemux->streams[n] != NULL && rmdemux->streams[n]->pad == pad) {
710       return gst_caps_copy (rmdemux->streams[n]->caps);
711     }
712   }
714   /* Base case */
715   return gst_caps_new_empty ();
718 /* this function is called when the pad is activated and should start
719  * processing data.
720  *
721  * We check if we can do random access to decide if we work push or
722  * pull based.
723  */
724 static gboolean
725 gst_rmdemux_sink_activate (GstPad * sinkpad)
727   if (gst_pad_check_pull_range (sinkpad)) {
728     return gst_pad_activate_pull (sinkpad, TRUE);
729   } else {
730     return gst_pad_activate_push (sinkpad, TRUE);
731   }
734 /* this function gets called when we activate ourselves in push mode.
735  * We cannot seek (ourselves) in the stream */
736 static gboolean
737 gst_rmdemux_sink_activate_push (GstPad * pad, gboolean active)
739   GstRMDemux *rmdemux;
741   rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
743   GST_DEBUG_OBJECT (rmdemux, "activate_push");
745   rmdemux->seekable = FALSE;
747   return TRUE;
750 /* this function gets called when we activate ourselves in pull mode.
751  * We can perform  random access to the resource and we start a task
752  * to start reading */
753 static gboolean
754 gst_rmdemux_sink_activate_pull (GstPad * pad, gboolean active)
756   GstRMDemux *rmdemux;
758   rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
760   GST_DEBUG_OBJECT (rmdemux, "activate_pull");
762   if (active) {
763     rmdemux->seekable = TRUE;
764     rmdemux->offset = 0;
765     rmdemux->loop_state = RMDEMUX_LOOP_STATE_HEADER;
766     rmdemux->data_offset = G_MAXUINT;
768     return gst_pad_start_task (pad, (GstTaskFunction) gst_rmdemux_loop, pad);
769   } else {
770     return gst_pad_stop_task (pad);
771   }
774 /* random access mode - just pass over to our chain function */
775 static void
776 gst_rmdemux_loop (GstPad * pad)
778   GstRMDemux *rmdemux;
779   GstBuffer *buffer;
780   GstFlowReturn ret = GST_FLOW_OK;
781   guint size;
783   rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
785   GST_DEBUG_OBJECT (rmdemux, "loop with state=%d and offset=0x%x",
786       rmdemux->loop_state, rmdemux->offset);
788   switch (rmdemux->state) {
789     case RMDEMUX_STATE_HEADER:
790       size = HEADER_SIZE;
791       break;
792     case RMDEMUX_STATE_HEADER_DATA:
793       size = DATA_SIZE;
794       break;
795     case RMDEMUX_STATE_DATA_PACKET:
796       size = rmdemux->avg_packet_size;
797       break;
798     case RMDEMUX_STATE_EOS:
799       GST_LOG_OBJECT (rmdemux, "At EOS, pausing task");
800       goto need_pause;
801     default:
802       GST_LOG_OBJECT (rmdemux, "Default: requires %d bytes (state is %d)",
803           (int) rmdemux->size, rmdemux->state);
804       size = rmdemux->size;
805   }
807   ret = gst_pad_pull_range (pad, rmdemux->offset, size, &buffer);
808   if (ret != GST_FLOW_OK) {
809     if (rmdemux->offset == rmdemux->index_offset) {
810       /* The index isn't available so forget about it */
811       rmdemux->loop_state = RMDEMUX_LOOP_STATE_DATA;
812       rmdemux->offset = rmdemux->data_offset;
813       GST_LOCK (rmdemux);
814       rmdemux->running = TRUE;
815       rmdemux->seekable = FALSE;
816       GST_UNLOCK (rmdemux);
817       return;
818     } else {
819       GST_DEBUG_OBJECT (rmdemux,
820           "Unable to pull %d bytes at offset %p (pull_range returned %d, state is %d)",
821           (int) size, rmdemux->offset, ret, GST_STATE (rmdemux));
822       goto need_pause;
823     }
824   }
826   size = GST_BUFFER_SIZE (buffer);
828   /* Defer to the chain function */
829   ret = gst_rmdemux_chain (pad, buffer);
830   if (ret != GST_FLOW_OK) {
831     GST_DEBUG_OBJECT (rmdemux, "Chain flow failed at %p", rmdemux->offset);
832     goto need_pause;
833   }
835   rmdemux->offset += size;
837   switch (rmdemux->loop_state) {
838     case RMDEMUX_LOOP_STATE_HEADER:
839       if (rmdemux->offset >= rmdemux->data_offset) {
840         /* It's the end of the header */
841         rmdemux->loop_state = RMDEMUX_LOOP_STATE_INDEX;
842         rmdemux->offset = rmdemux->index_offset;
843       }
844       break;
845     case RMDEMUX_LOOP_STATE_INDEX:
846       if (rmdemux->state == RMDEMUX_STATE_HEADER) {
847         if (rmdemux->index_offset == 0) {
848           /* We've read the last index */
849           rmdemux->loop_state = RMDEMUX_LOOP_STATE_DATA;
850           rmdemux->offset = rmdemux->data_offset;
851           GST_LOCK (rmdemux);
852           rmdemux->running = TRUE;
853           GST_UNLOCK (rmdemux);
854         } else {
855           /* Get the next index */
856           rmdemux->offset = rmdemux->index_offset;
857         }
858       }
859       break;
860     case RMDEMUX_LOOP_STATE_DATA:
861       break;
862   }
864   return;
866 need_pause:
867   {
868     GST_LOG_OBJECT (rmdemux, "pausing task");
869     gst_pad_pause_task (pad);
870     if (GST_FLOW_IS_FATAL (ret)) {
871       gst_rmdemux_send_event (rmdemux, gst_event_new_eos ());
872       /* FIXME: add translations */
873       GST_ELEMENT_ERROR (rmdemux, STREAM, FAILED,
874           (("Internal data stream error.")),
875           ("stream stopped, reason %d", ret));
876     }
877     return;
878   }
881 static gboolean
882 gst_rmdemux_fourcc_isplausible (guint32 fourcc)
884   int i;
886   for (i = 0; i < 4; i++) {
887     if (!isprint ((int) ((unsigned char *) (&fourcc))[i])) {
888       return FALSE;
889     }
890   }
891   return TRUE;
894 static GstFlowReturn
895 gst_rmdemux_chain (GstPad * pad, GstBuffer * buffer)
897   GstFlowReturn ret = GST_FLOW_OK;
898   const guint8 *data;
899   guint16 version;
901   GstRMDemux *rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad));
903   GST_LOG_OBJECT (rmdemux, "Chaining buffer of size %d",
904       GST_BUFFER_SIZE (buffer));
906   gst_adapter_push (rmdemux->adapter, buffer);
908   while (TRUE) {
909     GST_LOG_OBJECT (rmdemux, "looping in chain");
910     switch (rmdemux->state) {
911       case RMDEMUX_STATE_HEADER:
912       {
913         if (gst_adapter_available (rmdemux->adapter) < HEADER_SIZE)
914           goto unlock;
916         data = gst_adapter_peek (rmdemux->adapter, HEADER_SIZE);
918         rmdemux->object_id = RMDEMUX_FOURCC_GET (data + 0);
919         rmdemux->size = RMDEMUX_GUINT32_GET (data + 4) - HEADER_SIZE;
920         rmdemux->object_version = RMDEMUX_GUINT16_GET (data + 8);
922         /* Sanity-check. We assume that the FOURCC is printable ASCII */
923         if (!gst_rmdemux_fourcc_isplausible (rmdemux->object_id)) {
924           /* Failed. Remain in HEADER state, try again... We flush only 
925            * the actual FOURCC, not the entire header, because we could 
926            * need to resync anywhere at all... really, this should never 
927            * happen. */
928           GST_WARNING_OBJECT (rmdemux, "Bogus looking header, unprintable "
929               "FOURCC");
930           gst_adapter_flush (rmdemux->adapter, 4);
932           break;
933         }
935         GST_LOG_OBJECT (rmdemux, "header found with object_id=%"
936             GST_FOURCC_FORMAT
937             " size=%08x object_version=%d",
938             GST_FOURCC_ARGS (rmdemux->object_id), rmdemux->size,
939             rmdemux->object_version);
941         gst_adapter_flush (rmdemux->adapter, HEADER_SIZE);
943         switch (rmdemux->object_id) {
944           case GST_MAKE_FOURCC ('.', 'R', 'M', 'F'):
945             rmdemux->state = RMDEMUX_STATE_HEADER_RMF;
946             break;
947           case GST_MAKE_FOURCC ('P', 'R', 'O', 'P'):
948             rmdemux->state = RMDEMUX_STATE_HEADER_PROP;
949             break;
950           case GST_MAKE_FOURCC ('M', 'D', 'P', 'R'):
951             rmdemux->state = RMDEMUX_STATE_HEADER_MDPR;
952             break;
953           case GST_MAKE_FOURCC ('I', 'N', 'D', 'X'):
954             rmdemux->state = RMDEMUX_STATE_HEADER_INDX;
955             break;
956           case GST_MAKE_FOURCC ('D', 'A', 'T', 'A'):
957             rmdemux->state = RMDEMUX_STATE_HEADER_DATA;
958             break;
959           case GST_MAKE_FOURCC ('C', 'O', 'N', 'T'):
960             rmdemux->state = RMDEMUX_STATE_HEADER_CONT;
961             break;
962           default:
963             rmdemux->state = RMDEMUX_STATE_HEADER_UNKNOWN;
964             break;
965         }
966         break;
967       }
968       case RMDEMUX_STATE_HEADER_UNKNOWN:
969       {
970         if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
971           goto unlock;
973         GST_WARNING_OBJECT (rmdemux, "Unknown object_id %" GST_FOURCC_FORMAT,
974             GST_FOURCC_ARGS (rmdemux->object_id));
976         gst_adapter_flush (rmdemux->adapter, rmdemux->size);
977         rmdemux->state = RMDEMUX_STATE_HEADER;
978         break;
979       }
980       case RMDEMUX_STATE_HEADER_RMF:
981       {
982         if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
983           goto unlock;
985         if ((rmdemux->object_version == 0) || (rmdemux->object_version == 1)) {
986           data = gst_adapter_peek (rmdemux->adapter, rmdemux->size);
988           gst_rmdemux_parse__rmf (rmdemux, data, rmdemux->size);
989         }
991         gst_adapter_flush (rmdemux->adapter, rmdemux->size);
992         rmdemux->state = RMDEMUX_STATE_HEADER;
993         break;
994       }
995       case RMDEMUX_STATE_HEADER_PROP:
996       {
997         if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
998           goto unlock;
999         data = gst_adapter_peek (rmdemux->adapter, rmdemux->size);
1001         gst_rmdemux_parse_prop (rmdemux, data, rmdemux->size);
1003         gst_adapter_flush (rmdemux->adapter, rmdemux->size);
1004         rmdemux->state = RMDEMUX_STATE_HEADER;
1005         break;
1006       }
1007       case RMDEMUX_STATE_HEADER_MDPR:
1008       {
1009         if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
1010           goto unlock;
1011         data = gst_adapter_peek (rmdemux->adapter, rmdemux->size);
1013         ret = gst_rmdemux_parse_mdpr (rmdemux, data, rmdemux->size);
1015         gst_adapter_flush (rmdemux->adapter, rmdemux->size);
1016         rmdemux->state = RMDEMUX_STATE_HEADER;
1017         break;
1018       }
1019       case RMDEMUX_STATE_HEADER_CONT:
1020       {
1021         if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
1022           goto unlock;
1023         data = gst_adapter_peek (rmdemux->adapter, rmdemux->size);
1025         gst_rmdemux_parse_cont (rmdemux, data, rmdemux->size);
1027         gst_adapter_flush (rmdemux->adapter, rmdemux->size);
1028         rmdemux->state = RMDEMUX_STATE_HEADER;
1029         break;
1030       }
1031       case RMDEMUX_STATE_HEADER_DATA:
1032       {
1033         /* If we haven't already done so then signal there are no more pads */
1034         if (!rmdemux->have_pads) {
1035           gst_element_no_more_pads (GST_ELEMENT (rmdemux));
1036           rmdemux->have_pads = TRUE;
1038           GST_LOG_OBJECT (rmdemux, "no more pads.");
1039           gst_rmdemux_send_event (rmdemux,
1040               gst_event_new_newsegment (FALSE, 1.0, GST_FORMAT_TIME, (gint64) 0,
1041                   (gint64) - 1, 0));
1042         }
1044         /* The actual header is only 8 bytes */
1045         rmdemux->size = DATA_SIZE;
1046         GST_DEBUG_OBJECT (rmdemux, "data available %d",
1047             gst_adapter_available (rmdemux->adapter));
1048         if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
1049           goto unlock;
1051         data = gst_adapter_peek (rmdemux->adapter, rmdemux->size);
1053         gst_rmdemux_parse_data (rmdemux, data, rmdemux->size);
1055         gst_adapter_flush (rmdemux->adapter, rmdemux->size);
1057         rmdemux->state = RMDEMUX_STATE_DATA_PACKET;
1058         break;
1059       }
1060       case RMDEMUX_STATE_HEADER_INDX:
1061       {
1062         if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
1063           goto unlock;
1064         data = gst_adapter_peek (rmdemux->adapter, rmdemux->size);
1066         rmdemux->size = gst_rmdemux_parse_indx (rmdemux, data, rmdemux->size);
1068         /* Only flush the header */
1069         gst_adapter_flush (rmdemux->adapter, HEADER_SIZE);
1071         rmdemux->state = RMDEMUX_STATE_INDX_DATA;
1072         break;
1073       }
1074       case RMDEMUX_STATE_INDX_DATA:
1075       {
1076         /* There's not always an data to get... */
1077         if (rmdemux->size > 0) {
1078           if (gst_adapter_available (rmdemux->adapter) < rmdemux->size)
1079             goto unlock;
1081           data = gst_adapter_peek (rmdemux->adapter, rmdemux->size);
1083           gst_rmdemux_parse_indx_data (rmdemux, data, rmdemux->size);
1085           gst_adapter_flush (rmdemux->adapter, rmdemux->size);
1086         }
1088         rmdemux->state = RMDEMUX_STATE_HEADER;
1089         break;
1090       }
1091       case RMDEMUX_STATE_DATA_PACKET:
1092       {
1093         if (gst_adapter_available (rmdemux->adapter) < 2)
1094           goto unlock;
1096         data = gst_adapter_peek (rmdemux->adapter, 2);
1097         version = RMDEMUX_GUINT16_GET (data);
1098         GST_DEBUG_OBJECT (rmdemux, "Data packet with version=%d", version);
1100         if (version == 0 || version == 1) {
1101           guint16 length;
1103           if (gst_adapter_available (rmdemux->adapter) < 4)
1104             goto unlock;
1105           data = gst_adapter_peek (rmdemux->adapter, 4);
1107           length = RMDEMUX_GUINT16_GET (data + 2);
1108           if (length < 4) {
1109             /* Invalid, just drop it */
1110             gst_adapter_flush (rmdemux->adapter, 4);
1111           } else {
1112             if (gst_adapter_available (rmdemux->adapter) < length)
1113               goto unlock;
1114             data = gst_adapter_peek (rmdemux->adapter, length);
1116             gst_rmdemux_parse_packet (rmdemux, data + 4, version, length - 4);
1117             rmdemux->chunk_index++;
1119             gst_adapter_flush (rmdemux->adapter, length);
1120           }
1122           if (rmdemux->chunk_index == rmdemux->n_chunks || length == 0)
1123             rmdemux->state = RMDEMUX_STATE_HEADER;
1124         } else {
1125           /* Stream done */
1126           gst_adapter_flush (rmdemux->adapter, 2);
1128           if (rmdemux->data_offset == 0) {
1129             GST_LOG_OBJECT (rmdemux,
1130                 "No further data, internal demux state EOS");
1131             rmdemux->state = RMDEMUX_STATE_EOS;
1132           } else
1133             rmdemux->state = RMDEMUX_STATE_HEADER;
1134         }
1135         break;
1136       }
1137       case RMDEMUX_STATE_EOS:
1138         gst_rmdemux_send_event (rmdemux, gst_event_new_eos ());
1139         goto unlock;
1140       default:
1141         GST_WARNING_OBJECT (rmdemux, "Unhandled state %d", rmdemux->state);
1142         goto unlock;
1143     }
1146   }
1148 unlock:
1149   return ret;
1152 static GstRMDemuxStream *
1153 gst_rmdemux_get_stream_by_id (GstRMDemux * rmdemux, int id)
1155   int i;
1157   for (i = 0; i < rmdemux->n_streams; i++) {
1158     GstRMDemuxStream *stream;
1160     stream = rmdemux->streams[i];
1161     if (stream->id == id) {
1162       return stream;
1163     }
1164   }
1166   return NULL;
1169 static gboolean
1170 gst_rmdemux_send_event (GstRMDemux * rmdemux, GstEvent * event)
1172   int i;
1173   gboolean ret = TRUE;
1175   for (i = 0; i < rmdemux->n_streams; i++) {
1176     GstRMDemuxStream *stream;
1178     stream = rmdemux->streams[i];
1180     GST_DEBUG_OBJECT (rmdemux, "Pushing event to stream %d", i);
1182     gst_event_ref (event);
1183     ret = gst_pad_push_event (stream->pad, event);
1184     if (!ret)
1185       break;
1186   }
1187   gst_event_unref (event);
1189   return ret;
1192 GstFlowReturn
1193 gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
1195   GstFlowReturn ret = GST_FLOW_OK;
1196   int version = 0;
1198   if (stream->subtype == GST_RMDEMUX_STREAM_VIDEO) {
1199     char *name = g_strdup_printf ("video_%02d", rmdemux->n_video_streams);
1201     stream->pad =
1202         gst_pad_new_from_template (gst_static_pad_template_get
1203         (&gst_rmdemux_videosrc_template), name);
1204     g_free (name);
1206     switch (stream->fourcc) {
1207       case GST_RM_VDO_RV10:
1208         version = 1;
1209         break;
1210       case GST_RM_VDO_RV20:
1211         version = 2;
1212         break;
1213       case GST_RM_VDO_RV30:
1214         version = 3;
1215         break;
1216       case GST_RM_VDO_RV40:
1217         version = 4;
1218         break;
1219       default:
1220         GST_WARNING_OBJECT (rmdemux, "Unknown video FOURCC code");
1221     }
1223     if (version) {
1224       stream->caps =
1225           gst_caps_new_simple ("video/x-pn-realvideo", "rmversion", G_TYPE_INT,
1226           (int) version,
1227           "format", G_TYPE_INT,
1228           (int) stream->format,
1229           "subformat", G_TYPE_INT, (int) stream->subformat, NULL);
1230     }
1232     if (stream->caps) {
1233       gst_caps_set_simple (stream->caps,
1234           "width", G_TYPE_INT, stream->width,
1235           "height", G_TYPE_INT, stream->height,
1236           "framerate", G_TYPE_DOUBLE, stream->frame_rate, NULL);
1237     }
1238     rmdemux->n_video_streams++;
1240   } else if (stream->subtype == GST_RMDEMUX_STREAM_AUDIO) {
1241     char *name = g_strdup_printf ("audio_%02d", rmdemux->n_audio_streams);
1243     stream->pad =
1244         gst_pad_new_from_template (gst_static_pad_template_get
1245         (&gst_rmdemux_audiosrc_template), name);
1246     GST_LOG_OBJECT (rmdemux, "Created audio pad \"%s\"", name);
1247     g_free (name);
1248     switch (stream->fourcc) {
1249         /* Older RealAudio Codecs */
1250       case GST_RM_AUD_14_4:
1251         version = 1;
1252         break;
1254       case GST_RM_AUD_28_8:
1255         version = 2;
1256         break;
1258         /* DolbyNet (Dolby AC3, low bitrate) */
1259       case GST_RM_AUD_DNET:
1260         stream->caps =
1261             gst_caps_new_simple ("audio/x-ac3", "rate", G_TYPE_INT,
1262             (int) stream->rate, NULL);
1263         break;
1265         /* RealAudio 10 (AAC) */
1266       case GST_RM_AUD_RAAC:
1267         version = 10;
1268         break;
1270         /* MPEG-4 based */
1271       case GST_RM_AUD_RACP:
1272         stream->caps =
1273             gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT,
1274             (int) 4, NULL);
1275         break;
1277         /* Sony ATRAC3 */
1278       case GST_RM_AUD_ATRC:
1279         stream->caps = gst_caps_new_simple ("audio/x-vnd.sony.atrac3", NULL);
1280         break;
1282         /* RealAudio G2 audio */
1283       case GST_RM_AUD_COOK:
1284         version = 8;
1285         break;
1287         /* RALF is lossless */
1288       case GST_RM_AUD_RALF:
1289         GST_DEBUG_OBJECT (rmdemux, "RALF");
1290         stream->caps = gst_caps_new_simple ("audio/x-ralf-mpeg4-generic", NULL);
1291         break;
1293         /* Sipro/ACELP.NET Voice Codec (MIME unknown) */
1294       case GST_RM_AUD_SIPR:
1295         stream->caps = gst_caps_new_simple ("audio/x-sipro", NULL);
1296         break;
1298       default:
1299         GST_WARNING_OBJECT (rmdemux,
1300             "Unknown audio FOURCC code \"%" GST_FOURCC_FORMAT "\"",
1301             stream->fourcc);
1302         break;
1303     }
1305     if (version) {
1306       stream->caps =
1307           gst_caps_new_simple ("audio/x-pn-realaudio", "raversion", G_TYPE_INT,
1308           (int) version, NULL);
1309     }
1311     if (stream->caps) {
1312       gst_caps_set_simple (stream->caps,
1313           "flavor", G_TYPE_INT, (int) stream->flavor,
1314           "rate", G_TYPE_INT, (int) stream->rate,
1315           "channels", G_TYPE_INT, (int) stream->n_channels,
1316           "width", G_TYPE_INT, (int) stream->sample_width,
1317           "leaf_size", G_TYPE_INT, (int) stream->leaf_size,
1318           "packet_size", G_TYPE_INT, (int) stream->packet_size,
1319           "height", G_TYPE_INT, (int) stream->height, NULL);
1320     }
1321     rmdemux->n_audio_streams++;
1322   } else {
1323     GST_WARNING_OBJECT (rmdemux, "not adding stream of type %d",
1324         stream->subtype);
1325     goto beach;
1326   }
1328   GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
1329   rmdemux->streams[rmdemux->n_streams] = stream;
1330   rmdemux->n_streams++;
1331   GST_LOG_OBJECT (rmdemux, "n_streams is now %d", rmdemux->n_streams);
1333   if (stream->pad && stream->caps) {
1334     GST_DEBUG_OBJECT (rmdemux, "setting caps: %p", stream->caps);
1336     gst_pad_set_caps (stream->pad, stream->caps);
1337     gst_caps_unref (stream->caps);
1339     gst_pad_set_getcaps_function (stream->pad,
1340         GST_DEBUG_FUNCPTR (gst_rmdemux_src_getcaps));
1341     gst_pad_set_event_function (stream->pad,
1342         GST_DEBUG_FUNCPTR (gst_rmdemux_src_event));
1343     gst_pad_set_query_type_function (stream->pad,
1344         GST_DEBUG_FUNCPTR (gst_rmdemux_src_query_types));
1345     gst_pad_set_query_function (stream->pad,
1346         GST_DEBUG_FUNCPTR (gst_rmdemux_src_query));
1348     GST_DEBUG_OBJECT (rmdemux, "adding pad %p to rmdemux %p", stream->pad,
1349         rmdemux);
1350     gst_element_add_pad (GST_ELEMENT (rmdemux), stream->pad);
1352     gst_pad_push_event (stream->pad,
1353         gst_event_new_newsegment (FALSE, 1.0, GST_FORMAT_TIME, (gint64) 0,
1354             (gint64) - 1, 0));
1356     /* If there's some extra data then send it as the first packet */
1357     if (stream->extra_data_size > 0) {
1358       GstBuffer *buffer;
1360       if ((ret = gst_pad_alloc_buffer
1361               (stream->pad, GST_BUFFER_OFFSET_NONE, stream->extra_data_size,
1362                   stream->caps, &buffer))
1363           != GST_FLOW_OK) {
1364         GST_WARNING_OBJECT (rmdemux, "failed to alloc extra_data src "
1365             "buffer for stream %d", stream->id);
1366         goto beach;
1367       }
1369       memcpy (GST_BUFFER_DATA (buffer), stream->extra_data,
1370           stream->extra_data_size);
1372       GST_DEBUG_OBJECT (rmdemux, "Pushing extra_data of size %d to pad",
1373           stream->extra_data_size);
1374       ret = gst_pad_push (stream->pad, buffer);
1375     }
1376   }
1378 beach:
1379   return ret;
1382 G_GNUC_UNUSED static void
1383 re_hexdump_bytes (guint8 * ptr, int len, int offset)
1385   guint8 *end = ptr + len;
1386   int i;
1388   while (1) {
1389     if (ptr >= end)
1390       return;
1391     g_print ("%08x: ", offset);
1392     for (i = 0; i < 16; i++) {
1393       if (ptr + i >= end) {
1394         g_print ("   ");
1395       } else {
1396         g_print ("%02x ", ptr[i]);
1397       }
1398     }
1399     for (i = 0; i < 16; i++) {
1400       if (ptr + i >= end) {
1401         g_print (" ");
1402       } else {
1403         g_print ("%c", g_ascii_isprint (ptr[i]) ? ptr[i] : '.');
1404       }
1405     }
1406     g_print ("\n");
1407     ptr += 16;
1408     offset += 16;
1409   }
1412 static char *
1413 re_get_pascal_string (const guint8 * ptr)
1415   int length;
1417   length = ptr[0];
1418   return g_strndup ((char *) ptr + 1, length);
1421 static int
1422 re_skip_pascal_string (const guint8 * ptr)
1424   int length;
1426   length = ptr[0];
1428   return length + 1;
1431 static void
1432 gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const void *data, int length)
1434   GST_LOG_OBJECT (rmdemux, "file_version: %d", RMDEMUX_GUINT32_GET (data));
1435   GST_LOG_OBJECT (rmdemux, "num_headers: %d", RMDEMUX_GUINT32_GET (data + 4));
1438 static void
1439 gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const void *data, int length)
1441   GST_LOG_OBJECT (rmdemux, "max bitrate: %d", RMDEMUX_GUINT32_GET (data));
1442   GST_LOG_OBJECT (rmdemux, "avg bitrate: %d", RMDEMUX_GUINT32_GET (data + 4));
1443   GST_LOG_OBJECT (rmdemux, "max packet size: %d",
1444       RMDEMUX_GUINT32_GET (data + 8));
1445   rmdemux->avg_packet_size = RMDEMUX_GUINT32_GET (data + 12);
1446   GST_LOG_OBJECT (rmdemux, "avg packet size: %d", rmdemux->avg_packet_size);
1447   rmdemux->num_packets = RMDEMUX_GUINT32_GET (data + 16);
1448   GST_LOG_OBJECT (rmdemux, "number of packets: %d", rmdemux->num_packets);
1450   GST_LOG_OBJECT (rmdemux, "duration: %d", RMDEMUX_GUINT32_GET (data + 20));
1451   rmdemux->duration = RMDEMUX_GUINT32_GET (data + 20) * GST_MSECOND;
1453   GST_LOG_OBJECT (rmdemux, "preroll: %d", RMDEMUX_GUINT32_GET (data + 24));
1454   rmdemux->index_offset = RMDEMUX_GUINT32_GET (data + 28);
1455   GST_LOG_OBJECT (rmdemux, "offset of INDX section: 0x%08x",
1456       rmdemux->index_offset);
1457   rmdemux->data_offset = RMDEMUX_GUINT32_GET (data + 32);
1458   GST_LOG_OBJECT (rmdemux, "offset of DATA section: 0x%08x",
1459       rmdemux->data_offset);
1460   GST_LOG_OBJECT (rmdemux, "n streams: %d", RMDEMUX_GUINT16_GET (data + 36));
1461   GST_LOG_OBJECT (rmdemux, "flags: 0x%04x", RMDEMUX_GUINT16_GET (data + 38));
1464 static GstFlowReturn
1465 gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const void *data, int length)
1467   GstRMDemuxStream *stream;
1468   char *stream1_type_string;
1469   char *stream2_type_string;
1470   int stream_type;
1471   int offset;
1473   //re_hexdump_bytes ((guint8 *) data, length, 0);
1475   stream = g_new0 (GstRMDemuxStream, 1);
1477   stream->id = RMDEMUX_GUINT16_GET (data);
1478   stream->index = NULL;
1479   stream->seek_offset = 0;
1480   GST_LOG_OBJECT (rmdemux, "stream_number=%d", stream->id);
1482   offset = 30;
1483   stream_type = GST_RMDEMUX_STREAM_UNKNOWN;
1484   stream1_type_string = re_get_pascal_string (data + offset);
1485   offset += re_skip_pascal_string (data + offset);
1486   stream2_type_string = re_get_pascal_string (data + offset);
1487   offset += re_skip_pascal_string (data + offset);
1489   /* stream1_type_string for audio and video stream is a "put_whatever_you_want" field :
1490      observed values :
1491      - "[The ]Video/Audio Stream" (File produced by an official Real encoder)
1492      - "RealVideoPremierePlugIn-VIDEO/AUDIO" (File produced by Abobe Premiere)
1494      so, we should not rely on it to know which stream type it is
1495    */
1497   GST_LOG_OBJECT (rmdemux, "stream type: %s", stream1_type_string);
1498   GST_LOG_OBJECT (rmdemux, "MIME type=%s", stream2_type_string);
1500   if (strcmp (stream2_type_string, "video/x-pn-realvideo") == 0) {
1501     stream_type = GST_RMDEMUX_STREAM_VIDEO;
1502   } else if (strcmp (stream2_type_string, "audio/x-pn-realaudio") == 0) {
1503     stream_type = GST_RMDEMUX_STREAM_AUDIO;
1504   } else if (strcmp (stream2_type_string, "audio/x-ralf-mpeg4-generic") == 0) {
1505     /* Another audio type found in the real testsuite */
1506     stream_type = GST_RMDEMUX_STREAM_AUDIO;
1507   } else if (strcmp (stream1_type_string, "") == 0 &&
1508       strcmp (stream2_type_string, "logical-fileinfo") == 0) {
1509     stream_type = GST_RMDEMUX_STREAM_FILEINFO;
1510   } else {
1511     stream_type = GST_RMDEMUX_STREAM_UNKNOWN;
1512     GST_WARNING_OBJECT (rmdemux, "unknown stream type \"%s\",\"%s\"",
1513         stream1_type_string, stream2_type_string);
1514   }
1515   g_free (stream1_type_string);
1516   g_free (stream2_type_string);
1518   offset += 4;
1520   stream->subtype = stream_type;
1521   switch (stream_type) {
1523     case GST_RMDEMUX_STREAM_VIDEO:
1524       /* RV10/RV20/RV30/RV40 => video/x-pn-realvideo, version=1,2,3,4 */
1525       stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 8);
1526       stream->width = RMDEMUX_GUINT16_GET (data + offset + 12);
1527       stream->height = RMDEMUX_GUINT16_GET (data + offset + 14);
1528       stream->rate = RMDEMUX_GUINT16_GET (data + offset + 16);
1529       stream->subformat = RMDEMUX_GUINT32_GET (data + offset + 26);
1530       stream->format = RMDEMUX_GUINT32_GET (data + offset + 30);
1531       stream->extra_data_size = length - (offset + 34);
1532       stream->extra_data = (guint8 *) data + offset + 34;
1533       stream->frame_rate = (double) RMDEMUX_GUINT16_GET (data + offset + 22) +
1534           ((double) RMDEMUX_GUINT16_GET (data + offset + 24) / 65536.0);
1536       GST_DEBUG_OBJECT (rmdemux,
1537           "Video stream with fourcc=%" GST_FOURCC_FORMAT
1538           " width=%d height=%d rate=%d frame_rate=%f subformat=%x format=%x extra_data_size=%d",
1539           GST_FOURCC_ARGS (stream->fourcc), stream->width, stream->height,
1540           stream->rate, stream->frame_rate, stream->subformat, stream->format,
1541           stream->extra_data_size);
1542       break;
1543     case GST_RMDEMUX_STREAM_AUDIO:{
1544       stream->version = RMDEMUX_GUINT16_GET (data + offset + 4);
1545       stream->flavor = RMDEMUX_GUINT16_GET (data + offset + 22);
1546       stream->packet_size = RMDEMUX_GUINT32_GET (data + offset + 24);
1547       stream->leaf_size = RMDEMUX_GUINT16_GET (data + offset + 44);
1548       stream->height = RMDEMUX_GUINT16_GET (data + offset + 40);
1550       switch (stream->version) {
1551         case 4:
1552           stream->rate = RMDEMUX_GUINT16_GET (data + offset + 48);
1553           stream->sample_width = RMDEMUX_GUINT16_GET (data + offset + 52);
1554           stream->n_channels = RMDEMUX_GUINT16_GET (data + offset + 54);
1555           stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 62);
1556           stream->extra_data_size = 16;
1557           stream->extra_data = (guint8 *) data + offset + 71;
1558           break;
1559         case 5:
1560           stream->rate = RMDEMUX_GUINT16_GET (data + offset + 54);
1561           stream->sample_width = RMDEMUX_GUINT16_GET (data + offset + 58);
1562           stream->n_channels = RMDEMUX_GUINT16_GET (data + offset + 60);
1563           stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 66);
1564           stream->extra_data_size = RMDEMUX_GUINT32_GET (data + offset + 74);
1565           stream->extra_data = (guint8 *) data + offset + 78;
1566           break;
1567       }
1569       /*  14_4, 28_8, cook, dnet, sipr, raac, racp, ralf, atrc */
1570       GST_DEBUG_OBJECT (rmdemux,
1571           "Audio stream with rate=%d sample_width=%d n_channels=%d",
1572           stream->rate, stream->sample_width, stream->n_channels);
1574       break;
1575     }
1576     case GST_RMDEMUX_STREAM_FILEINFO:
1577     {
1578       int element_nb;
1580       /* Length of this section */
1581       GST_DEBUG_OBJECT (rmdemux, "length2: 0x%08x",
1582           RMDEMUX_GUINT32_GET (data + offset));
1583       offset += 4;
1585       /* Unknown : 00 00 00 00 */
1586       offset += 4;
1588       /* Number of variables that would follow (loop iterations) */
1589       element_nb = RMDEMUX_GUINT32_GET (data + offset);
1590       offset += 4;
1592       while (element_nb) {
1593         /* Category Id : 00 00 00 XX 00 00 */
1594         offset += 6;
1596         /* Variable Name */
1597         offset += re_skip_pascal_string (data + offset);
1599         /* Variable Value Type */
1600         /*   00 00 00 00 00 => integer/boolean, preceded by length */
1601         /*   00 00 00 02 00 => pascal string, preceded by length, no trailing \0 */
1602         offset += 5;
1604         /* Variable Value */
1605         offset += re_skip_pascal_string (data + offset);
1607         element_nb--;
1608       }
1609     }
1610       break;
1611     case GST_RMDEMUX_STREAM_UNKNOWN:
1612     default:
1613       break;
1614   }
1616   return gst_rmdemux_add_stream (rmdemux, stream);
1619 static guint
1620 gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const void *data, int length)
1622   int n;
1623   int id;
1625   n = RMDEMUX_GUINT32_GET (data);
1626   id = RMDEMUX_GUINT16_GET (data + 4);
1627   rmdemux->index_offset = RMDEMUX_GUINT32_GET (data + 6);
1629   GST_DEBUG_OBJECT (rmdemux, "Number of indices=%d Stream ID=%d length=%d", n,
1630       id, length);
1632   /* Point to the next index_stream */
1633   rmdemux->index_stream = gst_rmdemux_get_stream_by_id (rmdemux, id);
1635   /* Return the length of the index */
1636   return 14 * n;
1639 static void
1640 gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const void *data, int length)
1642   int i;
1643   int n;
1644   GstRMDemuxIndex *index;
1646   /* The number of index records */
1647   n = length / 14;
1649   if (rmdemux->index_stream == NULL)
1650     return;
1652   index = g_malloc (sizeof (GstRMDemuxIndex) * n);
1653   rmdemux->index_stream->index = index;
1654   rmdemux->index_stream->index_length = n;
1656   for (i = 0; i < n; i++) {
1657     index[i].timestamp = RMDEMUX_GUINT32_GET (data + 2) * GST_MSECOND;
1658     index[i].offset = RMDEMUX_GUINT32_GET (data + 6);
1660     GST_DEBUG_OBJECT (rmdemux, "Index found for timestamp=%f (at offset=%x)",
1661         (float) index[i].timestamp / GST_SECOND, index[i].offset);
1662     data += 14;
1663   }
1666 static void
1667 gst_rmdemux_parse_data (GstRMDemux * rmdemux, const void *data, int length)
1669   rmdemux->n_chunks = RMDEMUX_GUINT32_GET (data);
1670   rmdemux->data_offset = RMDEMUX_GUINT32_GET (data + 4);
1671   rmdemux->chunk_index = 0;
1672   GST_DEBUG_OBJECT (rmdemux,
1673       "Data chunk found with %d packets (next data at %p)", rmdemux->n_chunks,
1674       rmdemux->data_offset);
1677 static void
1678 gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const void *data, int length)
1680   gchar *title = (gchar *) re_get_pascal_string (data);
1682   GST_DEBUG_OBJECT (rmdemux, "File Content : (CONT) %s", title);
1683   g_free (title);
1686 static GstFlowReturn
1687 gst_rmdemux_parse_packet (GstRMDemux * rmdemux, const void *data,
1688     guint16 version, guint16 length)
1690   guint16 id;
1691   GstRMDemuxStream *stream;
1692   GstBuffer *buffer;
1693   guint16 packet_size;
1694   GstFlowReturn ret = GST_FLOW_OK;
1696   id = RMDEMUX_GUINT16_GET (data);
1697   rmdemux->cur_timestamp = RMDEMUX_GUINT32_GET (data + 2) * GST_MSECOND;
1699   GST_DEBUG_OBJECT (rmdemux,
1700       "Parsing a packet for stream=%d, timestamp=" GST_TIME_FORMAT
1701       ", version=%d", id, GST_TIME_ARGS (rmdemux->cur_timestamp), version);
1703   // TODO: This is skipping over either 2 or 3 bytes (version dependent)
1704   // without even reading it. What are these for?
1705   if (version == 0) {
1706     data += 8;
1707     packet_size = length - 8;
1708   } else {
1709     data += 9;
1710     packet_size = length - 9;
1711   }
1713   stream = gst_rmdemux_get_stream_by_id (rmdemux, id);
1715   if (!stream) {
1716     GST_WARNING_OBJECT (rmdemux, "No stream for stream id %d in parsing "
1717         "data packet", id);
1718     goto beach;
1719   }
1721   if ((rmdemux->offset + packet_size) > stream->seek_offset &&
1722       stream && stream->pad) {
1723     if ((ret = gst_pad_alloc_buffer (stream->pad, GST_BUFFER_OFFSET_NONE,
1724                 packet_size, stream->caps, &buffer)) != GST_FLOW_OK) {
1725       GST_WARNING_OBJECT (rmdemux, "failed to alloc src buffer for stream %d",
1726           id);
1727       return ret;
1728     }
1730     memcpy (GST_BUFFER_DATA (buffer), (guint8 *) data, packet_size);
1731     GST_BUFFER_TIMESTAMP (buffer) = rmdemux->cur_timestamp;
1733     GST_DEBUG_OBJECT (rmdemux, "Pushing buffer of size %d to pad", packet_size);
1734     ret = gst_pad_push (stream->pad, buffer);
1735   } else {
1736     GST_DEBUG_OBJECT (rmdemux,
1737         "Stream %d is skipping: seek_offset=%d, offset=%d, packet_size",
1738         stream->id, stream->seek_offset, rmdemux->offset, packet_size);
1739   }
1741 beach:
1742   return ret;