]> git.sesse.net Git - vlc/blob - modules/codec/lpcm.c
Improvements to preferences
[vlc] / modules / codec / lpcm.c
1 /*****************************************************************************
2  * lpcm.c: lpcm decoder/packetizer module
3  *****************************************************************************
4  * Copyright (C) 1999-2003 VideoLAN
5  * $Id$
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_category( CAT_INPUT );
81     set_subcategory( SUBCAT_INPUT_ACODEC );
82     set_description( _("Linear PCM audio decoder") );
83     set_capability( "decoder", 100 );
84     set_callbacks( OpenDecoder, CloseDecoder );
85
86     add_submodule();
87     set_description( _("Linear PCM audio packetizer") );
88     set_capability( "packetizer", 100 );
89     set_callbacks( OpenPacketizer, CloseDecoder );
90
91 vlc_module_end();
92
93 /*****************************************************************************
94  * OpenDecoder: probe the decoder and return score
95  *****************************************************************************/
96 static int OpenDecoder( vlc_object_t *p_this )
97 {
98     decoder_t *p_dec = (decoder_t*)p_this;
99     decoder_sys_t *p_sys;
100
101     if( p_dec->fmt_in.i_codec != VLC_FOURCC('l','p','c','m')
102          && p_dec->fmt_in.i_codec != VLC_FOURCC('l','p','c','b') )
103     {   
104         return VLC_EGENERIC;
105     }
106
107     /* Allocate the memory needed to store the decoder's structure */
108     if( ( p_dec->p_sys = p_sys =
109           (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
110     {
111         msg_Err( p_dec, "out of memory" );
112         return VLC_EGENERIC;
113     }
114
115     /* Misc init */
116     p_sys->b_packetizer = VLC_FALSE;
117     aout_DateSet( &p_sys->end_date, 0 );
118
119     /* Set output properties */
120     p_dec->fmt_out.i_cat = AUDIO_ES;
121     p_dec->fmt_out.i_codec = VLC_FOURCC('s','1','6','b');
122
123     /* Set callback */
124     p_dec->pf_decode_audio = (aout_buffer_t *(*)(decoder_t *, block_t **))
125         DecodeFrame;
126     p_dec->pf_packetize    = (block_t *(*)(decoder_t *, block_t **))
127         DecodeFrame;
128
129     return VLC_SUCCESS;
130 }
131
132 static int OpenPacketizer( vlc_object_t *p_this )
133 {
134     decoder_t *p_dec = (decoder_t*)p_this;
135
136     int i_ret = OpenDecoder( p_this );
137
138     if( i_ret != VLC_SUCCESS ) return i_ret;
139
140     p_dec->p_sys->b_packetizer = VLC_TRUE;
141
142     p_dec->fmt_out.i_codec = VLC_FOURCC('l','p','c','m');
143
144     return i_ret;
145 }
146
147 /*****************************************************************************
148  * DecodeFrame: decodes an lpcm frame.
149  ****************************************************************************
150  * Beware, this function must be fed with complete frames (PES packet).
151  *****************************************************************************/
152 static void *DecodeFrame( decoder_t *p_dec, block_t **pp_block )
153 {
154     decoder_sys_t *p_sys = p_dec->p_sys;
155     block_t       *p_block;
156     unsigned int  i_rate = 0, i_original_channels = 0, i_channels = 0;
157     int           i_frame_length;
158     uint8_t       i_header;
159
160     if( !pp_block || !*pp_block ) return NULL;
161
162     p_block = *pp_block;
163     *pp_block = NULL; /* So the packet doesn't get re-sent */
164
165     /* Date management */
166     if( p_block->i_pts > 0 &&
167         p_block->i_pts != aout_DateGet( &p_sys->end_date ) )
168     {
169         aout_DateSet( &p_sys->end_date, p_block->i_pts );
170     }
171
172     if( !aout_DateGet( &p_sys->end_date ) )
173     {
174         /* We've just started the stream, wait for the first PTS. */
175         block_Release( p_block );
176         return NULL;
177     }
178
179     if( p_block->i_buffer <= LPCM_HEADER_LEN )
180     {
181         msg_Err(p_dec, "frame is too short");
182         block_Release( p_block );
183         return NULL;
184     }
185
186     i_header = p_block->p_buffer[4];
187     switch ( (i_header >> 4) & 0x3 )
188     {
189     case 0:
190         i_rate = 48000;
191         break;
192     case 1:
193         i_rate = 96000;
194         break;
195     case 2:
196         i_rate = 44100;
197         break;
198     case 3:
199         i_rate = 32000;
200         break;
201     }
202
203     i_channels = (i_header & 0x7);
204     switch ( i_channels )
205     {
206     case 0:
207         i_original_channels = AOUT_CHAN_CENTER;
208         break;
209     case 1:
210         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
211         break;
212     case 2:
213         /* This is unsure. */
214         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE;
215         break;
216     case 3:
217         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
218                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
219         break;
220     case 4:
221         /* This is unsure. */
222         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
223                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
224                                | AOUT_CHAN_LFE;
225         break;
226     case 5:
227         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
228                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
229                                | AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
230         break;
231     case 6:
232         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
233                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
234                                | AOUT_CHAN_CENTER | AOUT_CHAN_MIDDLELEFT
235                                | AOUT_CHAN_MIDDLERIGHT;
236         break;
237     case 7:
238         i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
239                                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
240                                | AOUT_CHAN_CENTER | AOUT_CHAN_MIDDLELEFT
241                                | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE;
242         break;
243     }
244
245     /* Check frame sync and drop it. */
246     if( p_block->p_buffer[5] != 0x80 )
247     {
248         msg_Warn( p_dec, "no frame sync" );
249         block_Release( p_block );
250         return NULL;
251     }
252
253     /* Set output properties */
254     if( p_dec->fmt_out.audio.i_rate != i_rate )
255     {
256         aout_DateInit( &p_sys->end_date, i_rate );
257         aout_DateSet( &p_sys->end_date, p_block->i_pts );
258     }
259     p_dec->fmt_out.audio.i_rate = i_rate;
260     p_dec->fmt_out.audio.i_channels = i_channels + 1;
261     p_dec->fmt_out.audio.i_original_channels = i_original_channels;
262     p_dec->fmt_out.audio.i_physical_channels
263         = i_original_channels & AOUT_CHAN_PHYSMASK;
264
265     i_frame_length = (p_block->i_buffer - LPCM_HEADER_LEN) /
266         ( p_dec->fmt_out.audio.i_channels * 2 );
267
268     if( p_sys->b_packetizer )
269     {
270         p_block->i_pts = p_block->i_dts = aout_DateGet( &p_sys->end_date );
271         p_block->i_length =
272             aout_DateIncrement( &p_sys->end_date, i_frame_length ) -
273             p_block->i_pts;
274
275         /* Just pass on the incoming frame */
276         return p_block;
277     }
278     else
279     {
280         aout_buffer_t *p_aout_buffer;
281         p_aout_buffer = p_dec->pf_aout_buffer_new( p_dec, i_frame_length );
282         if( p_aout_buffer == NULL ) return NULL;
283
284         p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date );
285         p_aout_buffer->end_date =
286             aout_DateIncrement( &p_sys->end_date, i_frame_length );
287
288         memcpy( p_aout_buffer->p_buffer,
289                 p_block->p_buffer + LPCM_HEADER_LEN,
290                 p_block->i_buffer - LPCM_HEADER_LEN );
291
292         block_Release( p_block );
293         return p_aout_buffer;
294     }
295 }
296
297 /*****************************************************************************
298  * CloseDecoder : lpcm decoder destruction
299  *****************************************************************************/
300 static void CloseDecoder( vlc_object_t *p_this )
301 {
302     decoder_t *p_dec = (decoder_t*)p_this;
303     free( p_dec->p_sys );
304 }