635 lines
20 KiB
C++
Executable File
635 lines
20 KiB
C++
Executable File
/**********
|
|
This library is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
Free Software Foundation; either version 2.1 of the License, or (at your
|
|
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
|
|
|
|
This library is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this library; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
**********/
|
|
// "liveMedia"
|
|
// Copyright (c) 1996-2016 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 20
|
|
|
|
class 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:
|
|
handleClosure();
|
|
}
|
|
}
|
|
}
|
|
|
|
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)) {
|
|
}
|
|
|
|
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) {
|
|
packetIsLost = (our_random()%10 == 0); // simulate 10% packet loss #####
|
|
}
|
|
|
|
if (packetIsLost) {
|
|
// Read and discard the next input frame (that would be part of
|
|
// a lost packet):
|
|
Segment dummySegment;
|
|
unsigned numBytesRead;
|
|
struct timeval presentationTime;
|
|
// (this works only if the source can be read synchronously)
|
|
fInputSource->syncGetNextFrame(dummySegment.buf,
|
|
sizeof dummySegment.buf, numBytesRead,
|
|
presentationTime);
|
|
} else {
|
|
break; // from while (1)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
fAreEnqueueingADU = True;
|
|
fSegments->enqueueNewSegment(fInputSource, this);
|
|
} else {
|
|
// Return a frame now:
|
|
generateFrameFromHeadADU();
|
|
// sets fFrameSize, fPresentationTime, and fDurationInMicroseconds
|
|
|
|
// Call our own 'after getting' function. Because we're not a 'leaf'
|
|
// source, we can call this directly, without risking infinite recursion.
|
|
afterGetting(this);
|
|
}
|
|
}
|
|
|
|
Boolean MP3FromADUSource::needToGetAnADU() {
|
|
// Check whether we need to first enqueue a new ADU before we
|
|
// can generate a frame for our head ADU.
|
|
Boolean needToEnqueue = True;
|
|
|
|
if (!fSegments->isEmpty()) {
|
|
unsigned index = fSegments->headIndex();
|
|
Segment* seg = &(fSegments->headSegment());
|
|
int const endOfHeadFrame = (int) seg->dataHere();
|
|
unsigned frameOffset = 0;
|
|
|
|
while (1) {
|
|
int endOfData = frameOffset - seg->backpointer + seg->aduSize;
|
|
if (endOfData >= endOfHeadFrame) {
|
|
// We already have enough data to generate a frame
|
|
needToEnqueue = False;
|
|
break;
|
|
}
|
|
|
|
frameOffset += seg->dataHere();
|
|
index = SegmentQueue::nextIndex(index);
|
|
if (index == fSegments->nextFreeIndex()) break;
|
|
seg = &(fSegments->s[index]);
|
|
}
|
|
}
|
|
|
|
return needToEnqueue;
|
|
}
|
|
|
|
void MP3FromADUSource::insertDummyADUsIfNecessary() {
|
|
if (fSegments->isEmpty()) return; // shouldn't happen
|
|
|
|
// The tail segment (ADU) is assumed to have been recently
|
|
// enqueued. If its backpointer would overlap the data
|
|
// of the previous ADU, then we need to insert one or more
|
|
// empty, 'dummy' ADUs ahead of it. (This situation should occur
|
|
// only if an intermediate ADU was lost.)
|
|
|
|
unsigned tailIndex
|
|
= SegmentQueue::prevIndex(fSegments->nextFreeIndex());
|
|
Segment* tailSeg = &(fSegments->s[tailIndex]);
|
|
|
|
while (1) {
|
|
unsigned prevADUend; // relative to the start of the new ADU
|
|
if (fSegments->headIndex() != tailIndex) {
|
|
// there is a previous segment
|
|
unsigned prevIndex = SegmentQueue::prevIndex(tailIndex);
|
|
Segment& prevSegment = fSegments->s[prevIndex];
|
|
prevADUend = prevSegment.dataHere() + prevSegment.backpointer;
|
|
if (prevSegment.aduSize > prevADUend) {
|
|
// shouldn't happen if the previous ADU was well-formed
|
|
prevADUend = 0;
|
|
} else {
|
|
prevADUend -= prevSegment.aduSize;
|
|
}
|
|
} else {
|
|
prevADUend = 0;
|
|
}
|
|
|
|
if (tailSeg->backpointer > prevADUend) {
|
|
// We need to insert a dummy ADU in front of the tail
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "a->m:need to insert a dummy ADU (%d, %d, %d) [%d, %d]\n", tailSeg->backpointer, prevADUend, tailSeg->dataHere(), fSegments->headIndex(), fSegments->nextFreeIndex());
|
|
#endif
|
|
tailIndex = fSegments->nextFreeIndex();
|
|
if (!fSegments->insertDummyBeforeTail(prevADUend)) return;
|
|
tailSeg = &(fSegments->s[tailIndex]);
|
|
} else {
|
|
break; // no more dummy ADUs need to be inserted
|
|
}
|
|
}
|
|
}
|
|
|
|
Boolean MP3FromADUSource::generateFrameFromHeadADU() {
|
|
// Output a frame for the head ADU:
|
|
if (fSegments->isEmpty()) return False;
|
|
unsigned index = fSegments->headIndex();
|
|
Segment* seg = &(fSegments->headSegment());
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "a->m:outputting frame for %d<-%d (fs %d, dh %d), (descriptorSize: %d)\n", seg->aduSize, seg->backpointer, seg->frameSize, seg->dataHere(), seg->descriptorSize);
|
|
#endif
|
|
unsigned char* toPtr = fTo;
|
|
|
|
// output header and side info:
|
|
fFrameSize = seg->frameSize;
|
|
fPresentationTime = seg->presentationTime;
|
|
fDurationInMicroseconds = seg->durationInMicroseconds;
|
|
memmove(toPtr, seg->dataStart(), seg->headerSize + seg->sideInfoSize);
|
|
toPtr += seg->headerSize + seg->sideInfoSize;
|
|
|
|
// zero out the rest of the frame, in case ADU data doesn't fill it all in
|
|
unsigned bytesToZero = seg->dataHere();
|
|
for (unsigned i = 0; i < bytesToZero; ++i) {
|
|
toPtr[i] = '\0';
|
|
}
|
|
|
|
// Fill in the frame with appropriate ADU data from this and
|
|
// subsequent ADUs:
|
|
unsigned frameOffset = 0;
|
|
unsigned toOffset = 0;
|
|
unsigned const endOfHeadFrame = seg->dataHere();
|
|
|
|
while (toOffset < endOfHeadFrame) {
|
|
int startOfData = frameOffset - seg->backpointer;
|
|
if (startOfData > (int)endOfHeadFrame) break; // no more ADUs needed
|
|
|
|
int endOfData = startOfData + seg->aduSize;
|
|
if (endOfData > (int)endOfHeadFrame) {
|
|
endOfData = endOfHeadFrame;
|
|
}
|
|
|
|
unsigned fromOffset;
|
|
if (startOfData <= (int)toOffset) {
|
|
fromOffset = toOffset - startOfData;
|
|
startOfData = toOffset;
|
|
if (endOfData < startOfData) endOfData = startOfData;
|
|
} else {
|
|
fromOffset = 0;
|
|
|
|
// we may need some padding bytes beforehand
|
|
unsigned bytesToZero = startOfData - toOffset;
|
|
#ifdef DEBUG
|
|
if (bytesToZero > 0) fprintf(stderr, "a->m:outputting %d zero bytes (%d, %d, %d, %d)\n", bytesToZero, startOfData, toOffset, frameOffset, seg->backpointer);
|
|
#endif
|
|
toOffset += bytesToZero;
|
|
}
|
|
|
|
unsigned char* fromPtr
|
|
= &seg->dataStart()[seg->headerSize + seg->sideInfoSize + fromOffset];
|
|
unsigned bytesUsedHere = endOfData - startOfData;
|
|
#ifdef DEBUG
|
|
if (bytesUsedHere > 0) fprintf(stderr, "a->m:outputting %d bytes from %d<-%d\n", bytesUsedHere, seg->aduSize, seg->backpointer);
|
|
#endif
|
|
memmove(toPtr + toOffset, fromPtr, bytesUsedHere);
|
|
toOffset += bytesUsedHere;
|
|
|
|
frameOffset += seg->dataHere();
|
|
index = SegmentQueue::nextIndex(index);
|
|
if (index == fSegments->nextFreeIndex()) break;
|
|
seg = &(fSegments->s[index]);
|
|
}
|
|
|
|
fSegments->dequeue();
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
////////// Segment //////////
|
|
|
|
unsigned Segment::dataHere() {
|
|
int result = frameSize - (headerSize + sideInfoSize);
|
|
if (result < 0) {
|
|
return 0;
|
|
}
|
|
|
|
return (unsigned)result;
|
|
}
|
|
|
|
////////// SegmentQueue //////////
|
|
|
|
void SegmentQueue::enqueueNewSegment(FramedSource* inputSource,
|
|
FramedSource* usingSource) {
|
|
if (isFull()) {
|
|
usingSource->envir() << "SegmentQueue::enqueueNewSegment() overflow\n";
|
|
usingSource->handleClosure();
|
|
return;
|
|
}
|
|
|
|
fUsingSource = usingSource;
|
|
|
|
Segment& seg = nextFreeSegment();
|
|
inputSource->getNextFrame(seg.buf, sizeof seg.buf,
|
|
sqAfterGettingSegment, this,
|
|
FramedSource::handleClosure, usingSource);
|
|
}
|
|
|
|
void SegmentQueue::sqAfterGettingSegment(void* clientData,
|
|
unsigned numBytesRead,
|
|
unsigned /*numTruncatedBytes*/,
|
|
struct timeval presentationTime,
|
|
unsigned durationInMicroseconds) {
|
|
SegmentQueue* segQueue = (SegmentQueue*)clientData;
|
|
Segment& seg = segQueue->nextFreeSegment();
|
|
|
|
seg.presentationTime = presentationTime;
|
|
seg.durationInMicroseconds = durationInMicroseconds;
|
|
|
|
if (segQueue->sqAfterGettingCommon(seg, numBytesRead)) {
|
|
#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();
|
|
}
|
|
|
|
// Common code called after a new segment is enqueued
|
|
Boolean SegmentQueue::sqAfterGettingCommon(Segment& seg,
|
|
unsigned numBytesRead) {
|
|
unsigned char* fromPtr = seg.buf;
|
|
|
|
if (fIncludeADUdescriptors) {
|
|
// The newly-read data is assumed to be an ADU with a descriptor
|
|
// in front
|
|
(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;
|
|
MP3SideInfo sideInfo;
|
|
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) {
|
|
unsigned newADUSize
|
|
= numBytesRead - seg.descriptorSize - 4/*header size*/ - seg.sideInfoSize;
|
|
if (newADUSize > seg.aduSize) seg.aduSize = newADUSize;
|
|
}
|
|
fTotalDataSize += seg.dataHere();
|
|
fNextFreeIndex = nextIndex(fNextFreeIndex);
|
|
|
|
return True;
|
|
}
|
|
|
|
Boolean SegmentQueue::dequeue() {
|
|
if (isEmpty()) {
|
|
fUsingSource->envir() << "SegmentQueue::dequeue(): underflow!\n";
|
|
return False;
|
|
}
|
|
|
|
Segment& seg = s[headIndex()];
|
|
fTotalDataSize -= seg.dataHere();
|
|
fHeadIndex = nextIndex(fHeadIndex);
|
|
return True;
|
|
}
|
|
|
|
Boolean SegmentQueue::insertDummyBeforeTail(unsigned backpointer) {
|
|
if (isEmptyOrFull()) return False;
|
|
|
|
// Copy the current tail segment to its new position, then modify the
|
|
// old tail segment to be a 'dummy' ADU
|
|
|
|
unsigned newTailIndex = nextFreeIndex();
|
|
Segment& newTailSeg = s[newTailIndex];
|
|
|
|
unsigned oldTailIndex = prevIndex(newTailIndex);
|
|
Segment& oldTailSeg = s[oldTailIndex];
|
|
|
|
newTailSeg = oldTailSeg; // structure copy
|
|
|
|
// Begin by setting (replacing) the ADU descriptor of the dummy ADU:
|
|
unsigned char* ptr = oldTailSeg.buf;
|
|
if (fIncludeADUdescriptors) {
|
|
unsigned remainingFrameSize
|
|
= oldTailSeg.headerSize + oldTailSeg.sideInfoSize + 0 /* 0-size ADU */;
|
|
unsigned currentDescriptorSize = oldTailSeg.descriptorSize;
|
|
|
|
if (currentDescriptorSize == 2) {
|
|
ADUdescriptor::generateTwoByteDescriptor(ptr, remainingFrameSize);
|
|
} else {
|
|
(void)ADUdescriptor::generateDescriptor(ptr, remainingFrameSize);
|
|
}
|
|
}
|
|
|
|
// Then zero out the side info of the dummy frame:
|
|
if (!ZeroOutMP3SideInfo(ptr, oldTailSeg.frameSize,
|
|
backpointer)) return False;
|
|
|
|
unsigned dummyNumBytesRead
|
|
= oldTailSeg.descriptorSize + 4/*header size*/ + oldTailSeg.sideInfoSize;
|
|
return sqAfterGettingCommon(oldTailSeg, dummyNumBytesRead);
|
|
}
|