Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / gst / nuvdemux / gstnuvdemux.c
1 /* GStreamer NUV demuxer
2  * Copyright (C) <2006> Renato Araujo Oliveira Filho <renato.filho@indt.org.br>
3  *                      Rosfran Borges <rosfran.borges@indt.org.br>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 /* Element-Checklist-Version: 5 */
21
22 /**
23  * SECTION:element-nuvdemux
24  * @see_also: mythtvsrc
25  *
26  * Demuxes MythTVs NuppelVideo .nuv file into raw or compressed audio and/or
27  * video streams.
28  * 
29  * This element currently only supports pull-based scheduling.
30  * 
31  * <refsect2>
32  * <title>Example launch line</title>
33  * |[
34  * gst-launch filesrc test.nuv ! nuvdemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
35  * ]| Play (parse and decode) an .nuv file and try to output it to
36  * an automatically detected soundcard and videosink. If the NUV file contains
37  * compressed audio or video data, this will only work if you have the
38  * right decoder elements/plugins installed.
39  * </refsect2>
40  *
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include <gst/gst.h>
48 #include <gst/gsterror.h>
49 #include <gst/gstplugin.h>
50 #include <string.h>
51
52 #include "gst/gst-i18n-plugin.h"
53 #include "gstnuvdemux.h"
54
55 GST_DEBUG_CATEGORY_STATIC (nuvdemux_debug);
56 #define GST_CAT_DEFAULT nuvdemux_debug
57
58
59 #define GST_FLOW_ERROR_NO_DATA  -101
60
61 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);
62
63 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
64     GST_PAD_SINK,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS ("video/x-nuv"));
67
68 static GstStaticPadTemplate audio_src_template =
69 GST_STATIC_PAD_TEMPLATE ("audio_src",
70     GST_PAD_SRC,
71     GST_PAD_SOMETIMES,
72     GST_STATIC_CAPS_ANY);
73
74 static GstStaticPadTemplate video_src_template =
75 GST_STATIC_PAD_TEMPLATE ("video_src",
76     GST_PAD_SRC,
77     GST_PAD_SOMETIMES,
78     GST_STATIC_CAPS_ANY);
79
80 /* NUV Demux indexes init/dispose callers */
81 static void gst_nuv_demux_finalize (GObject * object);
82 static GstStateChangeReturn gst_nuv_demux_change_state (GstElement * element,
83     GstStateChange transition);
84 static void gst_nuv_demux_loop (GstPad * pad);
85 static GstFlowReturn gst_nuv_demux_chain (GstPad * pad, GstBuffer * buf);
86 static GstFlowReturn gst_nuv_demux_play (GstPad * pad);
87 static gboolean gst_nuv_demux_sink_activate_pull (GstPad * sinkpad,
88     gboolean active);
89 static gboolean gst_nuv_demux_sink_activate (GstPad * sinkpad);
90 static GstFlowReturn gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size,
91     gboolean move, GstBuffer ** buffer);
92 static void gst_nuv_demux_reset (GstNuvDemux * nuv);
93 static gboolean gst_nuv_demux_handle_sink_event (GstPad * sinkpad,
94     GstEvent * event);
95 static void gst_nuv_demux_destoy_src_pad (GstNuvDemux * nuv);
96 static void gst_nuv_demux_send_eos (GstNuvDemux * nuv);
97
98 /* GObject methods */
99 GST_BOILERPLATE (GstNuvDemux, gst_nuv_demux, GstElement, GST_TYPE_ELEMENT);
100
101 #if G_BYTE_ORDER == G_BIG_ENDIAN
102 static inline gdouble
103 _gdouble_swap_le_be (gdouble * d)
104 {
105   union
106   {
107     guint64 i;
108     gdouble d;
109   } u;
110
111   u.d = *d;
112   u.i = GUINT64_SWAP_LE_BE (u.i);
113   return u.d;
114 }
115
116 #define READ_DOUBLE_FROM_LE(d) (_gdouble_swap_le_be((gdouble* ) d))
117 #else /* G_BYTE_ORDER != G_BIG_ENDIAN */
118 #define READ_DOUBLE_FROM_LE(d) *((gdouble* ) (d))
119 #endif /* G_BYTE_ORDER != G_BIG_ENDIAN */
120
121
122 static void
123 gst_nuv_demux_base_init (gpointer klass)
124 {
125   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
126
127   gst_element_class_add_static_pad_template (element_class,
128       &audio_src_template);
129
130   gst_element_class_add_static_pad_template (element_class,
131       &video_src_template);
132
133   gst_element_class_add_static_pad_template (element_class,
134       &sink_template);
135   gst_element_class_set_details_simple (element_class, "Nuv demuxer",
136       "Codec/Demuxer",
137       "Demultiplex a MythTV NuppleVideo .nuv file into audio and video",
138       "Renato Araujo Oliveira Filho <renato.filho@indt.org.br>,"
139       "Rosfran Borges <rosfran.borges@indt.org.br>");
140 }
141
142 static void
143 gst_nuv_demux_class_init (GstNuvDemuxClass * klass)
144 {
145   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
146   GObjectClass *gobject_class = (GObjectClass *) klass;
147
148   gobject_class->finalize = gst_nuv_demux_finalize;
149
150   gstelement_class->change_state =
151       GST_DEBUG_FUNCPTR (gst_nuv_demux_change_state);
152 }
153
154 static void
155 gst_nuv_demux_init (GstNuvDemux * nuv, GstNuvDemuxClass * nuv_class)
156 {
157   nuv->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
158
159   gst_pad_set_activate_function (nuv->sinkpad, gst_nuv_demux_sink_activate);
160
161   gst_pad_set_activatepull_function (nuv->sinkpad,
162       gst_nuv_demux_sink_activate_pull);
163
164   gst_pad_set_chain_function (nuv->sinkpad,
165       GST_DEBUG_FUNCPTR (gst_nuv_demux_chain));
166
167   gst_pad_set_event_function (nuv->sinkpad, gst_nuv_demux_handle_sink_event);
168
169   gst_element_add_pad (GST_ELEMENT (nuv), nuv->sinkpad);
170
171   nuv->adapter = NULL;
172   nuv->mpeg_buffer = NULL;
173   nuv->h = NULL;
174   nuv->eh = NULL;
175   nuv->fh = NULL;
176   gst_nuv_demux_reset (nuv);
177 }
178
179 static void
180 gst_nuv_demux_finalize (GObject * object)
181 {
182   GstNuvDemux *nuv = GST_NUV_DEMUX (object);
183
184   if (nuv->mpeg_buffer != NULL) {
185     gst_buffer_unref (nuv->mpeg_buffer);
186   }
187
188   gst_nuv_demux_destoy_src_pad (nuv);
189   gst_nuv_demux_reset (nuv);
190   if (nuv->adapter != NULL) {
191     g_object_unref (nuv->adapter);
192     nuv->adapter = NULL;
193   }
194   G_OBJECT_CLASS (parent_class)->finalize (object);
195 }
196
197 /*****************************************************************************
198  * Utils functions
199  *****************************************************************************/
200
201 static gboolean
202 gst_nuv_demux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
203 {
204   gboolean res = FALSE;
205
206   switch (GST_EVENT_TYPE (event)) {
207     case GST_EVENT_NEWSEGMENT:
208       res = TRUE;
209       break;
210     default:
211       return gst_pad_event_default (sinkpad, event);
212       break;
213   }
214
215   gst_event_unref (event);
216   return res;
217 }
218
219
220 /* HeaderLoad:
221  */
222 static GstFlowReturn
223 gst_nuv_demux_header_load (GstNuvDemux * nuv, nuv_header ** h_ret)
224 {
225   GstBuffer *buffer = NULL;
226   GstFlowReturn res;
227   nuv_header *h;
228
229   res = gst_nuv_demux_read_bytes (nuv, 72, TRUE, &buffer);
230   if (res != GST_FLOW_OK)
231     return res;
232
233   h = g_new0 (nuv_header, 1);
234
235   memcpy (h->id, buffer->data, 12);
236   memcpy (h->version, buffer->data + 12, 5);
237   h->i_width = GST_READ_UINT32_LE (&buffer->data[20]);
238   h->i_height = GST_READ_UINT32_LE (&buffer->data[24]);
239   h->i_width_desired = GST_READ_UINT32_LE (&buffer->data[28]);
240   h->i_height_desired = GST_READ_UINT32_LE (&buffer->data[32]);
241   h->i_mode = buffer->data[36];
242   h->d_aspect = READ_DOUBLE_FROM_LE (&buffer->data[40]);
243   h->d_fps = READ_DOUBLE_FROM_LE (&buffer->data[48]);
244   h->i_video_blocks = GST_READ_UINT32_LE (&buffer->data[56]);
245   h->i_audio_blocks = GST_READ_UINT32_LE (&buffer->data[60]);
246   h->i_text_blocks = GST_READ_UINT32_LE (&buffer->data[64]);
247   h->i_keyframe_distance = GST_READ_UINT32_LE (&buffer->data[68]);
248
249   GST_DEBUG_OBJECT (nuv,
250       "nuv: h=%s v=%s %dx%d a=%f fps=%f v=%d a=%d t=%d kfd=%d", h->id,
251       h->version, h->i_width, h->i_height, h->d_aspect, h->d_fps,
252       h->i_video_blocks, h->i_audio_blocks, h->i_text_blocks,
253       h->i_keyframe_distance);
254
255   *h_ret = h;
256   gst_buffer_unref (buffer);
257   return res;
258 }
259
260 static GstFlowReturn
261 gst_nuv_demux_stream_header_data (GstNuvDemux * nuv)
262 {
263   GstFlowReturn res = gst_nuv_demux_header_load (nuv, &nuv->h);
264
265   if (res == GST_FLOW_OK)
266     nuv->state = GST_NUV_DEMUX_EXTRA_DATA;
267   return res;
268 }
269
270 /*
271  * Read NUV file tag
272  */
273 static GstFlowReturn
274 gst_nuv_demux_stream_file_header (GstNuvDemux * nuv)
275 {
276   GstFlowReturn res = GST_FLOW_OK;
277   GstBuffer *file_header = NULL;
278
279   res = gst_nuv_demux_read_bytes (nuv, 12, FALSE, &file_header);
280   if (res != GST_FLOW_OK) {
281     return res;
282   } else {
283     if (strncmp ((gchar *) file_header->data, "MythTVVideo", 11) ||
284         strncmp ((gchar *) file_header->data, "NuppelVideo", 11)) {
285       nuv->state = GST_NUV_DEMUX_HEADER_DATA;
286     } else {
287       GST_DEBUG_OBJECT (nuv, "error parsing file header");
288       nuv->state = GST_NUV_DEMUX_INVALID_DATA;
289       res = GST_FLOW_ERROR;
290     }
291   }
292   if (file_header != NULL) {
293     gst_buffer_unref (file_header);
294   }
295   return res;
296 }
297
298 /* FrameHeaderLoad:
299  */
300 static GstFlowReturn
301 gst_nuv_demux_frame_header_load (GstNuvDemux * nuv, nuv_frame_header ** h_ret)
302 {
303   unsigned char *data;
304   nuv_frame_header *h;
305   GstBuffer *buf = NULL;
306
307   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 12, TRUE, &buf);
308
309   if (res != GST_FLOW_OK) {
310     if (buf != NULL) {
311       gst_buffer_unref (buf);
312     }
313     return res;
314   }
315
316   h = g_new0 (nuv_frame_header, 1);
317   data = buf->data;
318
319   h->i_type = data[0];
320   h->i_compression = data[1];
321   h->i_keyframe = data[2];
322   h->i_filters = data[3];
323
324   h->i_timecode = GST_READ_UINT32_LE (&data[4]);
325   h->i_length = GST_READ_UINT32_LE (&data[8]);
326   GST_DEBUG_OBJECT (nuv, "frame hdr: t=%c c=%c k=%d f=0x%x timecode=%d l=%d",
327       h->i_type,
328       h->i_compression ? h->i_compression : ' ',
329       h->i_keyframe ? h->i_keyframe : ' ',
330       h->i_filters, h->i_timecode, h->i_length);
331
332   *h_ret = h;
333   gst_buffer_unref (buf);
334   return GST_FLOW_OK;
335 }
336
337 static GstFlowReturn
338 gst_nuv_demux_extended_header_load (GstNuvDemux * nuv,
339     nuv_extended_header ** h_ret)
340 {
341   unsigned char *data;
342   GstBuffer *buff = NULL;
343   nuv_extended_header *h;
344
345
346   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 512, TRUE, &buff);
347
348   if (res != GST_FLOW_OK) {
349     if (buff != NULL) {
350       gst_buffer_unref (buff);
351     }
352     return res;
353   }
354
355   h = g_new0 (nuv_extended_header, 1);
356   data = buff->data;
357   h->i_version = GST_READ_UINT32_LE (&data[0]);
358   h->i_video_fcc = GST_MAKE_FOURCC (data[4], data[5], data[6], data[7]);
359   h->i_audio_fcc = GST_MAKE_FOURCC (data[8], data[9], data[10], data[11]);
360   h->i_audio_sample_rate = GST_READ_UINT32_LE (&data[12]);
361   h->i_audio_bits_per_sample = GST_READ_UINT32_LE (&data[16]);
362   h->i_audio_channels = GST_READ_UINT32_LE (&data[20]);
363   h->i_audio_compression_ratio = GST_READ_UINT32_LE (&data[24]);
364   h->i_audio_quality = GST_READ_UINT32_LE (&data[28]);
365   h->i_rtjpeg_quality = GST_READ_UINT32_LE (&data[32]);
366   h->i_rtjpeg_luma_filter = GST_READ_UINT32_LE (&data[36]);
367   h->i_rtjpeg_chroma_filter = GST_READ_UINT32_LE (&data[40]);
368   h->i_lavc_bitrate = GST_READ_UINT32_LE (&data[44]);
369   h->i_lavc_qmin = GST_READ_UINT32_LE (&data[48]);
370   h->i_lavc_qmin = GST_READ_UINT32_LE (&data[52]);
371   h->i_lavc_maxqdiff = GST_READ_UINT32_LE (&data[56]);
372   h->i_seekable_offset = GST_READ_UINT64_LE (&data[60]);
373   h->i_keyframe_adjust_offset = GST_READ_UINT64_LE (&data[68]);
374
375   GST_DEBUG_OBJECT (nuv,
376       "ex hdr: v=%d vffc=%4.4s afcc=%4.4s %dHz %dbits ach=%d acr=%d aq=%d"
377       "rtjpeg q=%d lf=%d lc=%d lavc br=%d qmin=%d qmax=%d maxqdiff=%d seekableoff=%"
378       G_GINT64_FORMAT " keyfao=%" G_GINT64_FORMAT, h->i_version,
379       (gchar *) & h->i_video_fcc, (gchar *) & h->i_audio_fcc,
380       h->i_audio_sample_rate, h->i_audio_bits_per_sample, h->i_audio_channels,
381       h->i_audio_compression_ratio, h->i_audio_quality, h->i_rtjpeg_quality,
382       h->i_rtjpeg_luma_filter, h->i_rtjpeg_chroma_filter, h->i_lavc_bitrate,
383       h->i_lavc_qmin, h->i_lavc_qmax, h->i_lavc_maxqdiff, h->i_seekable_offset,
384       h->i_keyframe_adjust_offset);
385
386   *h_ret = h;
387   gst_buffer_unref (buff);
388   return res;
389 }
390
391 static gboolean
392 gst_nuv_demux_handle_src_event (GstPad * pad, GstEvent * event)
393 {
394   gst_event_unref (event);
395   return FALSE;
396 }
397
398
399 static void
400 gst_nuv_demux_create_pads (GstNuvDemux * nuv)
401 {
402   if (nuv->h->i_video_blocks != 0) {
403     GstCaps *video_caps = NULL;
404
405     nuv->src_video_pad =
406         gst_pad_new_from_static_template (&video_src_template, "video_src");
407
408     video_caps = gst_caps_new_simple ("video/x-divx",
409         "divxversion", G_TYPE_INT, 4,
410         "width", G_TYPE_INT, nuv->h->i_width,
411         "height", G_TYPE_INT, nuv->h->i_height,
412         "framerate", GST_TYPE_FRACTION, (gint) (nuv->h->d_fps * 1000.0f), 1000,
413         "pixel-aspect-ratio", GST_TYPE_FRACTION,
414         (gint) (nuv->h->d_aspect * 1000.0f), 1000, NULL);
415
416     gst_pad_use_fixed_caps (nuv->src_video_pad);
417     gst_pad_set_active (nuv->src_video_pad, TRUE);
418     gst_pad_set_caps (nuv->src_video_pad, video_caps);
419
420     gst_pad_set_event_function (nuv->src_video_pad,
421         gst_nuv_demux_handle_src_event);
422     gst_pad_set_active (nuv->src_video_pad, TRUE);
423     gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_video_pad);
424
425     gst_caps_unref (video_caps);
426   }
427
428   if (nuv->h->i_audio_blocks != 0) {
429     GstCaps *audio_caps = NULL;
430
431     nuv->src_audio_pad =
432         gst_pad_new_from_static_template (&audio_src_template, "audio_src");
433
434     audio_caps = gst_caps_new_simple ("audio/mpeg",
435         "rate", G_TYPE_INT, nuv->eh->i_audio_sample_rate,
436         "format", GST_TYPE_FOURCC, nuv->eh->i_audio_fcc,
437         "channels", G_TYPE_INT, nuv->eh->i_audio_channels,
438         "mpegversion", G_TYPE_INT, nuv->eh->i_version, NULL);
439
440     gst_pad_use_fixed_caps (nuv->src_audio_pad);
441     gst_pad_set_active (nuv->src_audio_pad, TRUE);
442     gst_pad_set_caps (nuv->src_audio_pad, audio_caps);
443     gst_pad_set_active (nuv->src_audio_pad, TRUE);
444     gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_audio_pad);
445
446     gst_pad_set_event_function (nuv->src_audio_pad,
447         gst_nuv_demux_handle_src_event);
448
449     gst_caps_unref (audio_caps);
450   }
451
452   gst_element_no_more_pads (GST_ELEMENT (nuv));
453 }
454
455 static GstFlowReturn
456 gst_nuv_demux_read_head_frame (GstNuvDemux * nuv)
457 {
458   GstFlowReturn ret = GST_FLOW_OK;
459
460   ret = gst_nuv_demux_frame_header_load (nuv, &nuv->fh);
461   if (ret != GST_FLOW_OK)
462     return ret;
463
464   nuv->state = GST_NUV_DEMUX_MOVI;
465   return ret;
466 }
467
468 static GstFlowReturn
469 gst_nuv_demux_stream_data (GstNuvDemux * nuv)
470 {
471   GstFlowReturn ret = GST_FLOW_OK;
472   GstBuffer *buf = NULL;
473   nuv_frame_header *h = nuv->fh;
474
475   if (h->i_type == 'R')
476     goto done;
477
478   if (h->i_length > 0) {
479     ret = gst_nuv_demux_read_bytes (nuv, h->i_length, TRUE, &buf);
480     if (ret != GST_FLOW_OK)
481       return ret;
482
483     if (h->i_timecode > 0)
484       GST_BUFFER_TIMESTAMP (buf) = h->i_timecode * GST_MSECOND;
485   }
486
487
488   switch (h->i_type) {
489     case 'V':
490     {
491       if (!buf)
492         break;
493
494       GST_BUFFER_OFFSET (buf) = nuv->video_offset;
495       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_video_pad));
496       ret = gst_pad_push (nuv->src_video_pad, buf);
497       nuv->video_offset++;
498       break;
499     }
500     case 'A':
501     {
502       if (!buf)
503         break;
504
505       GST_BUFFER_OFFSET (buf) = nuv->audio_offset;
506       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_audio_pad));
507       ret = gst_pad_push (nuv->src_audio_pad, buf);
508       nuv->audio_offset++;
509       break;
510     }
511     case 'S':
512     {
513       switch (h->i_compression) {
514         case 'V':
515           gst_pad_push_event (nuv->src_video_pad,
516               gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1,
517                   h->i_timecode));
518           break;
519         case 'A':
520           gst_pad_push_event (nuv->src_audio_pad,
521               gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
522           break;
523         default:
524           break;
525       }
526     }
527     default:
528       if (buf != NULL)
529         gst_buffer_unref (buf);
530
531       break;
532   }
533
534 done:
535   nuv->state = GST_NUV_DEMUX_FRAME_HEADER;
536   g_free (nuv->fh);
537   nuv->fh = NULL;
538   return ret;
539 }
540
541 static GstFlowReturn
542 gst_nuv_demux_stream_mpeg_data (GstNuvDemux * nuv)
543 {
544   GstFlowReturn ret = GST_FLOW_OK;
545
546   /* ffmpeg extra data */
547   ret =
548       gst_nuv_demux_read_bytes (nuv, nuv->mpeg_data_size, TRUE,
549       &nuv->mpeg_buffer);
550   if (ret != GST_FLOW_OK) {
551     return GST_FLOW_ERROR;
552   }
553   GST_BUFFER_SIZE (nuv->mpeg_buffer) = nuv->mpeg_data_size;
554   nuv->state = GST_NUV_DEMUX_EXTEND_HEADER;
555   return ret;
556 }
557
558 static GstFlowReturn
559 gst_nuv_demux_stream_extra_data (GstNuvDemux * nuv)
560 {
561   GstFlowReturn ret = GST_FLOW_OK;
562
563   /* Load 'D' */
564   nuv_frame_header *h;
565
566   ret = gst_nuv_demux_frame_header_load (nuv, &h);
567   if (ret != GST_FLOW_OK)
568     return ret;
569
570   if (h->i_type != 'D') {
571     g_free (h);
572     return GST_FLOW_ERROR;
573   }
574
575   if (h->i_length > 0) {
576     if (h->i_compression == 'F') {
577       nuv->state = GST_NUV_DEMUX_MPEG_DATA;
578     } else {
579       g_free (h);
580       return GST_FLOW_ERROR;
581     }
582   } else {
583     nuv->state = GST_NUV_DEMUX_EXTEND_HEADER;
584   }
585
586   g_free (h);
587   h = NULL;
588   return ret;
589 }
590
591 static GstFlowReturn
592 gst_nuv_demux_stream_extend_header_data (GstNuvDemux * nuv)
593 {
594   GstFlowReturn ret = GST_FLOW_OK;
595
596   ret = gst_nuv_demux_extended_header_load (nuv, &nuv->eh);
597   if (ret != GST_FLOW_OK)
598     return ret;
599
600   gst_nuv_demux_create_pads (nuv);
601   nuv->state = GST_NUV_DEMUX_FRAME_HEADER;
602   return ret;
603 }
604
605 static GstFlowReturn
606 gst_nuv_demux_stream_extend_header (GstNuvDemux * nuv)
607 {
608   GstBuffer *buf = NULL;
609   GstFlowReturn res = GST_FLOW_OK;
610
611   res = gst_nuv_demux_read_bytes (nuv, 1, FALSE, &buf);
612   if (res != GST_FLOW_OK) {
613     if (buf != NULL) {
614       gst_buffer_unref (buf);
615     }
616     return res;
617   }
618
619   if (buf->data[0] == 'X') {
620     nuv_frame_header *h = NULL;
621
622     gst_buffer_unref (buf);
623     buf = NULL;
624
625     res = gst_nuv_demux_frame_header_load (nuv, &h);
626     if (res != GST_FLOW_OK)
627       return res;
628
629     if (h->i_length != 512) {
630       g_free (h);
631       return GST_FLOW_ERROR;
632     }
633     g_free (h);
634     h = NULL;
635     nuv->state = GST_NUV_DEMUX_EXTEND_HEADER_DATA;
636   } else {
637     nuv->state = GST_NUV_DEMUX_INVALID_DATA;
638     GST_ELEMENT_ERROR (nuv, STREAM, DEMUX, (NULL),
639         ("Unsupported extended header (0x%02x)", buf->data[0]));
640     gst_buffer_unref (buf);
641     return GST_FLOW_ERROR;
642   }
643   return res;
644 }
645
646 static GstFlowReturn
647 gst_nuv_demux_play (GstPad * pad)
648 {
649   GstFlowReturn res = GST_FLOW_OK;
650   GstNuvDemux *nuv = GST_NUV_DEMUX (GST_PAD_PARENT (pad));
651
652   switch (nuv->state) {
653     case GST_NUV_DEMUX_START:
654       res = gst_nuv_demux_stream_file_header (nuv);
655       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
656         goto pause;
657       }
658       if (nuv->state != GST_NUV_DEMUX_HEADER_DATA)
659         break;
660
661     case GST_NUV_DEMUX_HEADER_DATA:
662       res = gst_nuv_demux_stream_header_data (nuv);
663       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
664         goto pause;
665       }
666       if (nuv->state != GST_NUV_DEMUX_EXTRA_DATA)
667         break;
668
669     case GST_NUV_DEMUX_EXTRA_DATA:
670       res = gst_nuv_demux_stream_extra_data (nuv);
671       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
672         goto pause;
673       }
674       if (nuv->state != GST_NUV_DEMUX_MPEG_DATA)
675         break;
676
677     case GST_NUV_DEMUX_MPEG_DATA:
678       res = gst_nuv_demux_stream_mpeg_data (nuv);
679       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
680         goto pause;
681       }
682
683       if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER)
684         break;
685
686     case GST_NUV_DEMUX_EXTEND_HEADER:
687       res = gst_nuv_demux_stream_extend_header (nuv);
688       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
689         goto pause;
690       }
691       if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER_DATA)
692         break;
693
694     case GST_NUV_DEMUX_EXTEND_HEADER_DATA:
695       res = gst_nuv_demux_stream_extend_header_data (nuv);
696       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
697         goto pause;
698       }
699
700       if (nuv->state != GST_NUV_DEMUX_FRAME_HEADER)
701         break;
702
703     case GST_NUV_DEMUX_FRAME_HEADER:
704       res = gst_nuv_demux_read_head_frame (nuv);
705       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
706         goto pause;
707       }
708       if (nuv->state != GST_NUV_DEMUX_MOVI)
709         break;
710
711     case GST_NUV_DEMUX_MOVI:
712       res = gst_nuv_demux_stream_data (nuv);
713       if ((res != GST_FLOW_OK) && (res != GST_FLOW_CUSTOM_ERROR)) {
714         goto pause;
715       }
716       break;
717     case GST_NUV_DEMUX_INVALID_DATA:
718       goto pause;
719       break;
720     default:
721       g_assert_not_reached ();
722   }
723
724   GST_DEBUG_OBJECT (nuv, "state: %d res:%s", nuv->state,
725       gst_flow_get_name (res));
726
727   return GST_FLOW_OK;
728
729 pause:
730   GST_LOG_OBJECT (nuv, "pausing task, reason %s", gst_flow_get_name (res));
731   gst_pad_pause_task (nuv->sinkpad);
732   if (res == GST_FLOW_UNEXPECTED) {
733     gst_nuv_demux_send_eos (nuv);
734   } else if (res == GST_FLOW_NOT_LINKED || res < GST_FLOW_UNEXPECTED) {
735     GST_ELEMENT_ERROR (nuv, STREAM, FAILED,
736         (_("Internal data stream error.")),
737         ("streaming stopped, reason %s", gst_flow_get_name (res)));
738
739     gst_nuv_demux_send_eos (nuv);
740   }
741   return res;
742 }
743
744 static void
745 gst_nuv_demux_send_eos (GstNuvDemux * nuv)
746 {
747   gst_element_post_message (GST_ELEMENT (nuv),
748       gst_message_new_segment_done (GST_OBJECT (nuv), GST_FORMAT_TIME, -1));
749
750   if (nuv->src_video_pad)
751     gst_pad_push_event (nuv->src_video_pad, gst_event_new_eos ());
752   if (nuv->src_audio_pad)
753     gst_pad_push_event (nuv->src_audio_pad, gst_event_new_eos ());
754 }
755
756 static GstFlowReturn
757 gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size, gboolean move,
758     GstBuffer ** buffer)
759 {
760   GstFlowReturn ret = GST_FLOW_OK;
761
762   if (size == 0) {
763     *buffer = gst_buffer_new ();
764     return ret;
765   }
766
767   if (nuv->mode == 0) {
768     ret = gst_pad_pull_range (nuv->sinkpad, nuv->offset, size, buffer);
769     if (ret == GST_FLOW_OK) {
770       if (move) {
771         nuv->offset += size;
772       }
773       /* got eos */
774     } else if (ret == GST_FLOW_UNEXPECTED) {
775       gst_nuv_demux_send_eos (nuv);
776       return GST_FLOW_WRONG_STATE;
777     }
778   } else {
779     if (gst_adapter_available (nuv->adapter) < size)
780       return GST_FLOW_ERROR_NO_DATA;
781
782     if (move) {
783       *buffer = gst_adapter_take_buffer (nuv->adapter, size);
784     } else {
785       guint8 *data = NULL;
786
787       data = (guint8 *) gst_adapter_peek (nuv->adapter, size);
788       *buffer = gst_buffer_new ();
789       gst_buffer_set_data (*buffer, data, size);
790     }
791   }
792   return ret;
793 }
794
795 static gboolean
796 gst_nuv_demux_sink_activate (GstPad * sinkpad)
797 {
798   gboolean res = TRUE;
799   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (sinkpad));
800
801   if (gst_pad_check_pull_range (sinkpad)) {
802     nuv->mode = 0;
803     if (nuv->adapter != NULL) {
804       g_object_unref (nuv->adapter);
805       nuv->adapter = NULL;
806     }
807     res = gst_pad_activate_pull (sinkpad, TRUE);
808   } else {
809     nuv->mode = 1;
810     if (!nuv->adapter) {
811       nuv->adapter = gst_adapter_new ();
812     }
813     res = gst_pad_activate_push (sinkpad, TRUE);
814   }
815
816   g_object_unref (nuv);
817   return res;
818 }
819
820 static gboolean
821 gst_nuv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
822 {
823   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (sinkpad));
824
825   if (active) {
826     gst_pad_start_task (sinkpad, (GstTaskFunction) gst_nuv_demux_loop, sinkpad);
827   } else {
828     gst_pad_stop_task (sinkpad);
829   }
830   gst_object_unref (nuv);
831
832   return TRUE;
833 }
834
835 static GstFlowReturn
836 gst_nuv_demux_chain (GstPad * pad, GstBuffer * buf)
837 {
838   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (pad));
839
840   gst_adapter_push (nuv->adapter, buf);
841
842   return gst_nuv_demux_play (pad);
843 }
844
845 static void
846 gst_nuv_demux_loop (GstPad * pad)
847 {
848   gst_nuv_demux_play (pad);
849 }
850
851 static void
852 gst_nuv_demux_reset (GstNuvDemux * nuv)
853 {
854   nuv->state = GST_NUV_DEMUX_START;
855   nuv->mode = 0;
856   nuv->offset = 0;
857   nuv->video_offset = 0;
858   nuv->audio_offset = 0;
859
860   if (nuv->adapter != NULL)
861     gst_adapter_clear (nuv->adapter);
862
863   if (nuv->mpeg_buffer != NULL) {
864     gst_buffer_unref (nuv->mpeg_buffer);
865     nuv->mpeg_buffer = NULL;
866   }
867
868   g_free (nuv->h);
869   nuv->h = NULL;
870
871   g_free (nuv->eh);
872   nuv->eh = NULL;
873
874   g_free (nuv->fh);
875   nuv->fh = NULL;
876 }
877
878 static void
879 gst_nuv_demux_destoy_src_pad (GstNuvDemux * nuv)
880 {
881   if (nuv->src_video_pad) {
882     gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_video_pad);
883     nuv->src_video_pad = NULL;
884   }
885
886   if (nuv->src_audio_pad) {
887     gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_audio_pad);
888     nuv->src_audio_pad = NULL;
889   }
890 }
891
892 static GstStateChangeReturn
893 gst_nuv_demux_change_state (GstElement * element, GstStateChange transition)
894 {
895   GstStateChangeReturn ret;
896
897   switch (transition) {
898     case GST_STATE_CHANGE_READY_TO_PAUSED:
899       break;
900     default:
901       break;
902   }
903
904   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
905   if (ret == GST_STATE_CHANGE_FAILURE)
906     goto done;
907
908   switch (transition) {
909     case GST_STATE_CHANGE_PAUSED_TO_READY:
910       gst_nuv_demux_destoy_src_pad (GST_NUV_DEMUX (element));
911       gst_nuv_demux_reset (GST_NUV_DEMUX (element));
912       break;
913     default:
914       break;
915   }
916
917 done:
918   return ret;
919 }
920
921 static gboolean
922 plugin_init (GstPlugin * plugin)
923 {
924   GST_DEBUG_CATEGORY_INIT (nuvdemux_debug, "nuvdemux",
925       0, "Demuxer for NUV streams");
926
927 #ifdef ENABLE_NLS
928   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
929       LOCALEDIR);
930   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
931 #endif /* ENABLE_NLS */
932
933   if (!gst_element_register (plugin, "nuvdemux", GST_RANK_SECONDARY,
934           GST_TYPE_NUV_DEMUX)) {
935     return FALSE;
936   }
937   return TRUE;
938 }
939
940 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
941     GST_VERSION_MINOR,
942     "nuvdemux",
943     "Demuxes MythTV NuppelVideo files",
944     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)