/* ***** 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 (jrecker@real.com), Ken Cooke (kenc@real.com) * June 2003 * * mp3dec.c - platform-independent top level MP3 decoder API **************************************************************************************/ #include "string.h" //#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 */ //#define PROFILE #ifdef PROFILE #include "systime.h" #endif /************************************************************************************** * 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; } /************************************************************************************** * 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; #ifdef PROFILE long time; #endif 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; #ifdef PROFILE time = systime_get(); #endif /* unpack side info */ siBytes = UnpackSideInfo(mp3DecInfo, *inbuf); if (siBytes < 0) { MP3ClearBadFrame(mp3DecInfo, outbuf); return ERR_MP3_INVALID_SIDEINFO; } *inbuf += siBytes; *bytesLeft -= (fhBytes + siBytes); #ifdef PROFILE time = systime_get() - time; printf("UnpackSideInfo: %i ms\n", time); #endif /* 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; } #ifdef PROFILE time = systime_get(); #endif /* 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; } #ifdef PROFILE time = systime_get() - time; printf("data buffer filling: %i ms\n", time); #endif } bitOffset = 0; mainBits = mp3DecInfo->mainDataBytes * 8; /* decode one complete frame */ for (gr = 0; gr < mp3DecInfo->nGrans; gr++) { for (ch = 0; ch < mp3DecInfo->nChans; ch++) { #ifdef PROFILE time = systime_get(); #endif /* unpack scale factors and compute size of scale factor block */ prevBitOffset = bitOffset; offset = UnpackScaleFactors(mp3DecInfo, mainPtr, &bitOffset, mainBits, gr, ch); #ifdef PROFILE time = systime_get() - time; printf("UnpackScaleFactors: %i ms\n", time); #endif 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; } #ifdef PROFILE time = systime_get(); #endif /* 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; } #ifdef PROFILE time = systime_get() - time; printf("Huffman: %i ms\n", time); #endif mainPtr += offset; mainBits -= (8*offset - prevBitOffset + bitOffset); } #ifdef PROFILE time = systime_get(); #endif /* dequantize coefficients, decode stereo, reorder short blocks */ if (Dequantize(mp3DecInfo, gr) < 0) { MP3ClearBadFrame(mp3DecInfo, outbuf); return ERR_MP3_INVALID_DEQUANTIZE; } #ifdef PROFILE time = systime_get() - time; printf("Dequantize: %i ms\n", time); #endif /* alias reduction, inverse MDCT, overlap-add, frequency inversion */ for (ch = 0; ch < mp3DecInfo->nChans; ch++) { #ifdef PROFILE time = systime_get(); #endif if (IMDCT(mp3DecInfo, gr, ch) < 0) { MP3ClearBadFrame(mp3DecInfo, outbuf); return ERR_MP3_INVALID_IMDCT; } #ifdef PROFILE time = systime_get() - time; printf("IMDCT: %i ms\n", time); #endif } #ifdef PROFILE time = systime_get(); #endif /* 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; } #ifdef PROFILE time = systime_get() - time; printf("Subband: %i ms\n", time); #endif } return ERR_MP3_NONE; }