]> git.sesse.net Git - vlc/blob - modules/codec/speex.c
* modules/codec/speex.c: fixed a couple of bugs.
[vlc] / modules / codec / speex.c
1 /*****************************************************************************
2  * speex.c: speex decoder/packetizer module making use of libspeex.
3  *****************************************************************************
4  * Copyright (C) 1999-2001 VideoLAN
5  * $Id: speex.c,v 1.2 2003/10/22 18:24:08 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             block_Release( p_block );
221             return VLC_EGENERIC;
222         }
223
224         p_sys->i_headers++;
225
226         if( p_sys->b_packetizer )
227         {
228             i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
229             block_Release( p_block );
230             return i_ret;
231         }
232         else
233         {
234             block_Release( p_block );
235             return VLC_SUCCESS;
236         }
237     }
238
239     if( p_sys->i_headers == 1 )
240     {
241         /* The next packet in order is the comments header */
242         ParseSpeexComments( p_dec, &oggpacket );
243         p_sys->i_headers++;
244
245         if( p_sys->b_packetizer )
246         {
247             i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
248             block_Release( p_block );
249             return i_ret;
250         }
251         else
252         {
253             block_Release( p_block );
254             return VLC_SUCCESS;
255         }
256     }
257
258     if( p_sys->i_headers < p_sys->p_header->extra_headers + 2 )
259     {
260         /* Skip them for now */
261         p_sys->i_headers++;
262     
263         if( p_sys->b_packetizer )
264         {
265             i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
266             block_Release( p_block );
267             return i_ret;
268         }
269         else
270         {
271             block_Release( p_block );
272             return VLC_SUCCESS;
273         }
274     }
275
276     i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
277     block_Release( p_block );
278     return i_ret;
279 }
280
281 /*****************************************************************************
282  * ProcessHeader: processes the inital Speex header packet.
283  *****************************************************************************/
284 static int ProcessHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
285 {
286     decoder_sys_t *p_sys = p_dec->p_sys;
287
288     void *p_state;
289     SpeexHeader *p_header;
290     SpeexMode *p_mode;
291     SpeexCallback callback;
292
293     p_sys->p_header = p_header =
294         speex_packet_to_header( p_oggpacket->packet, p_oggpacket->bytes );
295     if( !p_header )
296     {
297         msg_Err( p_dec, "Cannot read Speex header" );
298         return VLC_EGENERIC;
299     }
300     if( p_header->mode >= SPEEX_NB_MODES )
301     {
302         msg_Err( p_dec, "Mode number %d does not (yet/any longer) exist in "
303                  "this version of libspeex", p_header->mode );
304         return VLC_EGENERIC;
305     }
306
307     p_mode = speex_mode_list[p_header->mode];
308
309     if( p_header->speex_version_id > 1 )
310     {
311         msg_Err( p_dec, "This file was encoded with Speex bit-stream "
312                  "version %d, which I don't know how to decode",
313                  p_header->speex_version_id );
314         return VLC_EGENERIC;
315     }
316
317     if( p_mode->bitstream_version < p_header->mode_bitstream_version )
318     {
319         msg_Err( p_dec, "File encoded with a newer version of Speex" );
320         return VLC_EGENERIC;
321     }
322     if( p_mode->bitstream_version > p_header->mode_bitstream_version ) 
323     {
324         msg_Err( p_dec, "File encoded with an older version of Speex" );
325         return VLC_EGENERIC;
326     }
327    
328     msg_Dbg( p_dec, "Speex %d Hz audio using %s mode %s%s", 
329              p_header->rate, p_mode->modeName,
330              ( p_header->nb_channels == 1 ) ? " (mono" : " (stereo",
331              p_header->vbr ? ", VBR)" : ")" );
332
333     aout_DateInit( &p_sys->end_date, p_header->rate );
334
335     if( p_sys->b_packetizer )
336     {
337         /* add an input for the stream ouput */
338         p_sys->sout_format.i_sample_rate = p_header->rate;
339         p_sys->sout_format.i_channels    = p_header->nb_channels;
340         p_sys->sout_format.i_block_align = 1;
341         p_sys->sout_format.i_bitrate     = 0;
342
343         p_sys->p_sout_input = sout_InputNew( p_dec, &p_sys->sout_format );
344         if( !p_sys->p_sout_input )
345         {
346             msg_Err( p_dec, "cannot add a new stream" );
347             return VLC_EGENERIC;
348         }
349
350         /* We're done */
351         return VLC_SUCCESS;
352     }
353
354     /* Take care of speex decoder init */
355     speex_bits_init( &p_sys->bits );
356     p_sys->p_state = p_state = speex_decoder_init( p_mode );
357     if( !p_state )
358     {
359         msg_Err( p_dec, "Decoder initialization failed" );
360         return VLC_EGENERIC;
361     }
362
363     if( p_header->nb_channels == 2 )
364     {
365         SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
366         p_sys->stereo = stereo;
367         callback.callback_id = SPEEX_INBAND_STEREO;
368         callback.func = speex_std_stereo_request_handler;
369         callback.data = &p_sys->stereo;
370         speex_decoder_ctl( p_state, SPEEX_SET_HANDLER, &callback );
371     }
372
373     p_sys->aout_format.i_format = AOUT_FMT_S16_NE;
374     p_sys->aout_format.i_physical_channels =
375         p_sys->aout_format.i_original_channels =
376             pi_channels_maps[p_header->nb_channels];
377     p_sys->aout_format.i_rate = p_header->rate;
378
379     p_sys->p_aout = NULL;
380     p_sys->p_aout_input = aout_DecNew( p_dec, &p_sys->p_aout,
381                                        &p_sys->aout_format );
382     if( p_sys->p_aout_input == NULL )
383     {
384         msg_Err( p_dec, "failed to create aout fifo" );
385         return VLC_EGENERIC;
386     }
387
388     return VLC_SUCCESS;
389 }
390
391 /*****************************************************************************
392  * ProcessPacket: processes a Speex packet.
393  *****************************************************************************/
394 static int ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
395                           mtime_t i_pts )
396 {
397     decoder_sys_t *p_sys = p_dec->p_sys;
398
399     /* Date management */
400     if( i_pts > 0 && i_pts != aout_DateGet( &p_sys->end_date ) )
401     {
402         aout_DateSet( &p_sys->end_date, i_pts );
403     }
404
405     if( !aout_DateGet( &p_sys->end_date ) )
406     {
407         /* We've just started the stream, wait for the first PTS. */
408         return VLC_SUCCESS;
409     }
410
411     if( p_sys->b_packetizer )
412     {
413         return SendPacket( p_dec, p_oggpacket );
414     }
415     else
416     {
417         return DecodePacket( p_dec, p_oggpacket );
418     }
419 }
420
421 /*****************************************************************************
422  * DecodePacket: decodes a Speex packet.
423  *****************************************************************************/
424 static int DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
425 {
426     decoder_sys_t *p_sys = p_dec->p_sys;
427     int j;
428
429     /* Copy Ogg packet to Speex bitstream */
430     speex_bits_read_from( &p_sys->bits, p_oggpacket->packet,
431                           p_oggpacket->bytes );
432
433     /* Decode each frame of the packet */
434     for( j = 0; j != p_sys->p_header->frames_per_packet; j++ )
435     {
436         aout_buffer_t *p_aout_buffer;
437         int i_ret;
438
439         p_aout_buffer = aout_DecNewBuffer( p_sys->p_aout, p_sys->p_aout_input,
440                                            p_sys->p_header->frame_size );
441         if( !p_aout_buffer )
442         {
443             msg_Err( p_dec, "cannot get aout buffer" );
444             return VLC_SUCCESS;
445         }
446
447         i_ret = speex_decode( p_sys->p_state, &p_sys->bits,
448                               (int16_t *)p_aout_buffer->p_buffer );
449         if( i_ret == -1 ) break; /* End of stream */
450         if( i_ret== -2 )
451         {
452             msg_Warn( p_dec, "Decoding error: corrupted stream?" );
453             break;
454         }
455
456         if( speex_bits_remaining( &p_sys->bits ) < 0 )
457         {
458             msg_Warn( p_dec, "Decoding overflow: corrupted stream?" );
459             break;
460         }
461
462         if( p_sys->p_header->nb_channels == 2 )
463             speex_decode_stereo( (int16_t *)p_aout_buffer->p_buffer,
464                                  p_sys->p_header->frame_size, &p_sys->stereo );
465
466         /* Date management */
467         p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date );
468         p_aout_buffer->end_date =
469             aout_DateIncrement( &p_sys->end_date, p_sys->p_header->frame_size);
470
471         aout_DecPlay( p_sys->p_aout, p_sys->p_aout_input, p_aout_buffer );
472     }
473
474     return VLC_SUCCESS;
475 }
476
477 /*****************************************************************************
478  * SendPacket: send an ogg packet to the stream output.
479  *****************************************************************************/
480 static int SendPacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
481 {
482     decoder_sys_t *p_sys = p_dec->p_sys;
483
484     sout_buffer_t *p_sout_buffer =
485         sout_BufferNew( p_sys->p_sout_input->p_sout, p_oggpacket->bytes );
486
487     if( !p_sout_buffer ) return VLC_EGENERIC;
488
489     p_dec->p_vlc->pf_memcpy( p_sout_buffer->p_buffer,
490                              p_oggpacket->packet,
491                              p_oggpacket->bytes );
492
493     p_sout_buffer->i_bitrate = 0;
494
495     /* Date management */
496     p_sout_buffer->i_dts = p_sout_buffer->i_pts =
497         aout_DateGet( &p_sys->end_date );
498
499     if( p_sys->i_headers >= p_sys->p_header->extra_headers + 2 )
500         p_sout_buffer->i_length =
501             aout_DateIncrement( &p_sys->end_date,
502                                 p_sys->p_header->frame_size ) -
503             p_sout_buffer->i_pts;
504     else
505         p_sout_buffer->i_length = 0;
506
507     sout_InputSendBuffer( p_sys->p_sout_input, p_sout_buffer );
508
509     return VLC_SUCCESS;
510 }
511
512 /*****************************************************************************
513  * ParseSpeexComments: FIXME should be done in demuxer
514  *****************************************************************************/
515 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
516                            ((buf[base+2]<<16)&0xff0000)| \
517                            ((buf[base+1]<<8)&0xff00)| \
518                             (buf[base]&0xff))
519
520 static void ParseSpeexComments( decoder_t *p_dec, ogg_packet *p_oggpacket )
521 {
522     input_thread_t *p_input = (input_thread_t *)p_dec->p_parent;
523     decoder_sys_t *p_sys = p_dec->p_sys;
524
525     input_info_category_t *p_cat =
526         input_InfoCategory( p_input, _("Speex Comment") );
527
528     char *p_buf = (char *)p_oggpacket->packet;
529     SpeexMode *p_mode;
530     int i_len;
531
532     p_mode = speex_mode_list[p_sys->p_header->mode];
533     input_AddInfo( p_cat, _("Mode"), "%s%s",
534                    p_mode->modeName, p_sys->p_header->vbr ? " VBR" : "" );
535
536     if( p_oggpacket->bytes < 8 )
537     {
538         msg_Warn( p_dec, "Invalid/corrupted comments" );
539         return;
540     }
541
542     i_len = readint( p_buf, 0 ); p_buf += 4;
543     if( i_len > p_oggpacket->bytes - 4 )
544     {
545         msg_Warn( p_dec, "Invalid/corrupted comments" );
546         return;
547     }
548
549     input_AddInfo( p_cat, p_buf, "" );
550
551     /* TODO: finish comments parsing */
552 }
553
554 /*****************************************************************************
555  * EndDecoder: speex decoder destruction
556  *****************************************************************************/
557 static int EndDecoder( decoder_t * p_dec )
558 {
559     decoder_sys_t *p_sys = p_dec->p_sys;
560
561     if( p_sys->p_aout_input != NULL )
562     {
563         aout_DecDelete( p_sys->p_aout, p_sys->p_aout_input );
564     }
565
566     if( p_sys->p_sout_input != NULL )
567     {
568         sout_InputDelete( p_sys->p_sout_input );
569     }
570
571     if( p_sys->p_state )
572     {
573         speex_decoder_destroy( p_sys->p_state );
574         speex_bits_destroy( &p_sys->bits );
575     }
576
577     if( p_sys->p_header ) free( p_sys->p_header );
578     free( p_sys );
579
580     return VLC_SUCCESS;
581 }