]> git.sesse.net Git - vlc/blob - modules/demux/ogg.c
* ALL: I did add these files, but forgot to commit them ( ouarf ouarf :)
[vlc] / modules / demux / ogg.c
1 /*****************************************************************************
2  * ogg.c : ogg stream input module for vlc
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: ogg.c,v 1.1 2002/10/24 09:30:48 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32
33 #include <sys/types.h>
34
35 #include <ogg/ogg.h>
36
37 #define OGG_BLOCK_SIZE 4096
38 #define PAGES_READ_ONCE 1
39
40 /*****************************************************************************
41  * Definitions of structures and functions used by this plugins 
42  *****************************************************************************/
43 typedef struct logical_stream_s
44 {
45     ogg_stream_state os;                        /* logical stream of packets */
46
47     int              i_serial_no;
48     int              i_pages_read;
49     int              i_cat;                            /* AUDIO_ES, VIDEO_ES */
50     int              i_activated;
51     vlc_fourcc_t     i_fourcc;
52     vlc_fourcc_t     i_codec;
53
54     es_descriptor_t  *p_es;   
55     int              b_selected;                           /* newly selected */
56
57     /* info for vorbis logical streams */
58     int i_rate;
59     int i_channels;
60     int i_bitrate;
61
62 } logical_stream_t;
63
64 struct demux_sys_t
65 {
66     ogg_sync_state oy;        /* sync and verify incoming physical bitstream */
67
68     int i_streams;                           /* number of logical bitstreams */
69     logical_stream_t **pp_stream;  /* pointer to an array of logical streams */
70
71     /* current audio and video es */
72     logical_stream_t *p_stream_video;
73     logical_stream_t *p_stream_audio;
74
75     mtime_t i_time;
76     mtime_t i_length;
77     mtime_t i_pcr; 
78     int     b_seekable;
79 };
80
81 /*****************************************************************************
82  * Local prototypes
83  *****************************************************************************/
84 static int  Activate  ( vlc_object_t * );
85 static void Deactivate( vlc_object_t * );
86 static int  Demux     ( input_thread_t * );
87
88 /* Stream managment */
89 static int  Ogg_StreamStart  ( input_thread_t *, demux_sys_t *, int );
90 static int  Ogg_StreamSeek   ( input_thread_t *, demux_sys_t *, int, mtime_t );
91 static void Ogg_StreamStop   ( input_thread_t *, demux_sys_t *, int );
92
93 /* Bitstream manipulation */
94 static int  Ogg_Check        ( input_thread_t *p_input );
95 static int  Ogg_ReadPage     ( input_thread_t *, demux_sys_t *, ogg_page * );
96 static void Ogg_DecodePacket ( input_thread_t *p_input,
97                                logical_stream_t *p_stream,
98                                ogg_packet *p_oggpacket );
99 static int  Ogg_FindLogicalStreams( input_thread_t *p_input,
100                                     demux_sys_t *p_ogg );
101
102 /*****************************************************************************
103  * Module descriptor
104  *****************************************************************************/
105 vlc_module_begin();
106     set_description( _("ogg stream demux" ) );
107     set_capability( "demux", 50 );
108     set_callbacks( Activate, Deactivate );
109     add_shortcut( "ogg" );
110 vlc_module_end();
111
112 /*****************************************************************************
113  * Stream managment
114  *****************************************************************************/
115 static int Ogg_StreamStart( input_thread_t *p_input,
116                             demux_sys_t *p_ogg, int i_stream )
117 {
118 #define p_stream p_ogg->pp_stream[i_stream]
119     if( !p_stream->p_es )
120     {
121         msg_Warn( p_input, "stream[%d] unselectable", i_stream );
122         return( 0 );
123     }
124     if( p_stream->i_activated )
125     {
126         msg_Warn( p_input, "stream[%d] already selected", i_stream );
127         return( 1 );
128     }
129
130     if( !p_stream->p_es->p_decoder_fifo )
131     {
132         vlc_mutex_lock( &p_input->stream.stream_lock );
133         input_SelectES( p_input, p_stream->p_es );
134         vlc_mutex_unlock( &p_input->stream.stream_lock );
135     }
136     p_stream->i_activated = p_stream->p_es->p_decoder_fifo ? 1 : 0;
137
138     //Ogg_StreamSeek( p_input, p_ogg, i_stream, p_ogg->i_time );
139
140     return( p_stream->i_activated );
141 #undef  p_stream
142 }
143
144 static void Ogg_StreamStop( input_thread_t *p_input,
145                             demux_sys_t *p_ogg, int i_stream )
146 {
147 #define p_stream    p_ogg->pp_stream[i_stream]
148
149     if( !p_stream->i_activated )
150     {
151         msg_Warn( p_input, "stream[%d] already unselected", i_stream );
152         return;
153     }
154
155     if( p_stream->p_es->p_decoder_fifo )
156     {
157         vlc_mutex_lock( &p_input->stream.stream_lock );
158         input_UnselectES( p_input, p_stream->p_es );
159         vlc_mutex_unlock( &p_input->stream.stream_lock );
160     }
161
162     p_stream->i_activated = 0;
163
164 #undef  p_stream
165 }
166
167 static int Ogg_StreamSeek( input_thread_t *p_input, demux_sys_t  *p_ogg,
168                            int i_stream, mtime_t i_date )
169 {
170 #define p_stream    p_ogg->pp_stream[i_stream]
171
172     /* FIXME: todo */
173
174     return 1;
175 #undef p_stream
176 }
177
178 /****************************************************************************
179  * Ogg_Check: Check we are dealing with an ogg stream.
180  ****************************************************************************/
181 static int Ogg_Check( input_thread_t *p_input )
182 {
183     u8 *p_peek;
184     int i_size = input_Peek( p_input, &p_peek, 4 );
185
186     /* Check for the Ogg capture pattern */
187     if( !(i_size>3) || !(p_peek[0] == 'O') || !(p_peek[1] == 'g') ||
188         !(p_peek[2] == 'g') || !(p_peek[3] == 'S') )
189         return VLC_EGENERIC;
190
191     /* FIXME: Capture pattern might not be enough so we can also check for the
192      * the first complete page */
193
194     return VLC_SUCCESS;
195 }
196
197 /****************************************************************************
198  * Ogg_ReadPage: Read a full Ogg page from the physical bitstream.
199  ****************************************************************************
200  * Returns VLC_SUCCESS if a page has been read. An error might happen if we
201  * are at the end of stream.
202  ****************************************************************************/
203 static int Ogg_ReadPage( input_thread_t *p_input, demux_sys_t *p_ogg,
204                          ogg_page *p_oggpage )
205 {
206     int i_read = 0;
207     data_packet_t *p_data;
208     byte_t *p_buffer;
209
210     while( ogg_sync_pageout( &p_ogg->oy, p_oggpage ) != 1 )
211     {
212         i_read = input_SplitBuffer( p_input, &p_data, OGG_BLOCK_SIZE );
213         if( i_read <= 0 )
214             return VLC_EGENERIC;
215
216         p_buffer = ogg_sync_buffer( &p_ogg->oy, i_read );
217         p_input->p_vlc->pf_memcpy( p_buffer, p_data->p_payload_start, i_read );
218         ogg_sync_wrote( &p_ogg->oy, i_read );
219         input_DeletePacket( p_input->p_method_data, p_data );
220     }
221
222     return VLC_SUCCESS;
223 }
224
225 /****************************************************************************
226  * Ogg_DecodePacket: Decode an Ogg packet.
227  ****************************************************************************/
228 static void Ogg_DecodePacket( input_thread_t *p_input,
229                               logical_stream_t *p_stream,
230                               ogg_packet *p_oggpacket )
231 {
232     pes_packet_t  *p_pes;
233     data_packet_t *p_data;
234
235     if( !( p_pes = input_NewPES( p_input->p_method_data ) ) )
236     {
237         return;
238     }
239     if( !( p_data = input_NewPacket( p_input->p_method_data,
240                                      p_oggpacket->bytes ) ) )
241     {
242         input_DeletePES( p_input->p_method_data, p_pes );
243         return;
244     }
245
246     p_pes->p_first = p_pes->p_last = p_data;
247     p_pes->i_nb_data = 1;
248     p_pes->i_pes_size = p_oggpacket->bytes;
249     p_pes->i_dts = p_oggpacket->granulepos;
250
251     /* Convert the granule into a pts */
252     p_pes->i_pts = (p_oggpacket->granulepos < 0) ? 0 :
253         p_oggpacket->granulepos * 90000 / p_stream->i_rate;
254     p_pes->i_pts = (p_oggpacket->granulepos < 0) ? 0 :
255         input_ClockGetTS( p_input, p_input->stream.p_selected_program,
256                           p_pes->i_pts );
257
258     memcpy( p_data->p_payload_start, p_oggpacket->packet, p_oggpacket->bytes );
259
260     input_DecodePES( p_stream->p_es->p_decoder_fifo, p_pes );
261 }
262
263 /****************************************************************************
264  * Ogg_FindLogicalStreams: Find the logical streams embedded in the physical
265  *                         stream and fill p_ogg.
266  *****************************************************************************
267  * The initial page of a logical stream is marked as a 'bos' page.
268  * Furthermore, the Ogg specification mandates that grouped bitstreams begin
269  * together and all of the initial pages must appear before any data pages.
270  *
271  * On success this function returns VLC_SUCCESS.
272  ****************************************************************************/
273 static int Ogg_FindLogicalStreams( input_thread_t *p_input, demux_sys_t *p_ogg)
274 {
275     ogg_packet oggpacket;
276     ogg_page oggpage;
277     int i_stream;
278
279     while( Ogg_ReadPage( p_input, p_ogg, &oggpage ) == VLC_SUCCESS )
280     {
281         if( ogg_page_bos( &oggpage ) )
282         {
283
284             /* All is wonderful in our fine fine little world.
285              * We found the beginning of our first logical stream. */
286             while( ogg_page_bos( &oggpage ) )
287             {
288                 p_ogg->i_streams++;
289                 p_ogg->pp_stream =
290                     realloc( p_ogg->pp_stream, p_ogg->i_streams *
291                              sizeof(logical_stream_t *) );
292
293 #define p_stream p_ogg->pp_stream[p_ogg->i_streams - 1]
294
295                 p_stream = malloc( sizeof(logical_stream_t) );
296                 memset( p_stream, 0, sizeof(logical_stream_t) );
297
298                 /* Setup the logical stream */
299                 p_stream->i_pages_read++;
300                 p_stream->i_serial_no = ogg_page_serialno( &oggpage );
301                 ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
302
303                 /* Extract the initial header from the first page and verify
304                  * the codec type of tis Ogg bitstream */
305                 if( ogg_stream_pagein( &p_stream->os, &oggpage ) < 0 )
306                 {
307                     /* error. stream version mismatch perhaps */
308                     msg_Err( p_input, "Error reading first page of "
309                              "Ogg bitstream data" );
310                     return VLC_EGENERIC;
311                 }
312
313                 /* FIXME: check return value */
314                 ogg_stream_packetpeek( &p_stream->os, &oggpacket );
315
316                 /* Check for Vorbis header */
317                 if( oggpacket.bytes >= 7 &&
318                     ! strncmp( &oggpacket.packet[1], "vorbis", 6 ) )
319                 {
320                     oggpack_buffer opb;
321
322                     msg_Dbg( p_input, "found vorbis header" );
323                     p_stream->i_cat = AUDIO_ES;
324                     p_stream->i_fourcc = VLC_FOURCC( 'v','o','r','b' );
325
326                     /* Cheat and get additionnal info ;) */
327                     oggpack_readinit( &opb, oggpacket.packet, oggpacket.bytes);
328                     oggpack_adv( &opb, 88 );
329                     p_stream->i_channels = oggpack_read( &opb, 8 );
330                     p_stream->i_rate = oggpack_read( &opb, 32 );
331                     oggpack_adv( &opb, 32 );
332                     p_stream->i_bitrate = oggpack_read( &opb, 32 );
333                 }
334                 else
335                 {
336                     msg_Dbg( p_input, "found unknown codec" );
337                     ogg_stream_destroy( &p_stream->os );
338                     free( p_stream );
339                     p_ogg->i_streams--;
340                 }
341
342 #undef p_stream
343
344                 if( Ogg_ReadPage( p_input, p_ogg, &oggpage ) != VLC_SUCCESS )
345                     return VLC_EGENERIC;
346             }
347
348             /* This is the first data page, which means we are now finished
349              * with the initial pages. We just need to store it in the relevant
350              * bitstream. */
351             for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
352             {
353                 if( ogg_stream_pagein( &p_ogg->pp_stream[i_stream]->os,
354                                        &oggpage ) == 0 )
355                 {
356                     p_ogg->pp_stream[i_stream]->i_pages_read++;
357                     break;
358                 }
359             }
360             return VLC_SUCCESS;
361         }
362     }
363     return VLC_EGENERIC;
364 }
365
366 /*****************************************************************************
367  * Activate: initializes ogg demux structures
368  *****************************************************************************/
369 static int Activate( vlc_object_t * p_this )
370 {
371     int i_stream, b_forced;
372     demux_sys_t    *p_ogg;
373     input_thread_t *p_input = (input_thread_t *)p_this;
374
375     p_input->p_demux_data = NULL;
376     b_forced = ( ( *p_input->psz_demux )&&
377                  ( !strncmp( p_input->psz_demux, "ogg", 10 ) ) ) ? 1 : 0;
378
379     /* Check if we are dealing with an ogg stream */
380     if( !b_forced && ( Ogg_Check( p_input ) != VLC_SUCCESS ) )
381         return -1;
382
383     /* Allocate p_ogg */
384     if( !( p_ogg = malloc( sizeof( demux_sys_t ) ) ) )
385     {
386         msg_Err( p_input, "out of memory" );
387         goto error;
388     }
389     memset( p_ogg, 0, sizeof( demux_sys_t ) );
390     p_input->p_demux_data = p_ogg;
391
392     p_ogg->i_time = 0;
393     p_ogg->i_pcr  = 0;
394     p_ogg->b_seekable = ( ( p_input->stream.b_seekable )
395                         &&( p_input->stream.i_method == INPUT_METHOD_FILE ) );
396
397     /* Initialize the Ogg physical bitstream parser */
398     ogg_sync_init( &p_ogg->oy );
399
400     /* Find the logical streams embedded in the physical stream and
401      * initialize our p_ogg structure. */
402     if( Ogg_FindLogicalStreams( p_input, p_ogg ) != VLC_SUCCESS )
403     {
404         msg_Err( p_input, "couldn't find an ogg logical stream" );
405         goto error;
406     }
407
408     /* Set the demux function */
409     p_input->pf_demux = Demux;
410
411     /* Initialize access plug-in structures. */
412     if( p_input->i_mtu == 0 )
413     {
414         /* Improve speed. */
415         p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
416     }
417
418     /* Create one program */
419     vlc_mutex_lock( &p_input->stream.stream_lock );
420     if( input_InitStream( p_input, 0 ) == -1)
421     {
422         vlc_mutex_unlock( &p_input->stream.stream_lock );
423         msg_Err( p_input, "cannot init stream" );
424         goto error;
425     }    
426     if( input_AddProgram( p_input, 0, 0) == NULL )
427     {
428         vlc_mutex_unlock( &p_input->stream.stream_lock );
429         msg_Err( p_input, "cannot add program" );
430         goto error;
431     }
432     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
433     vlc_mutex_unlock( &p_input->stream.stream_lock ); 
434
435     for( i_stream = 0 ; i_stream < p_ogg->i_streams; i_stream++ )
436     {
437 #define p_stream p_ogg->pp_stream[i_stream]
438         vlc_mutex_lock( &p_input->stream.stream_lock );
439         p_stream->p_es = input_AddES( p_input,
440                                       p_input->stream.p_selected_program,
441                                       p_ogg->i_streams + 1, 0 );
442         vlc_mutex_unlock( &p_input->stream.stream_lock );
443         p_stream->p_es->i_stream_id = i_stream;
444         p_stream->p_es->i_fourcc = p_stream->i_fourcc;
445         p_stream->p_es->i_cat = p_stream->i_cat;
446 #undef p_stream
447     }
448
449     vlc_mutex_lock( &p_input->stream.stream_lock ); 
450     p_input->stream.i_mux_rate = 0;
451     vlc_mutex_unlock( &p_input->stream.stream_lock ); 
452
453     for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
454     {
455 #define p_stream  p_ogg->pp_stream[i_stream]
456         switch( p_stream->p_es->i_cat )
457         {
458             case( VIDEO_ES ):
459
460                 if( (p_ogg->p_stream_video == NULL) )
461                 {
462                     p_ogg->p_stream_video = p_stream;
463                     /* TODO add test to see if a decoder has been found */
464                     Ogg_StreamStart( p_input, p_ogg, i_stream );
465                 }
466                 break;
467
468             case( AUDIO_ES ):
469                 if( (p_ogg->p_stream_audio == NULL) ) 
470                 {
471                     p_ogg->p_stream_audio = p_stream;
472                     Ogg_StreamStart( p_input, p_ogg, i_stream );
473                 }
474                 break;
475
476             default:
477                 break;
478         }
479 #undef p_stream
480     }
481
482     /* we select the first audio and video ES */
483     vlc_mutex_lock( &p_input->stream.stream_lock );
484     if( !p_ogg->p_stream_video )
485     {
486         msg_Warn( p_input, "no video stream found" );
487     }
488     if( !p_ogg->p_stream_audio )
489     {
490         msg_Warn( p_input, "no audio stream found!" );
491     }
492     p_input->stream.p_selected_program->b_is_ok = 1;
493     vlc_mutex_unlock( &p_input->stream.stream_lock );
494
495     return 0;
496
497  error:
498     Deactivate( (vlc_object_t *)p_input );
499     return -1;
500
501 }
502
503 /*****************************************************************************
504  * Deactivate: frees unused data
505  *****************************************************************************/
506 static void Deactivate( vlc_object_t *p_this )
507 {
508     input_thread_t *p_input = (input_thread_t *)p_this;
509     demux_sys_t *p_ogg = (demux_sys_t *)p_input->p_demux_data  ; 
510     int i;
511
512     if( p_ogg )
513     {
514         /* Cleanup the bitstream parser */
515         ogg_sync_clear( &p_ogg->oy );
516
517         for( i = 0; i < p_ogg->i_streams; i++ )
518         {
519             ogg_stream_clear( &p_ogg->pp_stream[i]->os );
520             free( p_ogg->pp_stream[i] );
521         }
522         if( p_ogg->pp_stream ) free( p_ogg->pp_stream );
523
524         free( p_ogg );
525     }
526 }
527
528 /*****************************************************************************
529  * Demux: reads and demuxes data packets
530  *****************************************************************************
531  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
532  *****************************************************************************/
533 static int Demux( input_thread_t * p_input )
534 {
535     int i, i_stream, b_eos;
536     ogg_page    oggpage;
537     ogg_packet  oggpacket;
538     demux_sys_t *p_ogg  = (demux_sys_t *)p_input->p_demux_data;
539
540 #define p_stream p_ogg->pp_stream[i_stream]
541     /* detect new selected/unselected streams */
542     for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
543     {
544         if( p_stream->p_es )
545         {
546             if( p_stream->p_es->p_decoder_fifo &&
547                 !p_stream->i_activated )
548             {
549                 Ogg_StreamStart( p_input, p_ogg, i_stream );
550             }
551             else
552             if( !p_stream->p_es->p_decoder_fifo &&
553                 p_stream->i_activated )
554             {
555                 Ogg_StreamStop( p_input, p_ogg, i_stream );
556             }
557         }
558     }
559
560     /* search new video and audio stream selected
561      * if current have been unselected */
562     if( ( !p_ogg->p_stream_video )
563             || ( !p_ogg->p_stream_video->p_es->p_decoder_fifo ) )
564     {
565         p_ogg->p_stream_video = NULL;
566         for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
567         {
568             if( ( p_stream->i_cat == VIDEO_ES )
569                   &&( p_stream->p_es->p_decoder_fifo ) )
570             {
571                 p_ogg->p_stream_video = p_stream;
572                 break;
573             }
574         }
575     }
576     if( ( !p_ogg->p_stream_audio )
577             ||( !p_ogg->p_stream_audio->p_es->p_decoder_fifo ) )
578     {
579         p_ogg->p_stream_audio = NULL;
580         for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
581         {
582             if( ( p_stream->i_cat == AUDIO_ES )
583                   &&( p_stream->p_es->p_decoder_fifo ) )
584             {
585                 p_ogg->p_stream_audio = p_stream;
586                 break;
587             }
588         }
589     }
590
591     /* Update program clock reference */
592     p_ogg->i_pcr = p_ogg->i_time * 9 / 100;
593
594     if( (p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT)
595          || (input_ClockManageControl( p_input, 
596                       p_input->stream.p_selected_program,
597                          (mtime_t)0 ) == PAUSE_S) )
598     {
599         msg_Warn( p_input, "synchro reinit" );
600         p_input->stream.p_selected_program->i_synchro_state = SYNCHRO_OK;
601     }
602
603     /* Call the pace control. */
604     input_ClockManageRef( p_input,
605                           p_input->stream.p_selected_program,
606                           p_ogg->i_pcr );
607
608     /* Demux ogg pages from the stream */
609     b_eos = 0;
610     for( i = 0; i < PAGES_READ_ONCE; i++ )
611     {
612         if( Ogg_ReadPage(p_input, p_ogg, &oggpage ) != VLC_SUCCESS )
613         {
614             b_eos = 1;
615             break;
616         }
617
618         for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
619         {
620             /* FIXME: we already read the header */
621
622             if( ogg_stream_pagein( &p_stream->os, &oggpage ) != 0 )
623                 continue;
624
625             while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
626             {
627                 /* FIXME: handle discontinuity */
628
629                 if( !p_stream->p_es ||
630                     !p_stream->p_es->p_decoder_fifo )
631                 {
632                     break;
633                 }
634
635                 if( oggpacket.granulepos >= 0 )
636                     p_ogg->i_time = oggpacket.granulepos * 1000000
637                         / p_stream->i_rate;
638                 else
639                     p_ogg->i_time += (oggpacket.bytes * 1000000
640                         / p_stream->i_bitrate);
641
642                 Ogg_DecodePacket( p_input, p_stream, &oggpacket );
643             }
644         }
645 #undef p_stream
646     }
647
648     /* Did we reach the end of stream ? */
649     return( b_eos ? 0 : 1 );
650 }