]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugins-ugly0-10.git/blob - gst/mpegstream/gstdvddemux.c
5ca52363905abb48c0a004bb3c2cf5135cb9c44e
[glsdk/gst-plugins-ugly0-10.git] / gst / mpegstream / gstdvddemux.c
1 /* GStreamer
2  * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <string.h>
27 #include "gstdvddemux.h"
29 /* 
30  * Start the timestamp sequence at 2 seconds to allow for strange audio
31  * timestamps when audio crosses a VOBU 
32  */
33 #define INITIAL_END_PTM (2 * GST_SECOND)
35 GST_DEBUG_CATEGORY_STATIC (gstdvddemux_debug);
36 #define GST_CAT_DEFAULT (gstdvddemux_debug)
39 #define PARSE_CLASS(o)  GST_MPEG_PARSE_CLASS (G_OBJECT_GET_CLASS (o))
40 #define DEMUX_CLASS(o)  GST_MPEG_DEMUX_CLASS (G_OBJECT_GET_CLASS (o))
41 #define CLASS(o)  GST_DVD_DEMUX_CLASS (G_OBJECT_GET_CLASS (o))
44 /* Element factory information */
45 static GstElementDetails dvd_demux_details = {
46   "DVD Demuxer",
47   "Codec/Demuxer",
48   "Demultiplexes DVD (VOB) MPEG2 streams",
49   "Martin Soto <soto@informatik.uni-kl.de>"
50 };
52 /* DVDDemux signals and args */
53 enum
54 {
55   /* FILL ME */
56   LAST_SIGNAL
57 };
59 enum
60 {
61   ARG_0
62       /* FILL ME */
63 };
66 /* Define the capabilities separately, to be able to reuse them. */
68 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
69     GST_PAD_SINK,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS ("video/mpeg, "
72         "mpegversion = (int) 2, " "systemstream = (boolean) TRUE")
73     );
75 #define VIDEO_CAPS \
76   GST_STATIC_CAPS ("video/mpeg, " \
77     "mpegversion = (int) { 1, 2 }, " \
78     "systemstream = (boolean) FALSE" \
79   )
81 #define AUDIO_CAPS \
82   GST_STATIC_CAPS ( \
83     "audio/mpeg, " \
84       "mpegversion = (int) 1;" \
85     "audio/x-dvd-lpcm, " \
86       "width = (int) { 16, 20, 24 }, " \
87       "rate = (int) { 48000, 96000 }, " \
88       "channels = (int) [ 1, 8 ], " \
89       "dynamic_range = (int) [ 0, 255 ], " \
90       "emphasis = (boolean) { FALSE, TRUE }, " \
91       "mute = (boolean) { FALSE, TRUE }; " \
92     "audio/x-ac3;" \
93     "audio/x-dts" \
94   )
96 #define SUBPICTURE_CAPS \
97   GST_STATIC_CAPS ("video/x-dvd-subpicture")
99 static GstStaticPadTemplate cur_video_template =
100 GST_STATIC_PAD_TEMPLATE ("current_video",
101     GST_PAD_SRC,
102     GST_PAD_ALWAYS,
103     VIDEO_CAPS);
105 static GstStaticPadTemplate audio_template =
106 GST_STATIC_PAD_TEMPLATE ("dvd_audio_%02d",
107     GST_PAD_SRC,
108     GST_PAD_SOMETIMES,
109     AUDIO_CAPS);
111 static GstStaticPadTemplate cur_audio_template =
112 GST_STATIC_PAD_TEMPLATE ("current_audio",
113     GST_PAD_SRC,
114     GST_PAD_ALWAYS,
115     AUDIO_CAPS);
117 static GstStaticPadTemplate subpicture_template =
118 GST_STATIC_PAD_TEMPLATE ("subpicture_%d",
119     GST_PAD_SRC,
120     GST_PAD_SOMETIMES,
121     SUBPICTURE_CAPS);
123 static GstStaticPadTemplate cur_subpicture_template =
124 GST_STATIC_PAD_TEMPLATE ("current_subpicture",
125     GST_PAD_SRC,
126     GST_PAD_ALWAYS,
127     SUBPICTURE_CAPS);
130 static void gst_dvd_demux_class_init (GstDVDDemuxClass * klass);
131 static void gst_dvd_demux_base_init (GstDVDDemuxClass * klass);
132 static void gst_dvd_demux_init (GstDVDDemux * dvd_demux);
134 static void gst_dvd_demux_send_data (GstMPEGParse * mpeg_parse,
135     GstData * data, GstClockTime time);
137 static void gst_dvd_demux_send_discont
138     (GstMPEGParse * mpeg_parse, GstClockTime time);
139 static void gst_dvd_demux_handle_discont
140     (GstMPEGParse * mpeg_parse, GstEvent * event);
141 static gboolean gst_dvd_demux_handle_dvd_event
142     (GstDVDDemux * dvd_demux, GstEvent * event);
144 static GstMPEGStream *gst_dvd_demux_get_video_stream
145     (GstMPEGDemux * mpeg_demux,
146     guint8 stream_nr, gint type, const gpointer info);
147 static GstMPEGStream *gst_dvd_demux_get_audio_stream
148     (GstMPEGDemux * dvd_demux,
149     guint8 stream_nr, gint type, const gpointer info);
150 static GstMPEGStream *gst_dvd_demux_get_subpicture_stream
151     (GstMPEGDemux * dvd_demux,
152     guint8 stream_nr, gint type, const gpointer info);
154 static void gst_dvd_demux_process_private
155     (GstMPEGDemux * mpeg_demux,
156     GstBuffer * buffer,
157     guint stream_nr, GstClockTime timestamp, guint headerlen, guint datalen);
159 static void gst_dvd_demux_send_subbuffer
160     (GstMPEGDemux * mpeg_demux,
161     GstMPEGStream * outstream,
162     GstBuffer * buffer, GstClockTime timestamp, guint offset, guint size);
164 static void gst_dvd_demux_set_cur_audio
165     (GstDVDDemux * dvd_demux, gint stream_nr);
166 static void gst_dvd_demux_set_cur_subpicture
167     (GstDVDDemux * dvd_demux, gint stream_nr);
169 static void gst_dvd_demux_reset (GstDVDDemux * dvd_demux);
172 static GstElementStateReturn gst_dvd_demux_change_state (GstElement * element);
174 static GstMPEGDemuxClass *parent_class = NULL;
176 /*static guint gst_dvd_demux_signals[LAST_SIGNAL] = { 0 };*/
179 GType
180 gst_dvd_demux_get_type (void)
182   static GType dvd_demux_type = 0;
184   if (!dvd_demux_type) {
185     static const GTypeInfo dvd_demux_info = {
186       sizeof (GstDVDDemuxClass),
187       (GBaseInitFunc) gst_dvd_demux_base_init,
188       NULL,
189       (GClassInitFunc) gst_dvd_demux_class_init,
190       NULL,
191       NULL,
192       sizeof (GstDVDDemux),
193       0,
194       (GInstanceInitFunc) gst_dvd_demux_init,
195     };
197     dvd_demux_type = g_type_register_static (GST_TYPE_MPEG_DEMUX,
198         "GstDVDDemux", &dvd_demux_info, 0);
200     GST_DEBUG_CATEGORY_INIT (gstdvddemux_debug, "dvddemux", 0,
201         "DVD (VOB) demultiplexer element");
202   }
204   return dvd_demux_type;
208 static void
209 gst_dvd_demux_base_init (GstDVDDemuxClass * klass)
211   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
212   GstMPEGDemuxClass *demux_class = GST_MPEG_DEMUX_CLASS (klass);
213   GstMPEGParseClass *mpeg_parse_class = (GstMPEGParseClass *) klass;
215   mpeg_parse_class->send_data = gst_dvd_demux_send_data;
217   /* sink pad */
218   gst_element_class_add_pad_template (element_class,
219       gst_static_pad_template_get (&sink_template));
221   demux_class->audio_template = gst_static_pad_template_get (&audio_template);
223   klass->cur_video_template = gst_static_pad_template_get (&cur_video_template);
224   klass->cur_audio_template = gst_static_pad_template_get (&cur_audio_template);
225   klass->subpicture_template =
226       gst_static_pad_template_get (&subpicture_template);
227   klass->cur_subpicture_template =
228       gst_static_pad_template_get (&cur_subpicture_template);
230   gst_element_class_add_pad_template (element_class,
231       demux_class->audio_template);
233   gst_element_class_add_pad_template (element_class, klass->cur_video_template);
234   gst_element_class_add_pad_template (element_class, klass->cur_audio_template);
235   gst_element_class_add_pad_template (element_class,
236       klass->subpicture_template);
237   gst_element_class_add_pad_template (element_class,
238       klass->cur_subpicture_template);
240   gst_element_class_set_details (element_class, &dvd_demux_details);
244 static void
245 gst_dvd_demux_class_init (GstDVDDemuxClass * klass)
247   GstElementClass *gstelement_class;
248   GstMPEGParseClass *mpeg_parse_class;
249   GstMPEGDemuxClass *mpeg_demux_class;
251   parent_class = g_type_class_ref (GST_TYPE_MPEG_DEMUX);
253   gstelement_class = (GstElementClass *) klass;
254   mpeg_parse_class = (GstMPEGParseClass *) klass;
255   mpeg_demux_class = (GstMPEGDemuxClass *) klass;
257   gstelement_class->change_state = gst_dvd_demux_change_state;
259   mpeg_parse_class->send_discont = gst_dvd_demux_send_discont;
260   mpeg_parse_class->handle_discont = gst_dvd_demux_handle_discont;
262   mpeg_demux_class->get_audio_stream = gst_dvd_demux_get_audio_stream;
263   mpeg_demux_class->get_video_stream = gst_dvd_demux_get_video_stream;
264   mpeg_demux_class->send_subbuffer = gst_dvd_demux_send_subbuffer;
265   mpeg_demux_class->process_private = gst_dvd_demux_process_private;
267   klass->get_subpicture_stream = gst_dvd_demux_get_subpicture_stream;
271 static void
272 gst_dvd_demux_init (GstDVDDemux * dvd_demux)
274   GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (dvd_demux);
275   gint i;
277   GST_FLAG_SET (dvd_demux, GST_ELEMENT_EVENT_AWARE);
279   /* Create the pads for the current streams. */
280   dvd_demux->cur_video =
281       DEMUX_CLASS (dvd_demux)->new_output_pad (mpeg_demux, "current_video",
282       CLASS (dvd_demux)->cur_video_template);
283   gst_element_add_pad (GST_ELEMENT (mpeg_demux), dvd_demux->cur_video);
284   dvd_demux->cur_audio =
285       DEMUX_CLASS (dvd_demux)->new_output_pad (mpeg_demux, "current_audio",
286       CLASS (dvd_demux)->cur_audio_template);
287   gst_element_add_pad (GST_ELEMENT (mpeg_demux), dvd_demux->cur_audio);
288   dvd_demux->cur_subpicture =
289       DEMUX_CLASS (dvd_demux)->new_output_pad (mpeg_demux, "current_subpicture",
290       CLASS (dvd_demux)->cur_subpicture_template);
291   gst_element_add_pad (GST_ELEMENT (mpeg_demux), dvd_demux->cur_subpicture);
293   dvd_demux->mpeg_version = 0;
294   dvd_demux->cur_video_nr = 0;
295   dvd_demux->cur_audio_nr = 0;
296   dvd_demux->cur_subpicture_nr = 0;
298   dvd_demux->last_end_ptm = INITIAL_END_PTM;
299   dvd_demux->just_flushed = FALSE;
300   dvd_demux->discont_time = GST_CLOCK_TIME_NONE;
302   for (i = 0; i < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS; i++) {
303     dvd_demux->subpicture_stream[i] = NULL;
304   }
308 static void
309 gst_dvd_demux_send_data (GstMPEGParse * mpeg_parse, GstData * data,
310     GstClockTime time)
312   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_parse);
314   if (GST_IS_BUFFER (data)) {
315     gst_buffer_unref (GST_BUFFER (data));
316   } else {
317     GstEvent *event = GST_EVENT (data);
319     switch (GST_EVENT_TYPE (event)) {
320       case GST_EVENT_ANY:
321         gst_dvd_demux_handle_dvd_event (dvd_demux, event);
322         break;
324       case GST_EVENT_FLUSH:
325         GST_DEBUG_OBJECT (dvd_demux, "flush received");
327         dvd_demux->just_flushed = TRUE;
329         /* Propagate the event normally. */
330         gst_pad_event_default (mpeg_parse->sinkpad, event);
331         break;
333       default:
334         gst_pad_event_default (mpeg_parse->sinkpad, event);
335         break;
336     }
337   }
341 static gboolean
342 gst_dvd_demux_handle_dvd_event (GstDVDDemux * dvd_demux, GstEvent * event)
344   GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (dvd_demux);
345   GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (dvd_demux);
346   GstStructure *structure = event->event_data.structure.structure;
347   const char *event_type = gst_structure_get_string (structure, "event");
349   g_return_val_if_fail (event != NULL, FALSE);
351 #ifndef GST_DISABLE_GST_DEBUG
352   {
353     gchar *text = gst_structure_to_string (structure);
355     GST_LOG_OBJECT (dvd_demux, "processing event \"%s\"", text);
356     g_free (text);
357   }
358 #endif
360   if (strcmp (event_type, "dvd-audio-stream-change") == 0) {
361     gint stream_nr;
363     gst_structure_get_int (structure, "physical", &stream_nr);
364     if (stream_nr < -1 || stream_nr >= GST_MPEG_DEMUX_NUM_AUDIO_STREAMS) {
365       GST_ERROR_OBJECT (dvd_demux,
366           "GstDVDDemux: Invalid audio stream %02d", stream_nr);
367       return FALSE;
368     }
369     gst_dvd_demux_set_cur_audio (dvd_demux, stream_nr);
370     gst_event_unref (event);
371   }
373   else if (strcmp (event_type, "dvd-spu-stream-change") == 0) {
374     gint stream_nr;
376     gst_structure_get_int (structure, "physical", &stream_nr);
377     if (stream_nr < -1 || stream_nr >= GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS) {
378       GST_ERROR_OBJECT (dvd_demux,
379           "GstDVDDemux: Invalid subpicture stream %02d", stream_nr);
380       return FALSE;
381     }
382     gst_dvd_demux_set_cur_subpicture (dvd_demux, stream_nr);
383     gst_event_unref (event);
384   }
386   else if (strcmp (event_type, "dvd-nav-packet") == 0) {
387     GstStructure *structure = event->event_data.structure.structure;
388     GstClockTime start_ptm =
389         g_value_get_uint64 (gst_structure_get_value (structure, "start_ptm"));
390     GstClockTime end_ptm =
391         g_value_get_uint64 (gst_structure_get_value (structure, "end_ptm"));
393     if (start_ptm != dvd_demux->last_end_ptm) {
394       /* Set the adjust value to gap the discontinuity. */
395       mpeg_demux->adjust += GST_CLOCK_DIFF (dvd_demux->last_end_ptm, start_ptm);
397       GST_DEBUG_OBJECT (dvd_demux,
398           "PTM sequence discontinuity: from %0.3fs to "
399           "%0.3fs, new adjust %0.3fs",
400           (double) dvd_demux->last_end_ptm / GST_SECOND,
401           (double) start_ptm / GST_SECOND,
402           (double) mpeg_demux->adjust / GST_SECOND);
404       /* Disable mpeg_parse's timestamp adjustment in favour of the info
405        * from DVD nav packets.
406        * Timestamp adjustment is fairly evil, we would ideally use discont
407        * events instead. However, our current clocking has a pretty serious
408        * race condition: imagine that $pipeline is at time 30sec and $audio
409        * receives a discont to 0sec. Video processes its last buffer and
410        * calls _wait() on $timestamp, which is 30s - so we wait (hang) 30sec.
411        * This is unacceptable, obviously, and timestamp adjustment, no matter
412        * how evil, solves this.
413        * Before disabling this again, tripple check that al .vob files on our
414        * websites /media/ directory work fine, especially bullet.vob and
415        * barrage.vob.
416        */
417 #if 1
418       /* Try to prevent the mpegparse infrastructure from doing timestamp
419          adjustment. */
420       mpeg_parse->use_adjust = FALSE;
421       mpeg_parse->adjust = 0;
422 #endif
423     }
424     dvd_demux->last_end_ptm = end_ptm;
426     if (dvd_demux->just_flushed) {
427       /* The pipeline was just flushed, schedule a discontinuity with
428          the next sequence time. We don't do it here to reduce the
429          time gap between the discontinuity and the subsequent data
430          blocks. */
431 #if 1
432       dvd_demux->discont_time = start_ptm + mpeg_demux->adjust;
433 #else
434       dvd_demux->discont_time = start_ptm;
435 #endif
436       GST_DEBUG_OBJECT (dvd_demux, "Set discont time to %" G_GINT64_FORMAT,
437           dvd_demux->discont_time);
439       dvd_demux->just_flushed = FALSE;
440     }
442     gst_event_unref (event);
443   }
445   else {
446     if (GST_EVENT_TIMESTAMP (event) != GST_CLOCK_TIME_NONE) {
447       GST_EVENT_TIMESTAMP (event) += mpeg_demux->adjust;
448     }
449     gst_pad_event_default (mpeg_parse->sinkpad, event);
450   }
452   return TRUE;
456 static void
457 gst_dvd_demux_send_discont (GstMPEGParse * mpeg_parse, GstClockTime time)
459   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_parse);
460   GstEvent *discont;
461   gint i;
463   GST_DEBUG_OBJECT (dvd_demux, "sending discontinuity: %0.3fs",
464       (double) time / GST_SECOND);
466   GST_MPEG_PARSE_CLASS (parent_class)->send_discont (mpeg_parse, time);
468   discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time, NULL);
469   if (!discont) {
470     GST_ELEMENT_ERROR (GST_ELEMENT (dvd_demux),
471         RESOURCE, FAILED, (NULL), ("Allocation failed"));
472     return;
473   }
475   for (i = 0; i < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS; i++) {
476     if (dvd_demux->subpicture_stream[i] &&
477         GST_PAD_IS_USABLE (dvd_demux->subpicture_stream[i]->pad)) {
479       gst_event_ref (discont);
480       gst_pad_push (dvd_demux->subpicture_stream[i]->pad, GST_DATA (discont));
481     }
482   }
484   /* Distribute the event to the "current" pads. */
485   if (GST_PAD_IS_USABLE (dvd_demux->cur_video)) {
486     gst_event_ref (discont);
487     gst_pad_push (dvd_demux->cur_video, GST_DATA (discont));
488   }
490   if (GST_PAD_IS_USABLE (dvd_demux->cur_audio)) {
491     gst_event_ref (discont);
492     gst_pad_push (dvd_demux->cur_audio, GST_DATA (discont));
493   }
495   if (GST_PAD_IS_USABLE (dvd_demux->cur_subpicture)) {
496     gst_event_ref (discont);
497     gst_pad_push (dvd_demux->cur_subpicture, GST_DATA (discont));
498   }
500   gst_event_unref (discont);
503 static void
504 gst_dvd_demux_handle_discont (GstMPEGParse * mpeg_parse, GstEvent * event)
506   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_parse);
508   if (GST_EVENT_DISCONT_NEW_MEDIA (event)) {
509     gst_dvd_demux_reset (dvd_demux);
510   }
512   /* let parent handle and forward discont */
513   if (GST_MPEG_PARSE_CLASS (parent_class)->handle_discont != NULL)
514     GST_MPEG_PARSE_CLASS (parent_class)->handle_discont (mpeg_parse, event);
517 static GstMPEGStream *
518 gst_dvd_demux_get_video_stream (GstMPEGDemux * mpeg_demux,
519     guint8 stream_nr, gint type, const gpointer info)
521   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
522   GstMPEGStream *str =
523       parent_class->get_video_stream (mpeg_demux, stream_nr, type, info);
524   gint mpeg_version = *((gint *) info);
526   if (dvd_demux->mpeg_version != mpeg_version) {
527     GstCaps *caps;
529     caps = gst_caps_new_simple ("video/mpeg",
530         "mpegversion", G_TYPE_INT, mpeg_version,
531         "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
533     if (!gst_pad_set_explicit_caps (dvd_demux->cur_video, caps)) {
534       GST_ELEMENT_ERROR (GST_ELEMENT (mpeg_demux),
535           CORE, NEGOTIATION, (NULL), ("failed to set caps"));
536     } else {
537       dvd_demux->mpeg_version = mpeg_version;
538     }
539     gst_caps_free (caps);
540   }
542   return str;
545 static GstMPEGStream *
546 gst_dvd_demux_get_audio_stream (GstMPEGDemux * mpeg_demux,
547     guint8 stream_nr, gint type, const gpointer info)
549   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
550   guint32 sample_info = 0;
551   GstMPEGStream *str;
552   GstDVDLPCMStream *lpcm_str = NULL;
553   gboolean add_pad = FALSE;
554   GstCaps *caps;
556   g_return_val_if_fail (stream_nr < GST_MPEG_DEMUX_NUM_AUDIO_STREAMS, NULL);
557   g_return_val_if_fail (type > GST_MPEG_DEMUX_AUDIO_UNKNOWN &&
558       type < GST_DVD_DEMUX_AUDIO_LAST, NULL);
560   if (type < GST_MPEG_DEMUX_AUDIO_LAST) {
561     return parent_class->get_audio_stream (mpeg_demux, stream_nr, type, info);
562   }
564   if (type == GST_DVD_DEMUX_AUDIO_LPCM) {
565     sample_info = *((guint32 *) info);
566   }
568   str = mpeg_demux->audio_stream[stream_nr];
569   if (str == NULL) {
570     gchar *name;
572     if (type != GST_DVD_DEMUX_AUDIO_LPCM) {
573       str = g_new0 (GstMPEGStream, 1);
574     } else {
575       lpcm_str = g_new0 (GstDVDLPCMStream, 1);
576       str = (GstMPEGStream *) lpcm_str;
577     }
579     name = g_strdup_printf ("audio_%02d", stream_nr);
580     DEMUX_CLASS (dvd_demux)->init_stream (mpeg_demux, type, str, stream_nr,
581         name, DEMUX_CLASS (dvd_demux)->audio_template);
582     /* update caps */
583     str->type = GST_MPEG_DEMUX_AUDIO_UNKNOWN;
584     g_free (name);
585     add_pad = TRUE;
587     mpeg_demux->audio_stream[stream_nr] = str;
588   } else {
589     /* This stream may have been created by a derived class, reset the
590        size. */
591     if (type != GST_DVD_DEMUX_AUDIO_LPCM) {
592       str = g_renew (GstMPEGStream, str, 1);
593     } else {
594       lpcm_str = g_renew (GstDVDLPCMStream, str, 1);
595       str = (GstMPEGStream *) lpcm_str;
596     }
597   }
599   if (type != str->type ||
600       (type == GST_DVD_DEMUX_AUDIO_LPCM &&
601           sample_info != lpcm_str->sample_info)) {
602     gint width, rate, channels, dynamic_range;
603     gboolean emphasis, mute;
605     /* We need to set new caps for this pad. */
606     switch (type) {
607       case GST_DVD_DEMUX_AUDIO_LPCM:
608         /* Dynamic range in the lower byte */
609         dynamic_range = sample_info & 0xff;
611         /* Determine the sample width. */
612         switch (sample_info & 0xC000) {
613           case 0x8000:
614             width = 24;
615             break;
616           case 0x4000:
617             width = 20;
618             break;
619           default:
620             width = 16;
621             break;
622         }
624         /* Determine the rate. */
625         if (sample_info & 0x1000) {
626           rate = 96000;
627         } else {
628           rate = 48000;
629         }
631         mute = ((sample_info & 0x400000) != 0);
632         emphasis = ((sample_info & 0x800000) != 0);
634         /* Determine the number of channels. */
635         channels = ((sample_info >> 8) & 0x7) + 1;
637         caps = gst_caps_new_simple ("audio/x-dvd-lpcm",
638             "width", G_TYPE_INT, width,
639             "rate", G_TYPE_INT, rate,
640             "channels", G_TYPE_INT, channels,
641             "dynamic_range", G_TYPE_INT, dynamic_range,
642             "emphasis", G_TYPE_BOOLEAN, emphasis,
643             "mute", G_TYPE_BOOLEAN, mute, NULL);
645         lpcm_str->sample_info = sample_info;
646         lpcm_str->width = width;
647         lpcm_str->rate = rate;
648         lpcm_str->channels = channels;
649         lpcm_str->dynamic_range = dynamic_range;
650         lpcm_str->mute = mute;
651         lpcm_str->emphasis = emphasis;
653         break;
655       case GST_DVD_DEMUX_AUDIO_AC3:
656         caps = gst_caps_new_simple ("audio/x-ac3", NULL);
657         break;
659       case GST_DVD_DEMUX_AUDIO_DTS:
660         caps = gst_caps_new_simple ("audio/x-dts", NULL);
661         break;
663       default:
664         g_return_val_if_reached (NULL);
665         break;
666     }
668     gst_pad_set_explicit_caps (str->pad, caps);
670     if (str->number == dvd_demux->cur_audio_nr) {
671       /* This is the current audio stream.  Use the same caps. */
672       gst_pad_set_explicit_caps (dvd_demux->cur_audio, gst_caps_copy (caps));
673     }
674     if (add_pad)
675       gst_element_add_pad (GST_ELEMENT (mpeg_demux), str->pad);
677     str->type = type;
678   }
680   return str;
684 static GstMPEGStream *
685 gst_dvd_demux_get_subpicture_stream (GstMPEGDemux * mpeg_demux,
686     guint8 stream_nr, gint type, const gpointer info)
688   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
689   GstMPEGStream *str;
690   gchar *name;
691   GstCaps *caps;
692   gboolean add_pad = FALSE;
694   g_return_val_if_fail (stream_nr < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS, NULL);
695   g_return_val_if_fail (type > GST_DVD_DEMUX_SUBP_UNKNOWN &&
696       type < GST_DVD_DEMUX_SUBP_LAST, NULL);
698   str = dvd_demux->subpicture_stream[stream_nr];
700   if (str == NULL) {
701     str = g_new0 (GstMPEGStream, 1);
703     name = g_strdup_printf ("subpicture_%02d", stream_nr);
704     DEMUX_CLASS (dvd_demux)->init_stream (mpeg_demux, type, str, stream_nr,
705         name, CLASS (dvd_demux)->subpicture_template);
706     str->type = GST_DVD_DEMUX_SUBP_UNKNOWN;
707     g_free (name);
708     add_pad = TRUE;
710     dvd_demux->subpicture_stream[stream_nr] = str;
711   } else {
712     /* This stream may have been created by a derived class, reset the
713        size. */
714     str = g_renew (GstMPEGStream, str, 1);
715   }
717   if (str->type != GST_DVD_DEMUX_SUBP_DVD) {
718     /* We need to set new caps for this pad. */
719     caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
720     gst_pad_set_explicit_caps (str->pad, caps);
722     if (str->number == dvd_demux->cur_subpicture_nr) {
723       /* This is the current subpicture stream.  Use the same caps. */
724       gst_pad_set_explicit_caps (dvd_demux->cur_subpicture, caps);
725     }
727     gst_caps_free (caps);
728     if (add_pad)
729       gst_element_add_pad (GST_ELEMENT (mpeg_demux), str->pad);
730     str->type = GST_DVD_DEMUX_SUBP_DVD;
731   }
733   return str;
736 static void
737 gst_dvd_demux_process_private (GstMPEGDemux * mpeg_demux,
738     GstBuffer * buffer,
739     guint stream_nr, GstClockTime timestamp, guint headerlen, guint datalen)
741   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
742   guint8 *basebuf;
743   guint8 ps_id_code;
744   GstMPEGStream *outstream = NULL;
745   guint first_access = 0;
746   gint align = 1, len, off;
748   basebuf = GST_BUFFER_DATA (buffer);
750   /* Determine the substream number. */
751   ps_id_code = basebuf[headerlen + 4];
753   /* In the following, the "first access" refers to the location in a
754      buffer the time stamp is associated to.  DVDs include this
755      information explicitely. */
756   switch (stream_nr) {
757     case 0:
758       /* Private stream 1. */
760       if (ps_id_code >= 0x80 && ps_id_code <= 0x87) {
761         GST_LOG_OBJECT (dvd_demux,
762             "we have an audio (AC3) packet, track %d", ps_id_code - 0x80);
763         outstream = DEMUX_CLASS (dvd_demux)->get_audio_stream (mpeg_demux,
764             ps_id_code - 0x80, GST_DVD_DEMUX_AUDIO_AC3, NULL);
766         /* Determine the position of the "first access".  This
767            should always be the beginning of an AC3 frame. */
768         first_access = (basebuf[headerlen + 6] << 8) | basebuf[headerlen + 7];
770         headerlen += 4;
771         datalen -= 4;
772       } else if (ps_id_code >= 0x88 && ps_id_code <= 0x8f) {
773         GST_LOG_OBJECT (dvd_demux,
774             "we have an audio (DTS) packet, track %d", ps_id_code - 0x88);
775         outstream = DEMUX_CLASS (dvd_demux)->get_audio_stream (mpeg_demux,
776             ps_id_code - 0x88, GST_DVD_DEMUX_AUDIO_DTS, NULL);
778         /* Determine the position of the "first access".  This
779            should always be the beginning of a DTS frame. */
780         first_access = (basebuf[headerlen + 6] << 8) | basebuf[headerlen + 7];
782         headerlen += 4;
783         datalen -= 4;
784       } else if (ps_id_code >= 0xA0 && ps_id_code <= 0xA7) {
785         GstDVDLPCMStream *lpcm_str;
786         guint32 lpcm_sample_info;
788         GST_LOG_OBJECT (dvd_demux,
789             "we have an audio (LPCM) packet, track %d", ps_id_code - 0xA0);
791         /* Compose the sample info from the LPCM header, masking out the frame_num */
792         lpcm_sample_info =
793             basebuf[headerlen + 10] | (basebuf[headerlen +
794                 9] << 8) | ((basebuf[headerlen + 8] & 0xc0) << 16);
796         outstream = DEMUX_CLASS (dvd_demux)->get_audio_stream (mpeg_demux,
797             ps_id_code - 0xA0, GST_DVD_DEMUX_AUDIO_LPCM, &lpcm_sample_info);
798         lpcm_str = (GstDVDLPCMStream *) outstream;
800         /* Determine the position of the "first access". */
801         first_access = (basebuf[headerlen + 6] << 8) | basebuf[headerlen + 7];
803         /* Get rid of the LPCM header. */
804         headerlen += 7;
805         datalen -= 7;
807         /* align by frame round up to nearest byte */
808         align = (lpcm_str->width * lpcm_str->channels + 7) / 8;
809       } else if (ps_id_code >= 0x20 && ps_id_code <= 0x3F) {
810         GST_LOG_OBJECT (dvd_demux,
811             "we have a subpicture packet, track %d", ps_id_code - 0x20);
812         outstream = CLASS (dvd_demux)->get_subpicture_stream (mpeg_demux,
813             ps_id_code - 0x20, GST_DVD_DEMUX_SUBP_DVD, NULL);
815         headerlen += 1;
816         datalen -= 1;
817       } else {
818         GST_WARNING_OBJECT (dvd_demux,
819             "unknown DVD (private 1) id 0x%02x", ps_id_code);
820       }
821       break;
823     case 1:
824       /* Private stream 2 */
826       switch (ps_id_code) {
827         case 0:
828           GST_LOG_OBJECT (dvd_demux, "we have a PCI nav packet");
830           outstream = DEMUX_CLASS (mpeg_demux)->get_private_stream (mpeg_demux,
831               1, GST_MPEG_DEMUX_PRIVATE_UNKNOWN, NULL);
832           break;
834         case 1:
835           GST_LOG_OBJECT (dvd_demux, "we have a DSI nav packet");
837           outstream = DEMUX_CLASS (mpeg_demux)->get_private_stream (mpeg_demux,
838               1, GST_MPEG_DEMUX_PRIVATE_UNKNOWN, NULL);
839           break;
841         default:
842           GST_WARNING_OBJECT (dvd_demux,
843               "unknown DVD (private 2) id 0x%02x", ps_id_code);
844           break;
845       }
846       break;
848     default:
849       g_return_if_reached ();
850       break;
851   }
853   if (outstream == NULL) {
854     return;
855   }
857   if (timestamp != GST_CLOCK_TIME_NONE && first_access > 1) {
858     /* We have a first access location.  Since GStreamer doesn't have
859        a means to associate a timestamp to the middle of a buffer, we
860        send two separate buffers and put the timestamp in the second
861        one. */
862     off = headerlen + 4;
863     len = first_access - 1;
864     len -= len % align;
865     if (len > 0) {
866       DEMUX_CLASS (dvd_demux)->send_subbuffer (mpeg_demux, outstream,
867           buffer, GST_CLOCK_TIME_NONE, off, len);
868     }
869     off += len;
870     len = datalen - len;
871     len -= len % align;
872     if (len > 0) {
873       DEMUX_CLASS (dvd_demux)->send_subbuffer (mpeg_demux, outstream,
874           buffer, timestamp, off, len);
875     }
876   } else {
877     off = headerlen + 4;
878     len = datalen;
879     len -= len % align;
880     if (len > 0) {
881       DEMUX_CLASS (dvd_demux)->send_subbuffer (mpeg_demux, outstream,
882           buffer, timestamp, off, len);
883     }
884   }
888 static void
889 gst_dvd_demux_send_subbuffer (GstMPEGDemux * mpeg_demux,
890     GstMPEGStream * outstream, GstBuffer * buffer,
891     GstClockTime timestamp, guint offset, guint size)
893   GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (mpeg_demux);
894   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
895   GstPad *outpad;
896   gint cur_nr;
898   /* If there's a pending discontinuity, send it now. The idea is to
899      minimize the time interval between the discontinuity and the data
900      buffers following it. */
901   if (dvd_demux->discont_time != GST_CLOCK_TIME_NONE) {
902     if ((gint64) (dvd_demux->discont_time) < 0) {
903       GST_ERROR ("DVD Discont < 0! % " G_GINT64_FORMAT,
904           (gint64) dvd_demux->discont_time);
905     }
906     PARSE_CLASS (mpeg_demux)->send_discont (mpeg_parse,
907         dvd_demux->discont_time);
908     dvd_demux->discont_time = GST_CLOCK_TIME_NONE;
909   }
911   /* You never know what happens to a buffer when you send it.  Just
912      in case, we keep a reference to the buffer during the execution
913      of this function. */
914   gst_buffer_ref (buffer);
916   /* Send the buffer to the standard output pad. */
917   parent_class->send_subbuffer (mpeg_demux, outstream, buffer,
918       timestamp, offset, size);
920   /* Determine the current output pad and stream number for the given
921      type of stream. */
922   switch (GST_MPEG_DEMUX_STREAM_KIND (outstream->type)) {
923     case GST_MPEG_DEMUX_STREAM_VIDEO:
924       outpad = dvd_demux->cur_video;
925       cur_nr = dvd_demux->cur_video_nr;
926       break;
927     case GST_MPEG_DEMUX_STREAM_AUDIO:
928       outpad = dvd_demux->cur_audio;
929       cur_nr = dvd_demux->cur_audio_nr;
930       break;
931     case GST_MPEG_DEMUX_STREAM_PRIVATE:
932       outpad = NULL;
933       cur_nr = 0;
934       break;
935     case GST_DVD_DEMUX_STREAM_SUBPICTURE:
936       outpad = dvd_demux->cur_subpicture;
937       cur_nr = dvd_demux->cur_subpicture_nr;
938       break;
939     default:
940       g_return_if_reached ();
941       break;
942   }
944   if ((outpad != NULL) && (cur_nr == outstream->number) && (size > 0)) {
945     GstBuffer *outbuf;
947     /* We have a packet of the current stream. Send it to the
948        corresponding pad as well. */
949     outbuf = gst_buffer_create_sub (buffer, offset, size);
951     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
952     GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer) + offset;
954     gst_pad_push (outpad, GST_DATA (outbuf));
955   }
957   gst_buffer_unref (buffer);
961 static void
962 gst_dvd_demux_set_cur_audio (GstDVDDemux * dvd_demux, gint stream_nr)
964   GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (dvd_demux);
965   GstMPEGStream *str;
966   const GstCaps *caps;
968   g_return_if_fail (stream_nr >= -1 &&
969       stream_nr < GST_MPEG_DEMUX_NUM_AUDIO_STREAMS);
971   GST_DEBUG_OBJECT (dvd_demux, "changing current audio to %02d", stream_nr);
973   dvd_demux->cur_audio_nr = stream_nr;
975   if (stream_nr == -1) {
976     return;
977   }
979   str = mpeg_demux->audio_stream[stream_nr];
980   if (str != NULL) {
981     /* (Re)set the caps in the "current" pad. */
982     caps = GST_RPAD_EXPLICIT_CAPS (str->pad);
983     if (caps != NULL) {
984       gst_pad_set_explicit_caps (dvd_demux->cur_audio, caps);
985     }
986   }
990 static void
991 gst_dvd_demux_set_cur_subpicture (GstDVDDemux * dvd_demux, gint stream_nr)
993   GstMPEGStream *str;
995   g_return_if_fail (stream_nr >= -1 &&
996       stream_nr < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS);
998   GST_DEBUG_OBJECT (dvd_demux, "changing current subpicture to %02d",
999       stream_nr);
1001   dvd_demux->cur_subpicture_nr = stream_nr;
1003   if (stream_nr == -1) {
1004     return;
1005   }
1007   str = dvd_demux->subpicture_stream[stream_nr];
1008   if (str != NULL) {
1009     GstCaps *caps = NULL;
1011     /* (Re)set the caps in the "current" pad. */
1012     caps = GST_RPAD_EXPLICIT_CAPS (str->pad);
1013     gst_pad_set_explicit_caps (dvd_demux->cur_subpicture, caps);
1014   }
1017 static void
1018 gst_dvd_demux_reset (GstDVDDemux * dvd_demux)
1020   int i;
1022   GST_INFO ("Resetting the dvd demuxer");
1023   for (i = 0; i < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS; i++) {
1024     if (dvd_demux->subpicture_stream[i]) {
1025       gst_element_remove_pad (GST_ELEMENT (dvd_demux),
1026           dvd_demux->subpicture_stream[i]->pad);
1027       g_free (dvd_demux->subpicture_stream[i]);
1028       dvd_demux->subpicture_stream[i] = NULL;
1029     }
1030     dvd_demux->subpicture_time[i] = 0;
1031   }
1032   gst_pad_set_explicit_caps (dvd_demux->cur_video, NULL);
1033   gst_pad_set_explicit_caps (dvd_demux->cur_audio, NULL);
1034   gst_pad_set_explicit_caps (dvd_demux->cur_subpicture, NULL);
1036   dvd_demux->cur_video_nr = 0;
1037   dvd_demux->cur_audio_nr = 0;
1038   dvd_demux->cur_subpicture_nr = 0;
1039   dvd_demux->mpeg_version = 0;
1040   dvd_demux->last_end_ptm = INITIAL_END_PTM;
1042   dvd_demux->just_flushed = FALSE;
1043   dvd_demux->discont_time = GST_CLOCK_TIME_NONE;
1046 static GstElementStateReturn
1047 gst_dvd_demux_change_state (GstElement * element)
1049   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (element);
1051   switch (GST_STATE_TRANSITION (element)) {
1052     case GST_STATE_PAUSED_TO_READY:
1053       gst_dvd_demux_reset (dvd_demux);
1054       break;
1055     default:
1056       break;
1057   }
1059   return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1062 gboolean
1063 gst_dvd_demux_plugin_init (GstPlugin * plugin)
1065   return gst_element_register (plugin, "dvddemux",
1066       GST_RANK_PRIMARY, GST_TYPE_DVD_DEMUX);