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 $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
28 #include <string.h> /* memcpy(), memset() */
32 #include <vlc/decoder.h>
33 #include <vlc/input.h>
35 #include <input_ext-dec.h>
37 #include <vlc/input.h>
41 #include "speex_header.h"
42 #include "speex_stereo.h"
43 #include "speex_callbacks.h"
45 /*****************************************************************************
46 * decoder_sys_t : speex decoder descriptor
47 *****************************************************************************/
51 vlc_bool_t b_packetizer;
62 SpeexHeader *p_header;
63 SpeexStereoState stereo;
69 aout_instance_t *p_aout;
70 aout_input_t *p_aout_input;
71 audio_sample_format_t aout_format;
74 * Packetizer output properties
76 sout_packetizer_input_t *p_sout_input;
77 sout_format_t sout_format;
82 audio_date_t end_date;
86 static int pi_channels_maps[6] =
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
97 /****************************************************************************
99 ****************************************************************************/
100 static int OpenDecoder ( vlc_object_t * );
101 static int OpenPacketizer( vlc_object_t * );
103 static int InitDecoder ( decoder_t * );
104 static int RunDecoder ( decoder_t *, block_t * );
105 static int EndDecoder ( decoder_t * );
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 * );
112 static void ParseSpeexComments( decoder_t *, ogg_packet * );
114 /*****************************************************************************
116 *****************************************************************************/
118 set_description( _("Speex audio decoder") );
119 set_capability( "decoder", 100 );
120 set_callbacks( OpenDecoder, NULL );
123 set_description( _("Speex audio packetizer") );
124 set_capability( "packetizer", 100 );
125 set_callbacks( OpenPacketizer, NULL );
128 /*****************************************************************************
129 * OpenDecoder: probe the decoder and return score
130 *****************************************************************************/
131 static int OpenDecoder( vlc_object_t *p_this )
133 decoder_t *p_dec = (decoder_t*)p_this;
135 if( p_dec->p_fifo->i_fourcc != VLC_FOURCC('s','p','x',' ') )
140 p_dec->pf_init = InitDecoder;
141 p_dec->pf_decode = RunDecoder;
142 p_dec->pf_end = EndDecoder;
144 /* Allocate the memory needed to store the decoder's structure */
146 (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
148 msg_Err( p_dec, "out of memory" );
151 p_dec->p_sys->b_packetizer = VLC_FALSE;
156 static int OpenPacketizer( vlc_object_t *p_this )
158 decoder_t *p_dec = (decoder_t*)p_this;
160 int i_ret = OpenDecoder( p_this );
162 if( i_ret == VLC_SUCCESS ) p_dec->p_sys->b_packetizer = VLC_TRUE;
167 /*****************************************************************************
168 * InitDecoder: Initalize the decoder
169 *****************************************************************************/
170 static int InitDecoder( decoder_t *p_dec )
172 decoder_sys_t *p_sys = p_dec->p_sys;
174 aout_DateSet( &p_sys->end_date, 0 );
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',' ');
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;
188 p_sys->i_headers = 0;
189 p_sys->p_state = NULL;
190 p_sys->p_header = NULL;
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 )
202 decoder_sys_t *p_sys = p_dec->p_sys;
203 ogg_packet oggpacket;
206 /* Block to Ogg packet */
207 oggpacket.packet = p_block->p_buffer;
208 oggpacket.bytes = p_block->i_buffer;
209 oggpacket.granulepos = -1;
212 oggpacket.packetno = 0;
214 if( p_sys->i_headers == 0 )
216 /* Take care of the initial Speex header */
217 if( ProcessHeader( p_dec, &oggpacket ) != VLC_SUCCESS )
219 msg_Err( p_dec, "Initial Speex header is corrupted" );
220 block_Release( p_block );
226 if( p_sys->b_packetizer )
228 i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
229 block_Release( p_block );
234 block_Release( p_block );
239 if( p_sys->i_headers == 1 )
241 /* The next packet in order is the comments header */
242 ParseSpeexComments( p_dec, &oggpacket );
245 if( p_sys->b_packetizer )
247 i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
248 block_Release( p_block );
253 block_Release( p_block );
258 if( p_sys->i_headers < p_sys->p_header->extra_headers + 2 )
260 /* Skip them for now */
263 if( p_sys->b_packetizer )
265 i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
266 block_Release( p_block );
271 block_Release( p_block );
276 i_ret = ProcessPacket( p_dec, &oggpacket, p_block->i_pts );
277 block_Release( p_block );
281 /*****************************************************************************
282 * ProcessHeader: processes the inital Speex header packet.
283 *****************************************************************************/
284 static int ProcessHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
286 decoder_sys_t *p_sys = p_dec->p_sys;
289 SpeexHeader *p_header;
291 SpeexCallback callback;
293 p_sys->p_header = p_header =
294 speex_packet_to_header( p_oggpacket->packet, p_oggpacket->bytes );
297 msg_Err( p_dec, "Cannot read Speex header" );
300 if( p_header->mode >= SPEEX_NB_MODES )
302 msg_Err( p_dec, "Mode number %d does not (yet/any longer) exist in "
303 "this version of libspeex", p_header->mode );
307 p_mode = speex_mode_list[p_header->mode];
309 if( p_header->speex_version_id > 1 )
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 );
317 if( p_mode->bitstream_version < p_header->mode_bitstream_version )
319 msg_Err( p_dec, "File encoded with a newer version of Speex" );
322 if( p_mode->bitstream_version > p_header->mode_bitstream_version )
324 msg_Err( p_dec, "File encoded with an older version of Speex" );
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)" : ")" );
333 aout_DateInit( &p_sys->end_date, p_header->rate );
335 if( p_sys->b_packetizer )
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;
343 p_sys->p_sout_input = sout_InputNew( p_dec, &p_sys->sout_format );
344 if( !p_sys->p_sout_input )
346 msg_Err( p_dec, "cannot add a new stream" );
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 );
359 msg_Err( p_dec, "Decoder initialization failed" );
363 if( p_header->nb_channels == 2 )
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 );
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;
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 )
384 msg_Err( p_dec, "failed to create aout fifo" );
391 /*****************************************************************************
392 * ProcessPacket: processes a Speex packet.
393 *****************************************************************************/
394 static int ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
397 decoder_sys_t *p_sys = p_dec->p_sys;
399 /* Date management */
400 if( i_pts > 0 && i_pts != aout_DateGet( &p_sys->end_date ) )
402 aout_DateSet( &p_sys->end_date, i_pts );
405 if( !aout_DateGet( &p_sys->end_date ) )
407 /* We've just started the stream, wait for the first PTS. */
411 if( p_sys->b_packetizer )
413 return SendPacket( p_dec, p_oggpacket );
417 return DecodePacket( p_dec, p_oggpacket );
421 /*****************************************************************************
422 * DecodePacket: decodes a Speex packet.
423 *****************************************************************************/
424 static int DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
426 decoder_sys_t *p_sys = p_dec->p_sys;
429 /* Copy Ogg packet to Speex bitstream */
430 speex_bits_read_from( &p_sys->bits, p_oggpacket->packet,
431 p_oggpacket->bytes );
433 /* Decode each frame of the packet */
434 for( j = 0; j != p_sys->p_header->frames_per_packet; j++ )
436 aout_buffer_t *p_aout_buffer;
439 p_aout_buffer = aout_DecNewBuffer( p_sys->p_aout, p_sys->p_aout_input,
440 p_sys->p_header->frame_size );
443 msg_Err( p_dec, "cannot get aout buffer" );
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 */
452 msg_Warn( p_dec, "Decoding error: corrupted stream?" );
456 if( speex_bits_remaining( &p_sys->bits ) < 0 )
458 msg_Warn( p_dec, "Decoding overflow: corrupted stream?" );
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 );
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);
471 aout_DecPlay( p_sys->p_aout, p_sys->p_aout_input, p_aout_buffer );
477 /*****************************************************************************
478 * SendPacket: send an ogg packet to the stream output.
479 *****************************************************************************/
480 static int SendPacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
482 decoder_sys_t *p_sys = p_dec->p_sys;
484 sout_buffer_t *p_sout_buffer =
485 sout_BufferNew( p_sys->p_sout_input->p_sout, p_oggpacket->bytes );
487 if( !p_sout_buffer ) return VLC_EGENERIC;
489 p_dec->p_vlc->pf_memcpy( p_sout_buffer->p_buffer,
491 p_oggpacket->bytes );
493 p_sout_buffer->i_bitrate = 0;
495 /* Date management */
496 p_sout_buffer->i_dts = p_sout_buffer->i_pts =
497 aout_DateGet( &p_sys->end_date );
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;
505 p_sout_buffer->i_length = 0;
507 sout_InputSendBuffer( p_sys->p_sout_input, p_sout_buffer );
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)| \
520 static void ParseSpeexComments( decoder_t *p_dec, ogg_packet *p_oggpacket )
522 input_thread_t *p_input = (input_thread_t *)p_dec->p_parent;
523 decoder_sys_t *p_sys = p_dec->p_sys;
525 input_info_category_t *p_cat =
526 input_InfoCategory( p_input, _("Speex Comment") );
528 char *p_buf = (char *)p_oggpacket->packet;
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" : "" );
536 if( p_oggpacket->bytes < 8 )
538 msg_Warn( p_dec, "Invalid/corrupted comments" );
542 i_len = readint( p_buf, 0 ); p_buf += 4;
543 if( i_len > p_oggpacket->bytes - 4 )
545 msg_Warn( p_dec, "Invalid/corrupted comments" );
549 input_AddInfo( p_cat, p_buf, "" );
551 /* TODO: finish comments parsing */
554 /*****************************************************************************
555 * EndDecoder: speex decoder destruction
556 *****************************************************************************/
557 static int EndDecoder( decoder_t * p_dec )
559 decoder_sys_t *p_sys = p_dec->p_sys;
561 if( p_sys->p_aout_input != NULL )
563 aout_DecDelete( p_sys->p_aout, p_sys->p_aout_input );
566 if( p_sys->p_sout_input != NULL )
568 sout_InputDelete( p_sys->p_sout_input );
573 speex_decoder_destroy( p_sys->p_state );
574 speex_bits_destroy( &p_sys->bits );
577 if( p_sys->p_header ) free( p_sys->p_header );