📄 mp3adu.cpp
字号:
/**********This library is free software; you can redistribute it and/or modify it underthe terms of the GNU Lesser General Public License as published by theFree Software Foundation; either version 2.1 of the License, or (at youroption) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)This library is distributed in the hope that it will be useful, but WITHOUTANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESSFOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License formore details.You should have received a copy of the GNU Lesser General Public Licensealong with this library; if not, write to the Free Software Foundation, Inc.,59 Temple Place, Suite 330, Boston, MA 02111-1307 USA**********/// "liveMedia"// Copyright (c) 1996-2004 Live Networks, Inc. All rights reserved.// 'ADU' MP3 streams (for improved loss-tolerance)// Implementation#include "MP3ADU.hh"#include "MP3ADUdescriptor.hh"#include "MP3Internals.hh"#include <string.h>#ifdef TEST_LOSS#include "GroupsockHelper.hh"#endif// Segment data structures, used in the implementation below:#define SegmentBufSize 2000 /* conservatively high */class Segment {public: unsigned char buf[SegmentBufSize]; unsigned char* dataStart() { return &buf[descriptorSize]; } unsigned frameSize; // if it's a non-ADU frame unsigned dataHere(); // if it's a non-ADU frame unsigned descriptorSize; static unsigned const headerSize; unsigned sideInfoSize, aduSize; unsigned backpointer; struct timeval presentationTime; unsigned durationInMicroseconds;};unsigned const Segment::headerSize = 4;#define SegmentQueueSize 10class SegmentQueue {public: SegmentQueue(Boolean directionIsToADU, Boolean includeADUdescriptors) : fDirectionIsToADU(directionIsToADU), fIncludeADUdescriptors(includeADUdescriptors) { reset(); } Segment s[SegmentQueueSize]; unsigned headIndex() {return fHeadIndex;} Segment& headSegment() {return s[fHeadIndex];} unsigned nextFreeIndex() {return fNextFreeIndex;} Segment& nextFreeSegment() {return s[fNextFreeIndex];} Boolean isEmpty() {return isEmptyOrFull() && totalDataSize() == 0;} Boolean isFull() {return isEmptyOrFull() && totalDataSize() > 0;} static unsigned nextIndex(unsigned ix) {return (ix+1)%SegmentQueueSize;} static unsigned prevIndex(unsigned ix) {return (ix+SegmentQueueSize-1)%SegmentQueueSize;} unsigned totalDataSize() {return fTotalDataSize;} void enqueueNewSegment(FramedSource* inputSource, FramedSource* usingSource); Boolean dequeue(); Boolean insertDummyBeforeTail(unsigned backpointer); void reset() { fHeadIndex = fNextFreeIndex = fTotalDataSize = 0; }private: static void sqAfterGettingSegment(void* clientData, unsigned numBytesRead, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds); Boolean sqAfterGettingCommon(Segment& seg, unsigned numBytesRead); Boolean isEmptyOrFull() {return headIndex() == nextFreeIndex();} unsigned fHeadIndex, fNextFreeIndex, fTotalDataSize; // The following is used for asynchronous reads: FramedSource* fUsingSource; // This tells us whether the direction in which we're being used // is MP3->ADU, or vice-versa. (This flag is used for debugging output.) Boolean fDirectionIsToADU; // The following is true iff we're used to enqueue incoming // ADU frames, and these have an ADU descriptor in front Boolean fIncludeADUdescriptors;};////////// ADUFromMP3Source //////////ADUFromMP3Source::ADUFromMP3Source(UsageEnvironment& env, FramedSource* inputSource, Boolean includeADUdescriptors) : FramedFilter(env, inputSource), fAreEnqueueingMP3Frame(False), fSegments(new SegmentQueue(True /* because we're MP3->ADU */, False /*no descriptors in incoming frames*/)), fIncludeADUdescriptors(includeADUdescriptors), fTotalDataSizeBeforePreviousRead(0), fScale(1), fFrameCounter(0) {}ADUFromMP3Source::~ADUFromMP3Source() { delete fSegments;}char const* ADUFromMP3Source::MIMEtype() const { return "audio/MPA-ROBUST";}ADUFromMP3Source* ADUFromMP3Source::createNew(UsageEnvironment& env, FramedSource* inputSource, Boolean includeADUdescriptors) { // The source must be a MPEG audio source: if (strcmp(inputSource->MIMEtype(), "audio/MPEG") != 0) { env.setResultMsg(inputSource->name(), " is not an MPEG audio source"); return NULL; } return new ADUFromMP3Source(env, inputSource, includeADUdescriptors);}void ADUFromMP3Source::resetInput() { fSegments->reset();}Boolean ADUFromMP3Source::setScaleFactor(int scale) { if (scale < 1) return False; fScale = scale; return True;}void ADUFromMP3Source::doGetNextFrame() { if (!fAreEnqueueingMP3Frame) { // Arrange to enqueue a new MP3 frame: fTotalDataSizeBeforePreviousRead = fSegments->totalDataSize(); fAreEnqueueingMP3Frame = True; fSegments->enqueueNewSegment(fInputSource, this); } else { // 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); } }}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()); 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; } // 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;#ifdef DEBUG fprintf(stderr, "m->a:outputting ADU %d<-%d, nbr:%d, sis:%d, dh:%d, (descriptor size: %d)\n", tailSeg->aduSize, tailSeg->backpointer, fFrameSize, tailSeg->sideInfoSize, tailSeg->dataHere(), descriptorSize);#endif if (descriptorSize + fFrameSize > fMaxSize) { envir() << "ADUFromMP3Source::doGetNextFrame1(): not enough room (" << descriptorSize + fFrameSize << ">" << fMaxSize << ")\n"; fFrameSize = 0; return False; } unsigned char* toPtr = fTo; // output the ADU descriptor: if (fIncludeADUdescriptors) { fFrameSize += ADUdescriptor::generateDescriptor(toPtr, fFrameSize); } // 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;}////////// MP3FromADUSource //////////MP3FromADUSource::MP3FromADUSource(UsageEnvironment& env, FramedSource* inputSource, Boolean includeADUdescriptors) : FramedFilter(env, inputSource), fAreEnqueueingADU(False), fSegments(new SegmentQueue(False /* because we're ADU->MP3 */, includeADUdescriptors)), fIncludeADUdescriptors(includeADUdescriptors) {}MP3FromADUSource::~MP3FromADUSource() { delete fSegments;}char const* MP3FromADUSource::MIMEtype() const { return "audio/MPEG";}MP3FromADUSource* MP3FromADUSource::createNew(UsageEnvironment& env, FramedSource* inputSource, Boolean includeADUdescriptors) { // The source must be an MP3 ADU source: if (strcmp(inputSource->MIMEtype(), "audio/MPA-ROBUST") != 0) { env.setResultMsg(inputSource->name(), " is not an MP3 ADU source"); return NULL; } return new MP3FromADUSource(env, inputSource, includeADUdescriptors);}void MP3FromADUSource::doGetNextFrame() { if (fAreEnqueueingADU) insertDummyADUsIfNecessary(); fAreEnqueueingADU = False; if (needToGetAnADU()) { // Before returning a frame, we must enqueue at least one ADU:#ifdef TEST_LOSS NOTE: This code no longer works, because it uses synchronous reads, which are no longer supported. static unsigned const framesPerPacket = 10; static unsigned const frameCount = 0; static Boolean packetIsLost; while (1) { if ((frameCount++)%framesPerPacket == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -