]> git.sesse.net Git - vlc/blob - modules/codec/speex.c
* configure.ac, modules/codec/Modules.am, modules/codec/speex.c: new speex decoder...
[vlc] / modules / codec / speex.c
1 /*****************************************************************************
2  * speex.c: speex decoder module making use of libspeex.
3  *****************************************************************************
4  * Copyright (C) 1999-2001 VideoLAN
5  * $Id: speex.c,v 1.1 2003/10/22 17:12:30 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>                                    /* memcpy(), memset() */
29
30 #include <vlc/vlc.h>
31 #include <vlc/aout.h>
32 #include <vlc/decoder.h>
33 #include <vlc/input.h>
34 #include <vlc/sout.h>
35 #include <input_ext-dec.h>
36
37 #include <vlc/input.h>
38
39 #include <ogg/ogg.h>
40 #include <speex.h>
41 #include "speex_header.h"
42 #include "speex_stereo.h"
43 #include "speex_callbacks.h"
44
45 /*****************************************************************************
46  * decoder_sys_t : speex decoder descriptor
47  *****************************************************************************/
48 struct decoder_sys_t
49 {
50     /* Module mode */
51     vlc_bool_t b_packetizer;
52
53     /*
54      * Input properties
55      */
56     int i_headers;
57
58     /*
59      * Speex properties
60      */
61     SpeexBits bits;
62     SpeexHeader *p_header;
63     SpeexStereoState stereo;
64     void *p_state;
65
66     /*
67      * Output properties
68      */
69     aout_instance_t        *p_aout;
70     aout_input_t           *p_aout_input;
71     audio_sample_format_t   aout_format;
72
73     /*
74      * Packetizer output properties
75      */
76     sout_packetizer_input_t *p_sout_input;
77     sout_format_t           sout_format;
78
79     /*
80      * Common properties
81      */
82     audio_date_t          end_date;
83
84 };
85
86 static int pi_channels_maps[6] =
87 {
88     0,
89     AOUT_CHAN_CENTER,   AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
90     AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
91     AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
92      | AOUT_CHAN_REARRIGHT,
93     AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
94      | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
95 };
96
97 /****************************************************************************
98  * Local prototypes
99  ****************************************************************************/
100 static int OpenDecoder   ( vlc_object_t * );
101 static int OpenPacketizer( vlc_object_t * );
102
103 static int InitDecoder   ( decoder_t * );
104 static int RunDecoder    ( decoder_t *, block_t * );
105 static int EndDecoder    ( decoder_t * );
106
107 static int ProcessHeader ( decoder_t *, ogg_packet * );
108 static int ProcessPacket ( decoder_t *, ogg_packet *, mtime_t );
109 static int DecodePacket  ( decoder_t *, ogg_packet * );
110 static int SendPacket    ( decoder_t *, ogg_packet * );
111
112 static void ParseSpeexComments( decoder_t *, ogg_packet * );
113
114 /*****************************************************************************
115  * Module descriptor
116  *****************************************************************************/
117 vlc_module_begin();
118     set_description( _("Speex audio decoder") );
119     set_capability( "decoder", 100 );
120     set_callbacks( OpenDecoder, NULL );
121
122     add_submodule();
123     set_description( _("Speex audio packetizer") );
124     set_capability( "packetizer", 100 );
125     set_callbacks( OpenPacketizer, NULL );
126 vlc_module_end();
127
128 /*****************************************************************************
129  * OpenDecoder: probe the decoder and return score
130  *****************************************************************************/
131 static int OpenDecoder( vlc_object_t *p_this )
132 {
133     decoder_t *p_dec = (decoder_t*)p_this;
134
135     if( p_dec->p_fifo->i_fourcc != VLC_FOURCC('s','p','x',' ') )
136     {
137         return VLC_EGENERIC;
138     }
139
140     p_dec->pf_init = InitDecoder;
141     p_dec->pf_decode = RunDecoder;
142     p_dec->pf_end = EndDecoder;
143
144     /* Allocate the memory needed to store the decoder's structure */
145     if( ( p_dec->p_sys =
146           (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
147     {
148         msg_Err( p_dec, "out of memory" );
149         return VLC_EGENERIC;
150     }
151     p_dec->p_sys->b_packetizer = VLC_FALSE;
152
153     return VLC_SUCCESS;
154 }
155
156 static int OpenPacketizer( vlc_object_t *p_this )
157 {
158     decoder_t *p_dec = (decoder_t*)p_this;
159
160     int i_ret = OpenDecoder( p_this );
161
162     if( i_ret == VLC_SUCCESS ) p_dec->p_sys->b_packetizer = VLC_TRUE;
163
164     return i_ret;
165 }
166
167 /*****************************************************************************
168  * InitDecoder: Initalize the decoder
169  *****************************************************************************/
170 static int InitDecoder( decoder_t *p_dec )
171 {
172     decoder_sys_t *p_sys = p_dec->p_sys;
173
174     aout_DateSet( &p_sys->end_date, 0 );
175
176     p_sys->p_aout = NULL;
177     p_sys->p_aout_input = NULL;
178     p_sys->aout_format.i_format = VLC_FOURCC('s','p','x',' ');
179
180     p_sys->p_sout_input = NULL;
181     p_sys->sout_format.i_cat = AUDIO_ES;
182     p_sys->sout_format.i_fourcc = VLC_FOURCC( 's', 'p', 'x', ' ' );
183     p_sys->sout_format.i_block_align = 0;
184     p_sys->sout_format.i_bitrate     = 0;
185     p_sys->sout_format.i_extra_data  = 0;
186     p_sys->sout_format.p_extra_data  = NULL;
187
188     p_sys->i_headers = 0;
189     p_sys->p_state = NULL;
190     p_sys->p_header = NULL;
191
192     return VLC_SUCCESS;
193 }
194
195 /****************************************************************************
196  * RunDecoder: the whole thing
197  ****************************************************************************
198  * This function must be fed with ogg packets.
199  ****************************************************************************/
200 static int RunDecoder( decoder_t *p_dec, block_t *p_block )
201 {
202     decoder_sys_t *p_sys = p_dec->p_sys;
203     ogg_packet oggpacket;
204     int i_ret;
205
206     /* Block to Ogg packet */
207     oggpacket.packet = p_block->p_buffer;
208     oggpacket.bytes = p_block->i_buffer;
209     oggpacket.granulepos = -1;
210     oggpacket.b_o_s = 0;
211     oggpacket.e_o_s = 0;
212     oggpacket.packetno = 0;
213
214     if( p_sys->i_headers == 0 )
215     {
216         /* Take care of the initial Speex header */
217         if( ProcessHeader( p_dec, &oggpacket ) != VLC_SUCCESS )
218         {
219             msg_Err( p_dec, "Initial Speex header is corrupted" );
220             return VLC_EGENERIC;
221         }
222
223         p_sys->i_headers++;
224
225         if( p_sys->b_packetizer )
226         {
227             i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
228             block_Release( p_block );
229             return i_ret;
230         }
231         else
232         {
233             block_Release( p_block );
234             return VLC_SUCCESS;
235         }
236     }
237
238     if( p_sys->i_headers == 1 )
239     {
240         /* The next packet in order is the comments header */
241         ParseSpeexComments( p_dec, &oggpacket );
242         p_sys->i_headers++;
243
244         if( p_sys->b_packetizer )
245         {
246             i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
247             block_Release( p_block );
248             return i_ret;
249         }
250         else
251         {
252             block_Release( p_block );
253             return VLC_SUCCESS;
254         }
255     }
256
257     if( p_sys->i_headers < p_sys->p_header->extra_headers + 2 )
258     {
259         /* Skip them for now */
260         p_sys->i_headers++;
261     
262         if( p_sys->b_packetizer )
263         {
264             i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
265             block_Release( p_block );
266             return i_ret;
267         }
268         else
269         {
270             block_Release( p_block );
271             return VLC_SUCCESS;
272         }
273     }
274
275     i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
276     block_Release( p_block );
277     return i_ret;
278 }
279
280 /*****************************************************************************
281  * ProcessHeader: processes the inital Speex header packet.
282  *****************************************************************************/
283 static int ProcessHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
284 {
285     decoder_sys_t *p_sys = p_dec->p_sys;
286
287     void *p_state;
288     SpeexHeader *p_header;
289     SpeexMode *p_mode;
290     SpeexCallback callback;
291
292     p_sys->p_header = p_header =
293         speex_packet_to_header( p_oggpacket->packet, p_oggpacket->bytes );
294     if( !p_header )
295     {
296         msg_Err( p_dec, "Cannot read Speex header" );
297         return VLC_EGENERIC;
298     }
299     if( p_header->mode >= SPEEX_NB_MODES )
300     {
301         msg_Err( p_dec, "Mode number %d does not (yet/any longer) exist in "
302                  "this version of libspeex", p_header->mode );
303         return VLC_EGENERIC;
304     }
305
306     p_mode = speex_mode_list[p_header->mode];
307
308     if( p_header->speex_version_id > 1 )
309     {
310         msg_Err( p_dec, "This file was encoded with Speex bit-stream "
311                  "version %d, which I don't know how to decode",
312                  p_header->speex_version_id );
313         return VLC_EGENERIC;
314     }
315
316     if( p_mode->bitstream_version < p_header->mode_bitstream_version )
317     {
318         msg_Err( p_dec, "File encoded with a newer version of Speex" );
319         return VLC_EGENERIC;
320     }
321     if( p_mode->bitstream_version > p_header->mode_bitstream_version ) 
322     {
323         msg_Err( p_dec, "File encoded with an older version of Speex" );
324         return VLC_EGENERIC;
325     }
326    
327     msg_Dbg( p_dec, "Speex %d Hz audio using %s mode %s%s", 
328              p_header->rate, p_mode->modeName,
329              ( p_header->nb_channels == 1 ) ? " (mono" : " (stereo",
330              p_header->vbr ? ", VBR)" : ")" );
331
332     aout_DateInit( &p_sys->end_date, p_header->rate );
333
334     if( p_sys->b_packetizer )
335     {
336         /* add an input for the stream ouput */
337         p_sys->sout_format.i_sample_rate = p_header->rate;
338         p_sys->sout_format.i_channels    = p_header->nb_channels;
339         p_sys->sout_format.i_block_align = 1;
340         p_sys->sout_format.i_bitrate     = 0;
341
342         p_sys->p_sout_input = sout_InputNew( p_dec, &p_sys->sout_format );
343         if( !p_sys->p_sout_input )
344         {
345             msg_Err( p_dec, "cannot add a new stream" );
346             return VLC_EGENERIC;
347         }
348
349         /* We're done */
350         return VLC_SUCCESS;
351     }
352
353     /* Take care of speex decoder init */
354     speex_bits_init( &p_sys->bits );
355     p_sys->p_state = p_state = speex_decoder_init( p_mode );
356     if( !p_state )
357     {
358         msg_Err( p_dec, "Decoder initialization failed" );
359         return VLC_EGENERIC;
360     }
361
362     if( p_header->nb_channels == 1 )
363     {
364         SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
365         p_sys->stereo = stereo;
366         callback.callback_id = SPEEX_INBAND_STEREO;
367         callback.func = speex_std_stereo_request_handler;
368         callback.data = &p_sys->stereo;
369         speex_decoder_ctl( p_state, SPEEX_SET_HANDLER, &callback );
370     }
371
372     p_sys->aout_format.i_format = AOUT_FMT_S16_NE;
373     p_sys->aout_format.i_physical_channels =
374         p_sys->aout_format.i_original_channels =
375             pi_channels_maps[p_header->nb_channels];
376     p_sys->aout_format.i_rate = p_header->rate;
377
378     p_sys->p_aout = NULL;
379     p_sys->p_aout_input = aout_DecNew( p_dec, &p_sys->p_aout,
380                                        &p_sys->aout_format );
381     if( p_sys->p_aout_input == NULL )
382     {
383         msg_Err( p_dec, "failed to create aout fifo" );
384         return VLC_EGENERIC;
385     }
386
387     return VLC_SUCCESS;
388 }
389
390 /*****************************************************************************
391  * ProcessPacket: processes a Speex packet.
392  *****************************************************************************/
393 static int ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
394                           mtime_t i_pts )
395 {
396     decoder_sys_t *p_sys = p_dec->p_sys;
397
398     /* Date management */
399     if( i_pts > 0 && i_pts != aout_DateGet( &p_sys->end_date ) )
400     {
401         aout_DateSet( &p_sys->end_date, i_pts );
402     }
403
404     if( !aout_DateGet( &p_sys->end_date ) )
405     {
406         /* We've just started the stream, wait for the first PTS. */
407         return VLC_SUCCESS;
408     }
409
410     if( p_sys->b_packetizer )
411     {
412         return SendPacket( p_dec, p_oggpacket );
413     }
414     else
415     {
416         return DecodePacket( p_dec, p_oggpacket );
417     }
418 }
419
420 /*****************************************************************************
421  * DecodePacket: decodes a Speex packet.
422  *****************************************************************************/
423 static int DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
424 {
425     decoder_sys_t *p_sys = p_dec->p_sys;
426     int j;
427
428     /* Copy Ogg packet to Speex bitstream */
429     speex_bits_read_from( &p_sys->bits, p_oggpacket->packet,
430                           p_oggpacket->bytes );
431
432     /* Decode each frame of the packet */
433     for( j = 0; j != p_sys->p_header->frames_per_packet; j++ )
434     {
435         aout_buffer_t *p_aout_buffer;
436         int i_ret;
437
438         p_aout_buffer = aout_DecNewBuffer( p_sys->p_aout, p_sys->p_aout_input,
439                                            p_sys->p_header->frame_size );
440         if( !p_aout_buffer )
441         {
442             msg_Err( p_dec, "cannot get aout buffer" );
443             return VLC_SUCCESS;
444         }
445
446         i_ret = speex_decode( p_sys->p_state, &p_sys->bits,
447                               (int16_t *)p_aout_buffer->p_buffer );
448         if( i_ret == -1 ) break; /* End of stream */
449         if( i_ret== -2 )
450         {
451             msg_Warn( p_dec, "Decoding error: corrupted stream?" );
452             break;
453         }
454
455         if( speex_bits_remaining( &p_sys->bits ) < 0 )
456         {
457             msg_Warn( p_dec, "Decoding overflow: corrupted stream?" );
458             break;
459         }
460
461         if( p_sys->p_header->nb_channels == 2 )
462             speex_decode_stereo( (int16_t *)p_aout_buffer->p_buffer,
463                                  p_sys->p_header->frame_size, &p_sys->stereo );
464
465         /* Date management */
466         p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date );
467         p_aout_buffer->end_date =
468             aout_DateIncrement( &p_sys->end_date, p_sys->p_header->frame_size);
469
470         aout_DecPlay( p_sys->p_aout, p_sys->p_aout_input, p_aout_buffer );
471     }
472
473 }
474
475 /*****************************************************************************
476  * SendPacket: send an ogg packet to the stream output.
477  *****************************************************************************/
478 static int SendPacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
479 {
480     decoder_sys_t *p_sys = p_dec->p_sys;
481
482     sout_buffer_t *p_sout_buffer =
483         sout_BufferNew( p_sys->p_sout_input->p_sout, p_oggpacket->bytes );
484
485     if( !p_sout_buffer ) return VLC_EGENERIC;
486
487     p_dec->p_vlc->pf_memcpy( p_sout_buffer->p_buffer,
488                              p_oggpacket->packet,
489                              p_oggpacket->bytes );
490
491     p_sout_buffer->i_bitrate = 0;
492
493     /* Date management */
494     p_sout_buffer->i_dts = p_sout_buffer->i_pts =
495         aout_DateGet( &p_sys->end_date );
496
497     if( p_sys->i_headers >= p_sys->p_header->extra_headers + 2 )
498         p_sout_buffer->i_length =
499             aout_DateIncrement( &p_sys->end_date,
500                                 p_sys->p_header->frame_size ) -
501             p_sout_buffer->i_pts;
502     else
503         p_sout_buffer->i_length = 0;
504
505     sout_InputSendBuffer( p_sys->p_sout_input, p_sout_buffer );
506
507     return VLC_SUCCESS;
508 }
509
510 /*****************************************************************************
511  * ParseSpeexComments: FIXME should be done in demuxer
512  *****************************************************************************/
513 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
514                            ((buf[base+2]<<16)&0xff0000)| \
515                            ((buf[base+1]<<8)&0xff00)| \
516                             (buf[base]&0xff))
517
518 static void ParseSpeexComments( decoder_t *p_dec, ogg_packet *p_oggpacket )
519 {
520     input_thread_t *p_input = (input_thread_t *)p_dec->p_parent;
521     decoder_sys_t *p_sys = p_dec->p_sys;
522
523     input_info_category_t *p_cat =
524         input_InfoCategory( p_input, _("Speex Comment") );
525
526     char *p_buf = (char *)p_oggpacket->packet;
527     SpeexMode *p_mode;
528     int i_len;
529
530     p_mode = speex_mode_list[p_sys->p_header->mode];
531     input_AddInfo( p_cat, _("Mode"), "%s%s",
532                    p_mode->modeName, p_sys->p_header->vbr ? " VBR" : "" );
533
534     if( p_oggpacket->bytes < 8 )
535     {
536         msg_Warn( p_dec, "Invalid/corrupted comments" );
537         return;
538     }
539
540     i_len = readint( p_buf, 0 ); p_buf += 4;
541     if( i_len > p_oggpacket->bytes - 4 )
542     {
543         msg_Warn( p_dec, "Invalid/corrupted comments" );
544         return;
545     }
546
547     input_AddInfo( p_cat, p_buf, "" );
548
549     /* TODO: finish comments parsing */
550 }
551
552 /*****************************************************************************
553  * EndDecoder: speex decoder destruction
554  *****************************************************************************/
555 static int EndDecoder( decoder_t * p_dec )
556 {
557     decoder_sys_t *p_sys = p_dec->p_sys;
558
559     if( p_sys->p_aout_input != NULL )
560     {
561         aout_DecDelete( p_sys->p_aout, p_sys->p_aout_input );
562     }
563
564     if( p_sys->p_sout_input != NULL )
565     {
566         sout_InputDelete( p_sys->p_sout_input );
567     }
568
569     if( p_sys->p_state )
570     {
571         speex_decoder_destroy( p_sys->p_state );
572         speex_bits_destroy( &p_sys->bits );
573     }
574
575     if( p_sys->p_header ) free( p_sys->p_header );
576     free( p_sys );
577
578     return VLC_SUCCESS;
579 }