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.3 2003/11/16 21:07:30 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() */
31 #include <vlc/decoder.h>
32 #include <vlc/input.h>
36 #include "speex_header.h"
37 #include "speex_stereo.h"
38 #include "speex_callbacks.h"
40 /*****************************************************************************
41 * decoder_sys_t : speex decoder descriptor
42 *****************************************************************************/
46 vlc_bool_t b_packetizer;
52 int i_frame_in_packet;
58 SpeexHeader *p_header;
59 SpeexStereoState stereo;
65 audio_date_t end_date;
69 static int pi_channels_maps[6] =
72 AOUT_CHAN_CENTER, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
73 AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
74 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
75 | AOUT_CHAN_REARRIGHT,
76 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
77 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
80 /****************************************************************************
82 ****************************************************************************/
83 static int OpenDecoder ( vlc_object_t * );
84 static int OpenPacketizer( vlc_object_t * );
85 static void CloseDecoder ( vlc_object_t * );
87 static void *DecodeBlock ( decoder_t *, block_t ** );
88 static int ProcessHeader ( decoder_t *, ogg_packet * );
89 static void *ProcessPacket( decoder_t *, ogg_packet *, block_t ** );
91 static aout_buffer_t *DecodePacket( decoder_t *, ogg_packet * );
92 static block_t *SendPacket( decoder_t *, ogg_packet *, block_t * );
94 static void ParseSpeexComments( decoder_t *, ogg_packet * );
96 /*****************************************************************************
98 *****************************************************************************/
100 set_description( _("Speex audio decoder") );
101 set_capability( "decoder", 100 );
102 set_callbacks( OpenDecoder, CloseDecoder );
105 set_description( _("Speex audio packetizer") );
106 set_capability( "packetizer", 100 );
107 set_callbacks( OpenPacketizer, CloseDecoder );
110 /*****************************************************************************
111 * OpenDecoder: probe the decoder and return score
112 *****************************************************************************/
113 static int OpenDecoder( vlc_object_t *p_this )
115 decoder_t *p_dec = (decoder_t*)p_this;
116 decoder_sys_t *p_sys = p_dec->p_sys;
118 if( p_dec->fmt_in.i_codec != VLC_FOURCC('s','p','x',' ') )
123 /* Allocate the memory needed to store the decoder's structure */
124 if( ( p_dec->p_sys = p_sys =
125 (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
127 msg_Err( p_dec, "out of memory" );
130 p_dec->p_sys->b_packetizer = VLC_FALSE;
132 aout_DateSet( &p_sys->end_date, 0 );
134 /* Set output properties */
135 p_dec->fmt_out.i_cat = AUDIO_ES;
136 p_dec->fmt_out.i_codec = AOUT_FMT_S16_NE;
139 p_dec->pf_decode_audio = (aout_buffer_t *(*)(decoder_t *, block_t **))
141 p_dec->pf_packetize = (block_t *(*)(decoder_t *, block_t **))
144 p_sys->i_headers = 0;
145 p_sys->p_state = NULL;
146 p_sys->p_header = NULL;
147 p_sys->i_frame_in_packet = 0;
152 static int OpenPacketizer( vlc_object_t *p_this )
154 decoder_t *p_dec = (decoder_t*)p_this;
156 int i_ret = OpenDecoder( p_this );
158 if( i_ret == VLC_SUCCESS )
160 p_dec->p_sys->b_packetizer = VLC_TRUE;
161 p_dec->fmt_out.i_codec = VLC_FOURCC('s','p','x',' ');
167 /****************************************************************************
168 * DecodeBlock: the whole thing
169 ****************************************************************************
170 * This function must be fed with ogg packets.
171 ****************************************************************************/
172 static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
174 decoder_sys_t *p_sys = p_dec->p_sys;
175 ogg_packet oggpacket;
177 if( !pp_block ) return NULL;
181 /* Block to Ogg packet */
182 oggpacket.packet = (*pp_block)->p_buffer;
183 oggpacket.bytes = (*pp_block)->i_buffer;
187 if( p_sys->b_packetizer ) return NULL;
189 /* Block to Ogg packet */
190 oggpacket.packet = NULL;
194 oggpacket.granulepos = -1;
197 oggpacket.packetno = 0;
199 if( p_sys->i_headers == 0 )
201 /* Take care of the initial Speex header */
202 if( ProcessHeader( p_dec, &oggpacket ) != VLC_SUCCESS )
204 msg_Err( p_dec, "Initial Speex header is corrupted" );
205 block_Release( *pp_block );
211 return ProcessPacket( p_dec, &oggpacket, pp_block );
214 if( p_sys->i_headers == 1 )
216 /* The next packet in order is the comments header */
217 ParseSpeexComments( p_dec, &oggpacket );
220 return ProcessPacket( p_dec, &oggpacket, pp_block );
223 return ProcessPacket( p_dec, &oggpacket, pp_block );
226 /*****************************************************************************
227 * ProcessHeader: processes the inital Speex header packet.
228 *****************************************************************************/
229 static int ProcessHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
231 decoder_sys_t *p_sys = p_dec->p_sys;
234 SpeexHeader *p_header;
236 SpeexCallback callback;
238 p_sys->p_header = p_header =
239 speex_packet_to_header( p_oggpacket->packet, p_oggpacket->bytes );
242 msg_Err( p_dec, "Cannot read Speex header" );
245 if( p_header->mode >= SPEEX_NB_MODES )
247 msg_Err( p_dec, "Mode number %d does not (yet/any longer) exist in "
248 "this version of libspeex", p_header->mode );
252 p_mode = speex_mode_list[p_header->mode];
254 if( p_header->speex_version_id > 1 )
256 msg_Err( p_dec, "This file was encoded with Speex bit-stream "
257 "version %d, which I don't know how to decode",
258 p_header->speex_version_id );
262 if( p_mode->bitstream_version < p_header->mode_bitstream_version )
264 msg_Err( p_dec, "File encoded with a newer version of Speex" );
267 if( p_mode->bitstream_version > p_header->mode_bitstream_version )
269 msg_Err( p_dec, "File encoded with an older version of Speex" );
273 msg_Dbg( p_dec, "Speex %d Hz audio using %s mode %s%s",
274 p_header->rate, p_mode->modeName,
275 ( p_header->nb_channels == 1 ) ? " (mono" : " (stereo",
276 p_header->vbr ? ", VBR)" : ")" );
278 /* Take care of speex decoder init */
279 speex_bits_init( &p_sys->bits );
280 p_sys->p_state = p_state = speex_decoder_init( p_mode );
283 msg_Err( p_dec, "Decoder initialization failed" );
287 if( p_header->nb_channels == 2 )
289 SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
290 p_sys->stereo = stereo;
291 callback.callback_id = SPEEX_INBAND_STEREO;
292 callback.func = speex_std_stereo_request_handler;
293 callback.data = &p_sys->stereo;
294 speex_decoder_ctl( p_state, SPEEX_SET_HANDLER, &callback );
297 /* Setup the format */
298 p_dec->fmt_out.audio.i_physical_channels =
299 p_dec->fmt_out.audio.i_original_channels =
300 pi_channels_maps[p_header->nb_channels];
301 p_dec->fmt_out.audio.i_channels = p_header->nb_channels;
302 p_dec->fmt_out.audio.i_rate = p_header->rate;
304 aout_DateInit( &p_sys->end_date, p_header->rate );
309 /*****************************************************************************
310 * ProcessPacket: processes a Speex packet.
311 *****************************************************************************/
312 static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
315 decoder_sys_t *p_sys = p_dec->p_sys;
316 block_t *p_block = *pp_block;
318 /* Date management */
319 if( p_block && p_block->i_pts > 0 &&
320 p_block->i_pts != aout_DateGet( &p_sys->end_date ) )
322 aout_DateSet( &p_sys->end_date, p_block->i_pts );
325 if( !aout_DateGet( &p_sys->end_date ) )
327 /* We've just started the stream, wait for the first PTS. */
328 if( p_block ) block_Release( p_block );
332 *pp_block = NULL; /* To avoid being fed the same packet again */
334 if( p_sys->b_packetizer )
336 return SendPacket( p_dec, p_oggpacket, p_block );
340 aout_buffer_t *p_aout_buffer;
342 if( p_sys->i_headers >= p_sys->p_header->extra_headers + 2 )
343 p_aout_buffer = DecodePacket( p_dec, p_oggpacket );
345 p_aout_buffer = NULL; /* Skip headers */
349 block_Release( p_block );
351 return p_aout_buffer;
355 /*****************************************************************************
356 * DecodePacket: decodes a Speex packet.
357 *****************************************************************************/
358 static aout_buffer_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
360 decoder_sys_t *p_sys = p_dec->p_sys;
362 if( p_oggpacket->bytes )
364 /* Copy Ogg packet to Speex bitstream */
365 speex_bits_read_from( &p_sys->bits, p_oggpacket->packet,
366 p_oggpacket->bytes );
367 p_sys->i_frame_in_packet = 0;
370 /* Decode one frame at a time */
371 if( p_sys->i_frame_in_packet < p_sys->p_header->frames_per_packet )
373 aout_buffer_t *p_aout_buffer;
377 p_dec->pf_aout_buffer_new( p_dec, p_sys->p_header->frame_size );
383 i_ret = speex_decode( p_sys->p_state, &p_sys->bits,
384 (int16_t *)p_aout_buffer->p_buffer );
385 if( i_ret == -1 ) return NULL; /* End of stream */
388 msg_Warn( p_dec, "Decoding error: corrupted stream?" );
392 if( speex_bits_remaining( &p_sys->bits ) < 0 )
394 msg_Warn( p_dec, "Decoding overflow: corrupted stream?" );
397 if( p_sys->p_header->nb_channels == 2 )
398 speex_decode_stereo( (int16_t *)p_aout_buffer->p_buffer,
399 p_sys->p_header->frame_size, &p_sys->stereo );
401 /* Date management */
402 p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date );
403 p_aout_buffer->end_date =
404 aout_DateIncrement( &p_sys->end_date, p_sys->p_header->frame_size);
406 p_sys->i_frame_in_packet++;
408 return p_aout_buffer;
416 /*****************************************************************************
417 * SendPacket: send an ogg packet to the stream output.
418 *****************************************************************************/
419 static block_t *SendPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
422 decoder_sys_t *p_sys = p_dec->p_sys;
424 /* Date management */
425 p_block->i_dts = p_block->i_pts = aout_DateGet( &p_sys->end_date );
427 if( p_sys->i_headers >= p_sys->p_header->extra_headers + 2 )
429 aout_DateIncrement( &p_sys->end_date,
430 p_sys->p_header->frame_size ) -
433 p_block->i_length = 0;
438 /*****************************************************************************
439 * ParseSpeexComments: FIXME should be done in demuxer
440 *****************************************************************************/
441 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
442 ((buf[base+2]<<16)&0xff0000)| \
443 ((buf[base+1]<<8)&0xff00)| \
446 static void ParseSpeexComments( decoder_t *p_dec, ogg_packet *p_oggpacket )
448 input_thread_t *p_input = (input_thread_t *)p_dec->p_parent;
449 decoder_sys_t *p_sys = p_dec->p_sys;
451 input_info_category_t *p_cat =
452 input_InfoCategory( p_input, _("Speex Comment") );
454 char *p_buf = (char *)p_oggpacket->packet;
458 p_mode = speex_mode_list[p_sys->p_header->mode];
459 input_AddInfo( p_cat, _("Mode"), "%s%s",
460 p_mode->modeName, p_sys->p_header->vbr ? " VBR" : "" );
462 if( p_oggpacket->bytes < 8 )
464 msg_Warn( p_dec, "Invalid/corrupted comments" );
468 i_len = readint( p_buf, 0 ); p_buf += 4;
469 if( i_len > p_oggpacket->bytes - 4 )
471 msg_Warn( p_dec, "Invalid/corrupted comments" );
475 input_AddInfo( p_cat, p_buf, "" );
477 /* TODO: finish comments parsing */
480 /*****************************************************************************
481 * CloseDecoder: speex decoder destruction
482 *****************************************************************************/
483 static void CloseDecoder( vlc_object_t *p_this )
485 decoder_t * p_dec = (decoder_t *)p_this;
486 decoder_sys_t *p_sys = p_dec->p_sys;
490 speex_decoder_destroy( p_sys->p_state );
491 speex_bits_destroy( &p_sys->bits );
494 if( p_sys->p_header ) free( p_sys->p_header );