live555源碼分析—-關於mp3的處理

來源:互聯網
上載者:User

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.