/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. (See .) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********/ // "liveMedia" // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. // MP3 internal implementation details // Implementation #include "MP3InternalsHuffman.hh" #include #include #include #include // This is crufty old code that needs to be cleaned up ##### static unsigned const live_tabsel[2][3][16] = { { {32,32,64,96,128,160,192,224,256,288,320,352,384,416,448,448}, {32,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,384}, {32,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,320} }, { {32,32,48,56,64,80,96,112,128,144,160,176,192,224,256,256}, {8,8,16,24,32,40,48,56,64,80,96,112,128,144,160,160}, {8,8,16,24,32,40,48,56,64,80,96,112,128,144,160,160} } }; /* Note: live_tabsel[*][*][0 or 15] shouldn't occur; use dummy values there */ static long const live_freqs[] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000, 0 }; struct bandInfoStruct { int longIdx[23]; int longDiff[22]; int shortIdx[14]; int shortDiff[13]; }; static struct bandInfoStruct const bandInfo[7] = { /* MPEG 1.0 */ { {0,4,8,12,16,20,24,30,36,44,52,62,74, 90,110,134,162,196,238,288,342,418,576}, {4,4,4,4,4,4,6,6,8, 8,10,12,16,20,24,28,34,42,50,54, 76,158}, {0,4*3,8*3,12*3,16*3,22*3,30*3,40*3,52*3,66*3, 84*3,106*3,136*3,192*3}, {4,4,4,4,6,8,10,12,14,18,22,30,56} } , { {0,4,8,12,16,20,24,30,36,42,50,60,72, 88,106,128,156,190,230,276,330,384,576}, {4,4,4,4,4,4,6,6,6, 8,10,12,16,18,22,28,34,40,46,54, 54,192}, {0,4*3,8*3,12*3,16*3,22*3,28*3,38*3,50*3,64*3, 80*3,100*3,126*3,192*3}, {4,4,4,4,6,6,10,12,14,16,20,26,66} } , { {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576} , {4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102, 26} , {0,4*3,8*3,12*3,16*3,22*3,30*3,42*3,58*3,78*3,104*3,138*3,180*3,192*3} , {4,4,4,4,6,8,12,16,20,26,34,42,12} } , /* MPEG 2.0 */ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } , {0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} , {4,4,4,6,6,8,10,14,18,26,32,42,18 } } , { {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576}, {6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,52,64,70,76,36 } , {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,136*3,180*3,192*3} , {4,4,4,6,8,10,12,14,18,24,32,44,12 } } , { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 }, {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,134*3,174*3,192*3}, {4,4,4,6,8,10,12,14,18,24,30,40,18 } } , /* MPEG 2.5, wrong! table (it's just a copy of MPEG 2.0/44.1kHz) */ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } , {0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} , {4,4,4,6,6,8,10,14,18,26,32,42,18 } } , }; unsigned int n_slen2[512]; /* MPEG 2.0 slen for 'normal' mode */ unsigned int i_slen2[256]; /* MPEG 2.0 slen for intensity stereo */ #define MPG_MD_MONO 3 ////////// MP3FrameParams ////////// MP3FrameParams::MP3FrameParams() : bv(frameBytes, 0, sizeof frameBytes) /* by default */ { oldHdr = firstHdr = 0; static Boolean doneInit = False; if (doneInit) return; int i,j,k,l; for (i=0;i<5;i++) { for (j=0;j<6;j++) { for (k=0;k<6;k++) { int n = k + j * 6 + i * 36; i_slen2[n] = i|(j<<3)|(k<<6)|(3<<12); } } } for (i=0;i<4;i++) { for (j=0;j<4;j++) { for (k=0;k<4;k++) { int n = k + j * 4 + i * 16; i_slen2[n+180] = i|(j<<3)|(k<<6)|(4<<12); } } } for (i=0;i<4;i++) { for (j=0;j<3;j++) { int n = j + i * 3; i_slen2[n+244] = i|(j<<3) | (5<<12); n_slen2[n+500] = i|(j<<3) | (2<<12) | (1<<15); } } for (i=0;i<5;i++) { for (j=0;j<5;j++) { for (k=0;k<4;k++) { for (l=0;l<4;l++) { int n = l + k * 4 + j * 16 + i * 80; n_slen2[n] = i|(j<<3)|(k<<6)|(l<<9)|(0<<12); } } } } for (i=0;i<5;i++) { for (j=0;j<5;j++) { for (k=0;k<4;k++) { int n = k + j * 4 + i * 20; n_slen2[n+400] = i|(j<<3)|(k<<6)|(1<<12); } } } doneInit = True; } MP3FrameParams::~MP3FrameParams() { } void MP3FrameParams::setParamsFromHeader() { if (hdr & (1<<20)) { isMPEG2 = (hdr & (1<<19)) ? 0x0 : 0x1; isMPEG2_5 = 0; } else { isMPEG2 = 1; isMPEG2_5 = 1; } layer = 4-((hdr>>17)&3); if (layer == 4) layer = 3; // layer==4 is not allowed bitrateIndex = ((hdr>>12)&0xf); if (isMPEG2_5) { samplingFreqIndex = ((hdr>>10)&0x3) + 6; } else { samplingFreqIndex = ((hdr>>10)&0x3) + (isMPEG2*3); } hasCRC = (hdr & 0x10000) == 0; padding = ((hdr>>9)&0x1); extension = ((hdr>>8)&0x1); mode = ((hdr>>6)&0x3); mode_ext = ((hdr>>4)&0x3); copyright = ((hdr>>3)&0x1); original = ((hdr>>2)&0x1); emphasis = hdr & 0x3; stereo = (mode == MPG_MD_MONO) ? 1 : 2; if (((hdr>>10)&0x3) == 0x3) { #ifdef DEBUG_ERRORS fprintf(stderr,"Stream error - hdr: 0x%08x\n", hdr); #endif } bitrate = live_tabsel[isMPEG2][layer-1][bitrateIndex]; samplingFreq = live_freqs[samplingFreqIndex]; isStereo = (stereo > 1); isFreeFormat = (bitrateIndex == 0); frameSize = ComputeFrameSize(bitrate, samplingFreq, padding, isMPEG2, layer); sideInfoSize = computeSideInfoSize(); } unsigned MP3FrameParams::computeSideInfoSize() { unsigned size; if (isMPEG2) { size = isStereo ? 17 : 9; } else { size = isStereo ? 32 : 17; } if (hasCRC) { size += 2; } return size; } unsigned ComputeFrameSize(unsigned bitrate, unsigned samplingFreq, Boolean usePadding, Boolean isMPEG2, unsigned char layer) { if (samplingFreq == 0) return 0; unsigned const bitrateMultiplier = (layer == 1) ? 12000*4 : 144000; unsigned framesize; framesize = bitrate*bitrateMultiplier; framesize /= samplingFreq<<(isMPEG2 ? 1 : 0); framesize = framesize + usePadding - 4; return framesize; } #define TRUNC_FAIRLY static unsigned updateSideInfoSizes(MP3SideInfo& sideInfo, Boolean isMPEG2, unsigned char const* mainDataPtr, unsigned allowedNumBits, unsigned& part23Length0a, unsigned& part23Length0aTruncation, unsigned& part23Length0b, unsigned& part23Length0bTruncation, unsigned& part23Length1a, unsigned& part23Length1aTruncation, unsigned& part23Length1b, unsigned& part23Length1bTruncation) { unsigned p23L0, p23L1 = 0, p23L0Trunc = 0, p23L1Trunc = 0; p23L0 = sideInfo.ch[0].gr[0].part2_3_length; p23L1 = isMPEG2 ? 0 : sideInfo.ch[0].gr[1].part2_3_length; #ifdef TRUNC_ONLY0 if (p23L0 < allowedNumBits) allowedNumBits = p23L0; #endif #ifdef TRUNC_ONLY1 if (p23L1 < allowedNumBits) allowedNumBits = p23L1; #endif if (p23L0 + p23L1 > allowedNumBits) { /* We need to shorten one or both fields */ unsigned truncation = p23L0 + p23L1 - allowedNumBits; #ifdef TRUNC_FAIRLY p23L0Trunc = (truncation*p23L0)/(p23L0 + p23L1); p23L1Trunc = truncation - p23L0Trunc; #endif #if defined(TRUNC_FAVOR0) || defined(TRUNC_ONLY0) p23L1Trunc = (truncation>p23L1) ? p23L1 : truncation; p23L0Trunc = truncation - p23L1Trunc; #endif #if defined(TRUNC_FAVOR1) || defined(TRUNC_ONLY1) p23L0Trunc = (truncation>p23L0) ? p23L0 : truncation; p23L1Trunc = truncation - p23L0Trunc; #endif } /* ASSERT: (p23L0Trunc <= p23L0) && (p23l1Trunc <= p23L1) */ p23L0 -= p23L0Trunc; p23L1 -= p23L1Trunc; #ifdef DEBUG fprintf(stderr, "updateSideInfoSizes (allowed: %d): %d->%d, %d->%d\n", allowedNumBits, p23L0+p23L0Trunc, p23L0, p23L1+p23L1Trunc, p23L1); #endif // The truncations computed above are still estimates. We need to // adjust them so that the new fields will continue to end on // Huffman-encoded sample boundaries: updateSideInfoForHuffman(sideInfo, isMPEG2, mainDataPtr, p23L0, p23L1, part23Length0a, part23Length0aTruncation, part23Length0b, part23Length0bTruncation, part23Length1a, part23Length1aTruncation, part23Length1b, part23Length1bTruncation); p23L0 = part23Length0a + part23Length0b; p23L1 = part23Length1a + part23Length1b; sideInfo.ch[0].gr[0].part2_3_length = p23L0; sideInfo.ch[0].gr[1].part2_3_length = p23L1; part23Length0bTruncation += sideInfo.ch[1].gr[0].part2_3_length; /* allow for stereo */ sideInfo.ch[1].gr[0].part2_3_length = 0; /* output mono */ sideInfo.ch[1].gr[1].part2_3_length = 0; /* output mono */ return p23L0 + p23L1; } Boolean GetADUInfoFromMP3Frame(unsigned char const* framePtr, unsigned totFrameSize, unsigned& hdr, unsigned& frameSize, MP3SideInfo& sideInfo, unsigned& sideInfoSize, unsigned& backpointer, unsigned& aduSize) { if (totFrameSize < 4) return False; // there's not enough data MP3FrameParams fr; fr.hdr = ((unsigned)framePtr[0] << 24) | ((unsigned)framePtr[1] << 16) | ((unsigned)framePtr[2] << 8) | (unsigned)framePtr[3]; fr.setParamsFromHeader(); fr.setBytePointer(framePtr + 4, totFrameSize - 4); // skip hdr frameSize = 4 + fr.frameSize; if (fr.layer != 3) { // Special case for non-layer III frames backpointer = 0; sideInfoSize = 0; aduSize = fr.frameSize; return True; } sideInfoSize = fr.sideInfoSize; if (totFrameSize < 4 + sideInfoSize) return False; // not enough data fr.getSideInfo(sideInfo); hdr = fr.hdr; backpointer = sideInfo.main_data_begin; unsigned numBits = sideInfo.ch[0].gr[0].part2_3_length; numBits += sideInfo.ch[0].gr[1].part2_3_length; numBits += sideInfo.ch[1].gr[0].part2_3_length; numBits += sideInfo.ch[1].gr[1].part2_3_length; aduSize = (numBits+7)/8; #ifdef DEBUG fprintf(stderr, "mp3GetADUInfoFromFrame: hdr: %08x, frameSize: %d, part2_3_lengths: %d,%d,%d,%d, aduSize: %d, backpointer: %d\n", hdr, frameSize, sideInfo.ch[0].gr[0].part2_3_length, sideInfo.ch[0].gr[1].part2_3_length, sideInfo.ch[1].gr[0].part2_3_length, sideInfo.ch[1].gr[1].part2_3_length, aduSize, backpointer); #endif return True; } static void getSideInfo1(MP3FrameParams& fr, MP3SideInfo& si, int stereo, int ms_stereo, long sfreq, int /*single*/) { int ch, gr; #if 0 int powdiff = (single == 3) ? 4 : 0; #endif /* initialize all four "part2_3_length" fields to zero: */ si.ch[0].gr[0].part2_3_length = 0; si.ch[1].gr[0].part2_3_length = 0; si.ch[0].gr[1].part2_3_length = 0; si.ch[1].gr[1].part2_3_length = 0; si.main_data_begin = fr.getBits(9); if (stereo == 1) si.private_bits = fr.getBits(5); else si.private_bits = fr.getBits(3); for (ch=0; ch win-sw-flag = 0 */ gr_info.window_switching_flag = fr.get1Bit(); if (gr_info.window_switching_flag) { int i; gr_info.block_type = fr.getBits(2); gr_info.mixed_block_flag = fr.get1Bit(); gr_info.table_select[0] = fr.getBits(5); gr_info.table_select[1] = fr.getBits(5); /* * table_select[2] not needed, because there is no region2, * but to satisfy some verifications tools we set it either. */ gr_info.table_select[2] = 0; for (i=0;i<3;i++) { gr_info.subblock_gain[i] = fr.getBits(3); gr_info.full_gain[i] = gr_info.pow2gain + ((gr_info.subblock_gain[i])<<3); } #ifdef DEBUG_ERRORS if (gr_info.block_type == 0) { fprintf(stderr,"Blocktype == 0 and window-switching == 1 not allowed.\n"); } #endif /* region_count/start parameters are implicit in this case. */ gr_info.region1start = 36>>1; gr_info.region2start = 576>>1; } else { int i,r0c,r1c; for (i=0; i<3; i++) { gr_info.table_select[i] = fr.getBits(5); } r0c = gr_info.region0_count = fr.getBits(4); r1c = gr_info.region1_count = fr.getBits(3); gr_info.region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ; gr_info.region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1; gr_info.block_type = 0; gr_info.mixed_block_flag = 0; } gr_info.preflag = fr.get1Bit(); gr_info.scalefac_scale = fr.get1Bit(); gr_info.count1table_select = fr.get1Bit(); } } } static void getSideInfo2(MP3FrameParams& fr, MP3SideInfo& si, int stereo, int ms_stereo, long sfreq, int /*single*/) { int ch; #if 0 int powdiff = (single == 3) ? 4 : 0; #endif /* initialize all four "part2_3_length" fields to zero: */ si.ch[0].gr[0].part2_3_length = 0; si.ch[1].gr[0].part2_3_length = 0; si.ch[0].gr[1].part2_3_length = 0; si.ch[1].gr[1].part2_3_length = 0; si.main_data_begin = fr.getBits(8); if (stereo == 1) si.private_bits = fr.get1Bit(); else si.private_bits = fr.getBits(2); for (ch=0; ch win-sw-flag = 0 */ gr_info.window_switching_flag = fr.get1Bit(); if (gr_info.window_switching_flag) { int i; gr_info.block_type = fr.getBits(2); gr_info.mixed_block_flag = fr.get1Bit(); gr_info.table_select[0] = fr.getBits(5); gr_info.table_select[1] = fr.getBits(5); /* * table_select[2] not needed, because there is no region2, * but to satisfy some verifications tools we set it either. */ gr_info.table_select[2] = 0; for (i=0;i<3;i++) { gr_info.subblock_gain[i] = fr.getBits(3); gr_info.full_gain[i] = gr_info.pow2gain + ((gr_info.subblock_gain[i])<<3); } #ifdef DEBUG_ERRORS if (gr_info.block_type == 0) { fprintf(stderr,"Blocktype == 0 and window-switching == 1 not allowed.\n"); } #endif /* region_count/start parameters are implicit in this case. */ /* check this again! */ if (gr_info.block_type == 2) gr_info.region1start = 36>>1; else { gr_info.region1start = 54>>1; } gr_info.region2start = 576>>1; } else { int i,r0c,r1c; for (i=0; i<3; i++) { gr_info.table_select[i] = fr.getBits(5); } r0c = gr_info.region0_count = fr.getBits(4); r1c = gr_info.region1_count = fr.getBits(3); gr_info.region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ; gr_info.region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1; gr_info.block_type = 0; gr_info.mixed_block_flag = 0; } gr_info.scalefac_scale = fr.get1Bit(); gr_info.count1table_select = fr.get1Bit(); } } #define MPG_MD_JOINT_STEREO 1 void MP3FrameParams::getSideInfo(MP3SideInfo& si) { // First skip over the CRC if present: if (hasCRC) getBits(16); int single = -1; int ms_stereo; int sfreq = samplingFreqIndex; if (stereo == 1) { single = 0; } ms_stereo = (mode == MPG_MD_JOINT_STEREO) && (mode_ext & 0x2); if (isMPEG2) { getSideInfo2(*this, si, stereo, ms_stereo, sfreq, single); } else { getSideInfo1(*this, si, stereo, ms_stereo, sfreq, single); } } static void putSideInfo1(BitVector& bv, MP3SideInfo const& si, Boolean isStereo) { int ch, gr, i; int stereo = isStereo ? 2 : 1; bv.putBits(si.main_data_begin,9); if (stereo == 1) bv.putBits(si.private_bits, 5); else bv.putBits(si.private_bits, 3); for (ch=0; ch= bitrate) return i; } // "bitrate" was larger than any possible, so return the largest possible: return 14; } static void outputHeader(unsigned char* toPtr, unsigned hdr) { toPtr[0] = (unsigned char)(hdr>>24); toPtr[1] = (unsigned char)(hdr>>16); toPtr[2] = (unsigned char)(hdr>>8); toPtr[3] = (unsigned char)(hdr); } static void assignADUBackpointer(MP3FrameParams const& fr, unsigned aduSize, MP3SideInfo& sideInfo, unsigned& availableBytesForBackpointer) { // Give the ADU as large a backpointer as possible: unsigned maxBackpointerSize = fr.isMPEG2 ? 255 : 511; unsigned backpointerSize = availableBytesForBackpointer; if (backpointerSize > maxBackpointerSize) { backpointerSize = maxBackpointerSize; } // Store the new backpointer now: sideInfo.main_data_begin = backpointerSize; // Figure out how many bytes are available for the *next* ADU's backpointer: availableBytesForBackpointer = backpointerSize + fr.frameSize - fr.sideInfoSize ; if (availableBytesForBackpointer < aduSize) { availableBytesForBackpointer = 0; } else { availableBytesForBackpointer -= aduSize; } } unsigned TranscodeMP3ADU(unsigned char const* fromPtr, unsigned fromSize, unsigned toBitrate, unsigned char* toPtr, unsigned toMaxSize, unsigned& availableBytesForBackpointer) { // Begin by parsing the input ADU's parameters: unsigned hdr, inFrameSize, inSideInfoSize, backpointer, inAduSize; MP3SideInfo sideInfo; if (!GetADUInfoFromMP3Frame(fromPtr, fromSize, hdr, inFrameSize, sideInfo, inSideInfoSize, backpointer, inAduSize)) { return 0; } fromPtr += (4+inSideInfoSize); // skip to 'main data' // Alter the 4-byte MPEG header to reflect the output ADU: // (different bitrate; mono; no CRC) Boolean isMPEG2 = ((hdr&0x00080000) == 0); unsigned toBitrateIndex = MP3BitrateToBitrateIndex(toBitrate, isMPEG2); hdr &=~ 0xF000; hdr |= (toBitrateIndex<<12); // set bitrate index hdr |= 0x10200; // turn on !error-prot and padding bits hdr &=~ 0xC0; hdr |= 0xC0; // set mode to 3 (mono) // Set up the rest of the parameters of the new ADU: MP3FrameParams outFr; outFr.hdr = hdr; outFr.setParamsFromHeader(); // Figure out how big to make the output ADU: unsigned inAveAduSize = inFrameSize - inSideInfoSize; unsigned outAveAduSize = outFr.frameSize - outFr.sideInfoSize; unsigned desiredOutAduSize /*=inAduSize*outAveAduSize/inAveAduSize*/ = (2*inAduSize*outAveAduSize + inAveAduSize)/(2*inAveAduSize); // this rounds to the nearest integer if (toMaxSize < (4 + outFr.sideInfoSize)) return 0; unsigned maxOutAduSize = toMaxSize - (4 + outFr.sideInfoSize); if (desiredOutAduSize > maxOutAduSize) { desiredOutAduSize = maxOutAduSize; } // Figure out the new sizes of the various 'part23 lengths', // and how much they are truncated: unsigned part23Length0a, part23Length0aTruncation; unsigned part23Length0b, part23Length0bTruncation; unsigned part23Length1a, part23Length1aTruncation; unsigned part23Length1b, part23Length1bTruncation; unsigned numAduBits = updateSideInfoSizes(sideInfo, outFr.isMPEG2, fromPtr, 8*desiredOutAduSize, part23Length0a, part23Length0aTruncation, part23Length0b, part23Length0bTruncation, part23Length1a, part23Length1aTruncation, part23Length1b, part23Length1bTruncation); #ifdef DEBUG fprintf(stderr, "shrinkage %d->%d [(%d,%d),(%d,%d)] (trunc: [(%d,%d),(%d,%d)]) {%d}\n", inAduSize, (numAduBits+7)/8, part23Length0a, part23Length0b, part23Length1a, part23Length1b, part23Length0aTruncation, part23Length0bTruncation, part23Length1aTruncation, part23Length1bTruncation, maxOutAduSize); #endif unsigned actualOutAduSize = (numAduBits+7)/8; // Give the new ADU an appropriate 'backpointer': assignADUBackpointer(outFr, actualOutAduSize, sideInfo, availableBytesForBackpointer); ///// Now output the new ADU: // 4-byte header outputHeader(toPtr, hdr); toPtr += 4; // side info PutMP3SideInfoIntoFrame(sideInfo, outFr, toPtr); toPtr += outFr.sideInfoSize; // 'main data', using the new lengths unsigned toBitOffset = 0; unsigned fromBitOffset = 0; /* rebuild portion 0a: */ memmove(toPtr, fromPtr, (part23Length0a+7)/8); toBitOffset += part23Length0a; fromBitOffset += part23Length0a + part23Length0aTruncation; /* rebuild portion 0b: */ shiftBits(toPtr, toBitOffset, fromPtr, fromBitOffset, part23Length0b); toBitOffset += part23Length0b; fromBitOffset += part23Length0b + part23Length0bTruncation; /* rebuild portion 1a: */ shiftBits(toPtr, toBitOffset, fromPtr, fromBitOffset, part23Length1a); toBitOffset += part23Length1a; fromBitOffset += part23Length1a + part23Length1aTruncation; /* rebuild portion 1b: */ shiftBits(toPtr, toBitOffset, fromPtr, fromBitOffset, part23Length1b); toBitOffset += part23Length1b; /* zero out any remaining bits (probably unnecessary, but...) */ unsigned char const zero = '\0'; shiftBits(toPtr, toBitOffset, &zero, 0, actualOutAduSize*8 - numAduBits); return 4 + outFr.sideInfoSize + actualOutAduSize; }