-
Notifications
You must be signed in to change notification settings - Fork 5
/
mp3dec.c
417 lines (378 loc) · 15.9 KB
/
mp3dec.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
*
* The contents of this file, and the files included with this file, are
* subject to the current version of the RealNetworks Public Source License
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
* in which case the RCSL will apply. You may also obtain the license terms
* directly from RealNetworks. You may not use this file except in
* compliance with the RPSL or, if you have a valid RCSL with RealNetworks
* applicable to this file, the RCSL. Please see the applicable RPSL or
* RCSL for the rights, obligations and limitations governing use of the
* contents of the file.
*
* This file is part of the Helix DNA Technology. RealNetworks is the
* developer of the Original Code and owns the copyrights in the portions
* it created.
*
* This file, and the files included with this file, is distributed and made
* available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Technology Compatibility Kit Test Suite(s) Location:
* http://www.helixcommunity.org/content/tck
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
/**************************************************************************************
* Fixed-point MP3 decoder
* Jon Recker ([email protected]), Ken Cooke ([email protected])
* June 2003
*
* mp3dec.c - platform-independent top level MP3 decoder API
**************************************************************************************/
#include "string.h" // J.Sz. 21/04/2006
// #include "hlxclib/string.h" /* for memmove, memcpy (can replace with different implementations if desired) */
#include "mp3common.h" /* includes mp3dec.h (public API) and internal, platform-independent API */
/**************************************************************************************
* Function: MP3InitDecoder
*
* Description: allocate memory for platform-specific data
* clear all the user-accessible fields
*
* Inputs: none
*
* Outputs: none
*
* Return: handle to mp3 decoder instance, 0 if malloc fails
**************************************************************************************/
HMP3Decoder MP3InitDecoder(void)
{
MP3DecInfo *mp3DecInfo;
mp3DecInfo = AllocateBuffers();
return (HMP3Decoder)mp3DecInfo;
}
/**************************************************************************************
* Function: MP3FreeDecoder
*
* Description: free platform-specific data allocated by InitMP3Decoder
* zero out the contents of MP3DecInfo struct
*
* Inputs: valid MP3 decoder instance pointer (HMP3Decoder)
*
* Outputs: none
*
* Return: none
**************************************************************************************/
void MP3FreeDecoder(HMP3Decoder hMP3Decoder)
{
MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder;
if (!mp3DecInfo)
return;
FreeBuffers(mp3DecInfo);
}
/**************************************************************************************
* Function: MP3FindSyncWord
*
* Description: locate the next byte-alinged sync word in the raw mp3 stream
*
* Inputs: buffer to search for sync word
* max number of bytes to search in buffer
*
* Outputs: none
*
* Return: offset to first sync word (bytes from start of buf)
* -1 if sync not found after searching nBytes
**************************************************************************************/
int MP3FindSyncWord(unsigned char *buf, int nBytes)
{
int i;
/* find byte-aligned syncword - need 12 (MPEG 1,2) or 11 (MPEG 2.5) matching bits */
for (i = 0; i < nBytes - 1; i++) {
if ( (buf[i+0] & SYNCWORDH) == SYNCWORDH && (buf[i+1] & SYNCWORDL) == SYNCWORDL )
return i;
}
return -1;
}
/**************************************************************************************
* Function: MP3FindFreeSync
*
* Description: figure out number of bytes between adjacent sync words in "free" mode
*
* Inputs: buffer to search for next sync word
* the 4-byte frame header starting at the current sync word
* max number of bytes to search in buffer
*
* Outputs: none
*
* Return: offset to next sync word, minus any pad byte (i.e. nSlots)
* -1 if sync not found after searching nBytes
*
* Notes: this checks that the first 22 bits of the next frame header are the
* same as the current frame header, but it's still not foolproof
* (could accidentally find a sequence in the bitstream which
* appears to match but is not actually the next frame header)
* this could be made more error-resilient by checking several frames
* in a row and verifying that nSlots is the same in each case
* since free mode requires CBR (see spec) we generally only call
* this function once (first frame) then store the result (nSlots)
* and just use it from then on
**************************************************************************************/
static int MP3FindFreeSync(unsigned char *buf, unsigned char firstFH[4], int nBytes)
{
int offset = 0;
unsigned char *bufPtr = buf;
/* loop until we either:
* - run out of nBytes (FindMP3SyncWord() returns -1)
* - find the next valid frame header (sync word, version, layer, CRC flag, bitrate, and sample rate
* in next header must match current header)
*/
while (1) {
offset = MP3FindSyncWord(bufPtr, nBytes);
bufPtr += offset;
if (offset < 0) {
return -1;
} else if ( (bufPtr[0] == firstFH[0]) && (bufPtr[1] == firstFH[1]) && ((bufPtr[2] & 0xfc) == (firstFH[2] & 0xfc)) ) {
/* want to return number of bytes per frame, NOT counting the padding byte, so subtract one if padFlag == 1 */
if ((firstFH[2] >> 1) & 0x01)
bufPtr--;
return bufPtr - buf;
}
bufPtr += 3;
nBytes -= (offset + 3);
};
// return -1; Removed KJ
}
/**************************************************************************************
* Function: MP3GetLastFrameInfo
*
* Description: get info about last MP3 frame decoded (number of sampled decoded,
* sample rate, bitrate, etc.)
*
* Inputs: valid MP3 decoder instance pointer (HMP3Decoder)
* pointer to MP3FrameInfo struct
*
* Outputs: filled-in MP3FrameInfo struct
*
* Return: none
*
* Notes: call this right after calling MP3Decode
**************************************************************************************/
void MP3GetLastFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo)
{
MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder;
if (!mp3DecInfo || mp3DecInfo->layer != 3) {
mp3FrameInfo->bitrate = 0;
mp3FrameInfo->nChans = 0;
mp3FrameInfo->samprate = 0;
mp3FrameInfo->bitsPerSample = 0;
mp3FrameInfo->outputSamps = 0;
mp3FrameInfo->layer = 0;
mp3FrameInfo->version = 0;
} else {
mp3FrameInfo->bitrate = mp3DecInfo->bitrate;
mp3FrameInfo->nChans = mp3DecInfo->nChans;
mp3FrameInfo->samprate = mp3DecInfo->samprate;
mp3FrameInfo->bitsPerSample = 16;
mp3FrameInfo->outputSamps = mp3DecInfo->nChans * (int)samplesPerFrameTab[mp3DecInfo->version][mp3DecInfo->layer - 1];
mp3FrameInfo->layer = mp3DecInfo->layer;
mp3FrameInfo->version = mp3DecInfo->version;
}
}
/**************************************************************************************
* Function: MP3GetNextFrameInfo
*
* Description: parse MP3 frame header
*
* Inputs: valid MP3 decoder instance pointer (HMP3Decoder)
* pointer to MP3FrameInfo struct
* pointer to buffer containing valid MP3 frame header (located using
* MP3FindSyncWord(), above)
*
* Outputs: filled-in MP3FrameInfo struct
*
* Return: error code, defined in mp3dec.h (0 means no error, < 0 means error)
**************************************************************************************/
int MP3GetNextFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo, unsigned char *buf)
{
MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder;
if (!mp3DecInfo)
return ERR_MP3_NULL_POINTER;
if (UnpackFrameHeader(mp3DecInfo, buf) == -1 || mp3DecInfo->layer != 3)
return ERR_MP3_INVALID_FRAMEHEADER;
MP3GetLastFrameInfo(mp3DecInfo, mp3FrameInfo);
return ERR_MP3_NONE;
}
/**************************************************************************************
* Function: MP3ClearBadFrame
*
* Description: zero out pcm buffer if error decoding MP3 frame
*
* Inputs: mp3DecInfo struct with correct frame size parameters filled in
* pointer pcm output buffer
*
* Outputs: zeroed out pcm buffer
*
* Return: none
**************************************************************************************/
static void MP3ClearBadFrame(MP3DecInfo *mp3DecInfo, short *outbuf)
{
int i;
if (!mp3DecInfo)
return;
for (i = 0; i < mp3DecInfo->nGrans * mp3DecInfo->nGranSamps * mp3DecInfo->nChans; i++)
outbuf[i] = 0;
}
/**************************************************************************************
* Function: MP3Decode
*
* Description: decode one frame of MP3 data
*
* Inputs: valid MP3 decoder instance pointer (HMP3Decoder)
* double pointer to buffer of MP3 data (containing headers + mainData)
* number of valid bytes remaining in inbuf
* pointer to outbuf, big enough to hold one frame of decoded PCM samples
* flag indicating whether MP3 data is normal MPEG format (useSize = 0)
* or reformatted as "self-contained" frames (useSize = 1)
*
* Outputs: PCM data in outbuf, interleaved LRLRLR... if stereo
* number of output samples = nGrans * nGranSamps * nChans
* updated inbuf pointer, updated bytesLeft
*
* Return: error code, defined in mp3dec.h (0 means no error, < 0 means error)
*
* Notes: switching useSize on and off between frames in the same stream
* is not supported (bit reservoir is not maintained if useSize on)
**************************************************************************************/
int MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, short *outbuf, int useSize)
{
int offset, bitOffset, mainBits, gr, ch, fhBytes, siBytes, freeFrameBytes;
int prevBitOffset, sfBlockBits, huffBlockBits;
unsigned char *mainPtr;
MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder;
if (!mp3DecInfo)
return ERR_MP3_NULL_POINTER;
/* unpack frame header */
fhBytes = UnpackFrameHeader(mp3DecInfo, *inbuf);
if (fhBytes < 0)
return ERR_MP3_INVALID_FRAMEHEADER; /* don't clear outbuf since we don't know size (failed to parse header) */
*inbuf += fhBytes;
/* unpack side info */
siBytes = UnpackSideInfo(mp3DecInfo, *inbuf);
if (siBytes < 0) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INVALID_SIDEINFO;
}
*inbuf += siBytes;
*bytesLeft -= (fhBytes + siBytes);
/* if free mode, need to calculate bitrate and nSlots manually, based on frame size */
if (mp3DecInfo->bitrate == 0 || mp3DecInfo->freeBitrateFlag) {
if (!mp3DecInfo->freeBitrateFlag) {
/* first time through, need to scan for next sync word and figure out frame size */
mp3DecInfo->freeBitrateFlag = 1;
mp3DecInfo->freeBitrateSlots = MP3FindFreeSync(*inbuf, *inbuf - fhBytes - siBytes, *bytesLeft);
if (mp3DecInfo->freeBitrateSlots < 0) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_FREE_BITRATE_SYNC;
}
freeFrameBytes = mp3DecInfo->freeBitrateSlots + fhBytes + siBytes;
mp3DecInfo->bitrate = (freeFrameBytes * mp3DecInfo->samprate * 8) / (mp3DecInfo->nGrans * mp3DecInfo->nGranSamps);
}
mp3DecInfo->nSlots = mp3DecInfo->freeBitrateSlots + CheckPadBit(mp3DecInfo); /* add pad byte, if required */
}
/* useSize != 0 means we're getting reformatted (RTP) packets (see RFC 3119)
* - calling function assembles "self-contained" MP3 frames by shifting any main_data
* from the bit reservoir (in previous frames) to AFTER the sync word and side info
* - calling function should set mainDataBegin to 0, and tell us exactly how large this
* frame is (in bytesLeft)
*/
if (useSize) {
mp3DecInfo->nSlots = *bytesLeft;
if (mp3DecInfo->mainDataBegin != 0 || mp3DecInfo->nSlots <= 0) {
/* error - non self-contained frame, or missing frame (size <= 0), could do loss concealment here */
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INVALID_FRAMEHEADER;
}
/* can operate in-place on reformatted frames */
mp3DecInfo->mainDataBytes = mp3DecInfo->nSlots;
mainPtr = *inbuf;
*inbuf += mp3DecInfo->nSlots;
*bytesLeft -= (mp3DecInfo->nSlots);
} else {
/* out of data - assume last or truncated frame */
if (mp3DecInfo->nSlots > *bytesLeft) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INDATA_UNDERFLOW;
}
/* fill main data buffer with enough new data for this frame */
if (mp3DecInfo->mainDataBytes >= mp3DecInfo->mainDataBegin) {
/* adequate "old" main data available (i.e. bit reservoir) */
memmove(mp3DecInfo->mainBuf, mp3DecInfo->mainBuf + mp3DecInfo->mainDataBytes - mp3DecInfo->mainDataBegin, mp3DecInfo->mainDataBegin);
memcpy(mp3DecInfo->mainBuf + mp3DecInfo->mainDataBegin, *inbuf, mp3DecInfo->nSlots);
mp3DecInfo->mainDataBytes = mp3DecInfo->mainDataBegin + mp3DecInfo->nSlots;
*inbuf += mp3DecInfo->nSlots;
*bytesLeft -= (mp3DecInfo->nSlots);
mainPtr = mp3DecInfo->mainBuf;
} else {
/* not enough data in bit reservoir from previous frames (perhaps starting in middle of file) */
memcpy(mp3DecInfo->mainBuf + mp3DecInfo->mainDataBytes, *inbuf, mp3DecInfo->nSlots);
mp3DecInfo->mainDataBytes += mp3DecInfo->nSlots;
*inbuf += mp3DecInfo->nSlots;
*bytesLeft -= (mp3DecInfo->nSlots);
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_MAINDATA_UNDERFLOW;
}
}
bitOffset = 0;
mainBits = mp3DecInfo->mainDataBytes * 8;
/* decode one complete frame */
for (gr = 0; gr < mp3DecInfo->nGrans; gr++) {
for (ch = 0; ch < mp3DecInfo->nChans; ch++) {
/* unpack scale factors and compute size of scale factor block */
prevBitOffset = bitOffset;
offset = UnpackScaleFactors(mp3DecInfo, mainPtr, &bitOffset, mainBits, gr, ch);
sfBlockBits = 8*offset - prevBitOffset + bitOffset;
huffBlockBits = mp3DecInfo->part23Length[gr][ch] - sfBlockBits;
mainPtr += offset;
mainBits -= sfBlockBits;
if (offset < 0 || mainBits < huffBlockBits) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INVALID_SCALEFACT;
}
/* decode Huffman code words */
prevBitOffset = bitOffset;
offset = DecodeHuffman(mp3DecInfo, mainPtr, &bitOffset, huffBlockBits, gr, ch);
if (offset < 0) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INVALID_HUFFCODES;
}
mainPtr += offset;
mainBits -= (8*offset - prevBitOffset + bitOffset);
}
/* dequantize coefficients, decode stereo, reorder short blocks */
if (Dequantize(mp3DecInfo, gr) < 0) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INVALID_DEQUANTIZE;
}
/* alias reduction, inverse MDCT, overlap-add, frequency inversion */
for (ch = 0; ch < mp3DecInfo->nChans; ch++)
if (IMDCT(mp3DecInfo, gr, ch) < 0) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INVALID_IMDCT;
}
/* subband transform - if stereo, interleaves pcm LRLRLR */
if (Subband(mp3DecInfo, outbuf + gr*mp3DecInfo->nGranSamps*mp3DecInfo->nChans) < 0) {
MP3ClearBadFrame(mp3DecInfo, outbuf);
return ERR_MP3_INVALID_SUBBAND;
}
}
return ERR_MP3_NONE;
}