转载 :http://blog.csdn.net/gavinr/article/details/7186627
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;
- }