RFC3199定義了MP3的RTP打包規則。首先來看看處理*.mp3的sesseion是如何建立的
static ServerMediaSession* createNewSMS(UsageEnvironment& env,char const* fileName, FILE* /*fid*/) {... else if (strcmp(extension, ".mp3") == 0) { // Assumed to be a MPEG-1 or 2 Audio file: NEW_SMS("MPEG-1 or 2 Audio"); //去注釋STREAM_USING_ADUS宏,傳輸時使用ADUs,而不是MP3裸幀 // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following://#define STREAM_USING_ADUS 1 //去注釋INTERLEAVE_ADUS宏,在傳輸之前獎ADUs重排序(交錯) // To also reorder ADUs before streaming, uncomment the following://#define INTERLEAVE_ADUS 1 // (For more information about ADUs and interleaving, // see <http://www.live555.com/rtp-mp3/>) Boolean useADUs = False; Interleaving* interleaving = NULL;#ifdef STREAM_USING_ADUS useADUs = True;#ifdef INTERLEAVE_ADUS unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own... unsigned const interleaveCycleSize = (sizeof interleaveCycle)/(sizeof (unsigned char)); interleaving = new Interleaving(interleaveCycleSize, interleaveCycle); //建立一個用於交錯的filter#endif#endif sms->addSubsession(MP3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, useADUs, interleaving)); //注意這裡傳遞的參數 }}
上面的代碼開啟宏STREAM_USING_ADUS,則會將MP3幀打包成ADU後,再發送。開啟宏INTERLEAVE_ADUS,MP3打包成ADU後,將進行交錯排列。可以看到預設情況下,這兩個選項都是關閉的。在MP3AudioFileServerMediaSubsession::createNewStreamSource函數中會調用一個createNewStreamSourceCommon函數,處理ADU的打包操作。
FramedSource* MP3AudioFileServerMediaSubsession::createNewStreamSourceCommon(FramedSource* baseMP3Source, unsigned mp3NumBytes, unsigned& estBitrate) { FramedSource* streamSource; fFileDuration = 0.0; do { streamSource = baseMP3Source; // by default if (streamSource == NULL) break; // Use the MP3 file size, plus the duration, to estimate the stream's bitrate: if (mp3NumBytes > 0 && fFileDuration > 0.0) { estBitrate = (unsigned)(mp3NumBytes/(125*fFileDuration) + 0.5); // kbps, rounded } else { estBitrate = 128; // kbps, estimate } if (fGenerateADUs) { //判斷是否打包成ADU後發送 // Add a filter that converts the source MP3s to ADUs: streamSource = ADUFromMP3Source::createNew(envir(), streamSource); if (streamSource == NULL) break; if (fInterleaving != NULL) {// Add another filter that interleaves the ADUs before packetizing:streamSource = MP3ADUinterleaver::createNew(envir(), *fInterleaving, streamSource);if (streamSource == NULL) break; } } else if (fFileDuration > 0.0) { // //注意了,對於不需要打包成ADU的情況,這裡有一個打包再解包的過程,為是的方便定位 // // Because this is a seekable file, insert a pair of filters: one that // converts the input MP3 stream to ADUs; another that converts these // ADUs back to MP3. This allows us to seek within the input stream without // tripping over the MP3 'bit reservoir': streamSource = ADUFromMP3Source::createNew(envir(), streamSource); if (streamSource == NULL) break; streamSource = MP3FromADUSource::createNew(envir(), streamSource); if (streamSource == NULL) break; } } while (0); return streamSource; //返回的是最外層的source}
上面的代碼處理了兩種情況:一是根據fGenerateADUs的值決定是否產生ADU後再發送,二是對於不需要打包成ADU的情況,這裡有一個打包再解包的過程,為是的方便定位。 下面來看ADU的打包過程
void ADUFromMP3Source::doGetNextFrame() { if (!fAreEnqueueingMP3Frame) { //分支1 // Arrange to enqueue a new MP3 frame: fTotalDataSizeBeforePreviousRead = fSegments->totalDataSize(); fAreEnqueueingMP3Frame = True; fSegments->enqueueNewSegment(fInputSource, this); } else { //分支2 // Deliver an ADU from a previously-read MP3 frame: fAreEnqueueingMP3Frame = False; if (!doGetNextFrame1()) { // An internal error occurred; act as if our source went away: FramedSource::handleClosure(this); } }}
fAreEnqueueingMP3Frame初始直為False, 首先會執行第1個分支。從後面的分析中,我們會發現fSegments->enqueueNewSegmen函數最後又調用了ADUFromMP3Source::doGetNextFrame函數,這時將執行第二個分支。
void SegmentQueue::enqueueNewSegment(FramedSource* inputSource, FramedSource* usingSource) { if (isFull()) { usingSource->envir() << "SegmentQueue::enqueueNewSegment() overflow\n"; FramedSource::handleClosure(usingSource); return; } fUsingSource = usingSource; Segment& seg = nextFreeSegment(); //從source擷取mp3資料 inputSource->getNextFrame(seg.buf, sizeof seg.buf, sqAfterGettingSegment, this, FramedSource::handleClosure, usingSource);}
從source中擷取mp3資料的過程就不關注了,直接看sqAfterGettingSegment函數的處理
void SegmentQueue::sqAfterGettingSegment(void* clientData, unsigned numBytesRead, unsigned /*numTruncatedBytes*/, struct timeval presentationTime, unsigned durationInMicroseconds) { SegmentQueue* segQueue = (SegmentQueue*)clientData; Segment& seg = segQueue->nextFreeSegment(); //擷取剛才讀入資料的segment seg.presentationTime = presentationTime; seg.durationInMicroseconds = durationInMicroseconds; if (segQueue->sqAfterGettingCommon(seg, numBytesRead)) { //分析讀取到的mp3 frame#ifdef DEBUG char const* direction = segQueue->fDirectionIsToADU ? "m->a" : "a->m"; fprintf(stderr, "%s:read frame %d<-%d, fs:%d, sis:%d, dh:%d, (descriptor size: %d)\n", direction, seg.aduSize, seg.backpointer, seg.frameSize, seg.sideInfoSize, seg.dataHere(), seg.descriptorSize);#endif } // Continue our original calling source where it left off: segQueue->fUsingSource->doGetNextFrame(); //又一次調用了doGetNextFrame函數}
擷取到的資料存放區在segment中,先調用sqAfterGettingCommon函數對frame進行分析。最後又一次調用了ADUFromMP3Source::doGetNextFrame函數,只不過這次將會執行第2個分支。先來看看sqAfterGettingCommon函數的細節。
// Common code called after a new segment is enqueuedBoolean SegmentQueue::sqAfterGettingCommon(Segment& seg, unsigned numBytesRead) { unsigned char* fromPtr = seg.buf;//是否已經包含了ADU標識 if (fIncludeADUdescriptors) { //對ADUFromMP3Source中的SegmentQueue,這個值必然為False // The newly-read data is assumed to be an ADU with a descriptor // in front // //getRemainingFrameSize中根據fromPtr第1個位元組決定了descriptor為1個或者兩個位元組 // (void)ADUdescriptor::getRemainingFrameSize(fromPtr); seg.descriptorSize = (unsigned)(fromPtr-seg.buf); } else { seg.descriptorSize = 0; } // parse the MP3-specific info in the frame to get the ADU params unsigned hdr; //4位元組的frame頭 MP3SideInfo sideInfo; //side info //分析frame,擷取相關資訊 if (!GetADUInfoFromMP3Frame(fromPtr, numBytesRead, hdr, seg.frameSize, sideInfo, seg.sideInfoSize, seg.backpointer, seg.aduSize)) { return False; } // If we've just read an ADU (rather than a regular MP3 frame), then use the // entire "numBytesRead" data for the 'aduSize', so that we include any // 'ancillary data' that may be present at the end of the ADU: if (!fDirectionIsToADU) { //預設值為True unsigned newADUSize = numBytesRead - seg.descriptorSize - 4/*header size*/ - seg.sideInfoSize; if (newADUSize > seg.aduSize) seg.aduSize = newADUSize; } fTotalDataSize += seg.dataHere(); fNextFreeIndex = nextIndex(fNextFreeIndex); //更新空閑segment索引 return True;}
上面代碼中的fIncludeADUdescriptors變數,表示讀入到segment中的資料是否包含ADU標識符,顯然ADUFromMP3Source處理的未經處理資料肯定是不包含ADU標識符的。
GetADUInfoFromMP3Frame函數從mp3幀中擷取ADU相關的資訊
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; //MP3FrameParams類專門用來分析mp3幀資訊 //前4個位元組是mp3的幀頭 fr.hdr = ((unsigned)framePtr[0] << 24) | ((unsigned)framePtr[1] << 16) | ((unsigned)framePtr[2] << 8) | (unsigned)framePtr[3]; fr.setParamsFromHeader(); //分析4位元組頭部 fr.setBytePointer(framePtr + 4, totFrameSize - 4); // skip hdr frameSize = 4 + fr.frameSize; //非mp3幀(mp2或者mp1) 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; //4位元組頭 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; //adu位元組數#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;}
來看MP3 frame頭的分析
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>>16)&0x1)^0x1; 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); //計算frame的大小 sideInfoSize = computeSideInfoSize(); }
標準的mp3 frame(立體聲不帶CRC), 附加資訊大小32位
frameSize的計算方式
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; framesize = framesize + usePadding - 4; return framesize;}
這裡的bitrate單位為kbps
MP3幀長取決於位率和頻率,計算公式為:
. mpeg1.0 layer1 : 幀長= (48000*bitrate)/sampling_freq + padding
layer2&3: 幀長= (144000*bitrate)/sampling_freq + padding
. mpeg2.0 layer1 : 幀長= (24000*bitrate)/sampling_freq + padding
layer2&3 : 幀長= (72000*bitrate)/sampling_freq + padding
根據公式,位率為128kbps,採樣頻率為44.1kHz,padding(幀長調節)為0時,幀長為417位元組。
奇怪的是,padding為0,最後幾個bit不是丟棄掉了嗎?
到此,ADUFromMP3Source::doGetNextFrame()函數第1個分支分析完了,現在來看其第2個分支。分支2主要調用了ADUFromMP3Source::doGetNextFrame1函數。
Boolean ADUFromMP3Source::doGetNextFrame1() { // First, check whether we have enough previously-read data to output an // ADU for the last-read MP3 frame: unsigned tailIndex; Segment* tailSeg; Boolean needMoreData; if (fSegments->isEmpty()) { needMoreData = True; tailSeg = NULL; tailIndex = 0; // unneeded, but stops compiler warnings } else { tailIndex = SegmentQueue::prevIndex(fSegments->nextFreeIndex()); //擷取上一個填充了資料的segment tailSeg = &(fSegments->s[tailIndex]); needMoreData = fTotalDataSizeBeforePreviousRead < tailSeg->backpointer // bp points back too far || tailSeg->backpointer + tailSeg->dataHere() < tailSeg->aduSize; // not enough data } if (needMoreData) { // // We don't have enough data to output an ADU from the last-read MP3 // frame, so need to read another one and try again: doGetNextFrame(); //沒有足夠的資料,則重新讀取資料 return True; } //從尾部的segment中擷取一個ADU // Output an ADU from the tail segment: fFrameSize = tailSeg->headerSize+tailSeg->sideInfoSize+tailSeg->aduSize; fPresentationTime = tailSeg->presentationTime; fDurationInMicroseconds = tailSeg->durationInMicroseconds; unsigned descriptorSize = fIncludeADUdescriptors ? ADUdescriptor::computeSize(fFrameSize) : 0; if (descriptorSize + fFrameSize > fMaxSize) { envir() << "ADUFromMP3Source::doGetNextFrame1(): not enough room (" << descriptorSize + fFrameSize << ">" << fMaxSize << ")\n"; fFrameSize = 0; return False; } unsigned char* toPtr = fTo; //輸出ADU描述符 // output the ADU descriptor: if (fIncludeADUdescriptors) { //預設值為False fFrameSize += ADUdescriptor::generateDescriptor(toPtr, fFrameSize); } //輸出header和side info // output header and side info: memmove(toPtr, tailSeg->dataStart(), tailSeg->headerSize + tailSeg->sideInfoSize); toPtr += tailSeg->headerSize + tailSeg->sideInfoSize; //輸出資料 // go back to the frame that contains the start of our data: unsigned offset = 0; unsigned i = tailIndex; unsigned prevBytes = tailSeg->backpointer; while (prevBytes > 0) { i = SegmentQueue::prevIndex(i); unsigned dataHere = fSegments->s[i].dataHere(); if (dataHere < prevBytes) { prevBytes -= dataHere; } else { offset = dataHere - prevBytes; break; } } // dequeue any segments that we no longer need: while (fSegments->headIndex() != i) { fSegments->dequeue(); // we're done with it } unsigned bytesToUse = tailSeg->aduSize; while (bytesToUse > 0) { Segment& seg = fSegments->s[i]; unsigned char* fromPtr = &seg.dataStart()[seg.headerSize + seg.sideInfoSize + offset]; unsigned dataHere = seg.dataHere() - offset; unsigned bytesUsedHere = dataHere < bytesToUse ? dataHere : bytesToUse; memmove(toPtr, fromPtr, bytesUsedHere); bytesToUse -= bytesUsedHere; toPtr += bytesUsedHere; offset = 0; i = SegmentQueue::nextIndex(i); } if (fFrameCounter++%fScale == 0) { //快進快退操作,丟棄不需要的幀 // Call our own 'after getting' function. Because we're not a 'leaf' // source, we can call this directly, without risking infinite recursion. afterGetting(this); } else { // Don't use this frame; get another one: doGetNextFrame(); } return True;}