/********** 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 2.1 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-2016 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; }