您的位置:首页 > 其它

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

2015-02-05 09:23 441 查看
转载 :http://blog.csdn.net/gavinr/article/details/7186627

RFC3199定义了MP3的RTP打包规则。首先来看看处理*.mp3的sesseion是如何创建的

[cpp] view
plaincopyprint?

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的打包操作。

[cpp] view
plaincopyprint?

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的打包过程

[cpp] view
plaincopyprint?

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函数,这时将执行第二个分支。

[cpp] view
plaincopyprint?

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函数的处理

[cpp] view
plaincopyprint?

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函数的细节。

[cpp] view
plaincopyprint?

// 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相关的信息

[cpp] view
plaincopyprint?

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头的分析

[cpp] view
plaincopyprint?

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的计算方式

[cpp] view
plaincopyprint?

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函数。

[cpp] view
plaincopyprint?

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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: