现在的位置: 首页 > 综合 > 正文

live555源码分析—-关于mp3的处理

2013年08月13日 ⁄ 综合 ⁄ 共 11726字 ⁄ 字号 评论关闭

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

抱歉!评论已关闭.