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 enqueued Boolean 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; }