]> git.sesse.net Git - vlc/blob - modules/codec/lpcm.c
string review
[vlc] / modules / codec / lpcm.c
1 /*****************************************************************************
2  * lpcm.c: lpcm decoder/packetizer module
3  *****************************************************************************
4  * Copyright (C) 1999-2003 VideoLAN
5  * $Id: lpcm.c,v 1.21 2004/01/25 18:20:12 bigben Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Henri Fallon <henri@videolan.org>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *          Gildas Bazin <gbazin@netcourrier.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <vlc/vlc.h>
31 #include <vlc/decoder.h>
32
33 /*****************************************************************************
34  * decoder_sys_t : lpcm decoder descriptor
35  *****************************************************************************/
36 struct decoder_sys_t
37 {
38     /* Module mode */
39     vlc_bool_t b_packetizer;
40
41     /*
42      * Output properties
43      */
44     audio_date_t end_date;
45
46 };
47
48 /*
49  * LPCM header :
50  * - PES header
51  * - private stream ID (16 bits) == 0xA0 -> not in the bitstream
52  *
53  * - frame number (8 bits)
54  * - unknown (16 bits) == 0x0003 ?
55  * - unknown (4 bits)
56  * - current frame (4 bits)
57  * - unknown (2 bits)
58  * - frequency (2 bits) 0 == 48 kHz, 1 == 32 kHz, 2 == ?, 3 == ?
59  * - unknown (1 bit)
60  * - number of channels - 1 (3 bits) 1 == 2 channels
61  * - start code (8 bits) == 0x80
62  */
63
64 #define LPCM_HEADER_LEN 6
65
66 /*****************************************************************************
67  * Local prototypes
68  *****************************************************************************/
69 static int  OpenDecoder   ( vlc_object_t * );
70 static int  OpenPacketizer( vlc_object_t * );
71 static void CloseDecoder  ( vlc_object_t * );
72
73 static void *DecodeFrame  ( decoder_t *, block_t ** );
74
75 /*****************************************************************************
76  * Module descriptor
77  *****************************************************************************/
78 vlc_module_begin();
79
80     set_description( _("Linear PCM audio decoder") );
81     set_capability( "decoder", 100 );
82     set_callbacks( OpenDecoder, CloseDecoder );
83
84     add_submodule();
85     set_description( _("Linear PCM audio packetizer") );
86     set_capability( "packetizer", 100 );
87     set_callbacks( OpenPacketizer, CloseDecoder );
88
89 vlc_module_end();
90
91 /*****************************************************************************
92  * OpenDecoder: probe the decoder and return score
93  *****************************************************************************/
94 static int OpenDecoder( vlc_object_t *p_this )
95 {
96     decoder_t *p_dec = (decoder_t*)p_this;
97     decoder_sys_t *p_sys;
98
99     if( p_dec->fmt_in.i_codec != VLC_FOURCC('l','p','c','m')
100          && p_dec->fmt_in.i_codec != VLC_FOURCC('l','p','c','b') )
101     {   
102         return VLC_EGENERIC;
103     }
104
105     /* Allocate the memory needed to store the decoder's structure */
106     if( ( p_dec->p_sys = p_sys =
107           (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
108     {
109         msg_Err( p_dec, "out of memory" );
110         return VLC_EGENERIC;
111     }
112
113     /* Misc init */
114     p_sys->b_packetizer = VLC_FALSE;
115     aout_DateSet( &p_sys->end_date, 0 );
116
117     /* Set output properties */
118     p_dec->fmt_out.i_cat = AUDIO_ES;
119     p_dec->fmt_out.i_codec = VLC_FOURCC('s','1','6','b');
120
121     /* Set callback */
122     p_dec->pf_decode_audio = (aout_buffer_t *(*)(decoder_t *, block_t **))
123         DecodeFrame;
124     p_dec->pf_packetize    = (block_t *(*)(decoder_t *, block_t **))
125         DecodeFrame;
126
127     return VLC_SUCCESS;
128 }
129
130 static int OpenPacketizer( vlc_object_t *p_this )
131 {
132     decoder_t *p_dec = (decoder_t*)p_this;
133
134     int i_ret = OpenDecoder( p_this );
135
136     if( i_ret != VLC_SUCCESS ) return i_ret;
137
138     p_dec->p_sys->b_packetizer = VLC_TRUE;
139
140     p_dec->fmt_out.i_codec = VLC_FOURCC('l','p','c','m');
141
142     return i_ret;
143 }
144
145 /*****************************************************************************
146  * DecodeFrame: decodes an lpcm frame.
147  ****************************************************************************
148  * Beware, this function must be fed with complete frames (PES packet).
149  *****************************************************************************/
150 static void *DecodeFrame( decoder_t *p_dec, block_t **pp_block )
151 {
152     decoder_sys_t *p_sys = p_dec->p_sys;
153     block_t       *p_block;
154     unsigned int  i_rate = 0, i_original_channels = 0, i_channels = 0;
155     int           i_frame_length;
156     uint8_t       i_header;
157
158     if( !pp_block || !*pp_block ) return NULL;
159
160     p_block = *pp_block;
161     *pp_block = NULL; /* So the packet doesn't get re-sent */
162
163     /* Date management */
164     if( p_block->i_pts > 0 &&
165         p_block->i_pts != aout_DateGet( &p_sys->end_date ) )
166     {
167         aout_DateSet( &p_sys->end_date, p_block->i_pts );
168     }
169
170     if( !aout_DateGet( &p_sys->end_date ) )
171     {
172         /* We've just started the stream, wait for the first PTS. */
173         block_Release( p_block );
174         return NULL;
175     }
176
177     if( p_block->i_buffer <= LPCM_HEADER_LEN )
178     {
179         msg_Err(p_dec, "frame is too short");
180         block_Release( p_block );
181         return NULL;
182     }
183
184     i_header = p_block->p_buffer[4];
185     switch ( (i_header >> 4) & 0x3 )
186     {
187     case 0:
188         i_rate = 48000;
189         break;
190     case 1:
191         i_rate = 96000;
192         break;
193     case 2:
194         i_rate = 44100;
195         break;
196     case 3:
197         i_rate = 32000;
198         break;
199     }
200
201     i_channels = (i_header & 0x7);
202     switch ( i_channels )
203     {
204     case 0:
205         i_original_channels = AOUT_CHAN_CENTER;
206         break;
207     case 1:
208         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
209         break;
210     case 2:
211         /* This is unsure. */
212         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE;
213         break;
214     case 3:
215         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
216                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
217         break;
218     case 4:
219         /* This is unsure. */
220         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
221                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
222                                | AOUT_CHAN_LFE;
223         break;
224     case 5:
225         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
226                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
227                                | AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
228         break;
229     case 6:
230         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
231                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
232                                | AOUT_CHAN_CENTER | AOUT_CHAN_MIDDLELEFT
233                                | AOUT_CHAN_MIDDLERIGHT;
234         break;
235     case 7:
236         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
237                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
238                                | AOUT_CHAN_CENTER | AOUT_CHAN_MIDDLELEFT
239                                | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE;
240         break;
241     }
242
243     /* Check frame sync and drop it. */
244     if( p_block->p_buffer[5] != 0x80 )
245     {
246         msg_Warn( p_dec, "no frame sync" );
247         block_Release( p_block );
248         return NULL;
249     }
250
251     /* Set output properties */
252     if( p_dec->fmt_out.audio.i_rate != i_rate )
253     {
254         aout_DateInit( &p_sys->end_date, i_rate );
255         aout_DateSet( &p_sys->end_date, p_block->i_pts );
256     }
257     p_dec->fmt_out.audio.i_rate = i_rate;
258     p_dec->fmt_out.audio.i_channels = i_channels + 1;
259     p_dec->fmt_out.audio.i_original_channels = i_original_channels;
260     p_dec->fmt_out.audio.i_physical_channels
261         = i_original_channels & AOUT_CHAN_PHYSMASK;
262
263     i_frame_length = (p_block->i_buffer - LPCM_HEADER_LEN) /
264         ( p_dec->fmt_out.audio.i_channels * 2 );
265
266     if( p_sys->b_packetizer )
267     {
268         p_block->i_pts = p_block->i_dts = aout_DateGet( &p_sys->end_date );
269         p_block->i_length =
270             aout_DateIncrement( &p_sys->end_date, i_frame_length ) -
271             p_block->i_pts;
272
273         /* Just pass on the incoming frame */
274         return p_block;
275     }
276     else
277     {
278         aout_buffer_t *p_aout_buffer;
279         p_aout_buffer = p_dec->pf_aout_buffer_new( p_dec, i_frame_length );
280         if( p_aout_buffer == NULL ) return NULL;
281
282         p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date );
283         p_aout_buffer->end_date =
284             aout_DateIncrement( &p_sys->end_date, i_frame_length );
285
286         memcpy( p_aout_buffer->p_buffer,
287                 p_block->p_buffer + LPCM_HEADER_LEN,
288                 p_block->i_buffer - LPCM_HEADER_LEN );
289
290         block_Release( p_block );
291         return p_aout_buffer;
292     }
293 }
294
295 /*****************************************************************************
296  * CloseDecoder : lpcm decoder destruction
297  *****************************************************************************/
298 static void CloseDecoder( vlc_object_t *p_this )
299 {
300     decoder_t *p_dec = (decoder_t*)p_this;
301     free( p_dec->p_sys );
302 }