blob: 9ccd0ff34e9753683accc3310fc4f6b392490966 [file] [log] [blame]
/**********
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 3 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-2020 Live Networks, Inc. All rights reserved.
// A filter that breaks up an AC3 audio elementary stream into frames
// Implementation
#include "AC3AudioStreamFramer.hh"
#include "StreamParser.hh"
#include <GroupsockHelper.hh>
////////// AC3AudioStreamParser definition //////////
class AC3FrameParams {
public:
AC3FrameParams() : samplingFreq(0) {}
// 8-byte header at the start of each frame:
// u_int32_t hdr0, hdr1;
unsigned hdr0, hdr1;
// parameters derived from the headers
unsigned kbps, samplingFreq, frameSize;
void setParamsFromHeader();
};
class AC3AudioStreamParser: public StreamParser {
public:
AC3AudioStreamParser(AC3AudioStreamFramer* usingSource,
FramedSource* inputSource);
virtual ~AC3AudioStreamParser();
public:
void testStreamCode(unsigned char ourStreamCode,
unsigned char* ptr, unsigned size);
unsigned parseFrame(unsigned& numTruncatedBytes);
// returns the size of the frame that was acquired, or 0 if none was
void registerReadInterest(unsigned char* to, unsigned maxSize);
AC3FrameParams const& currentFrame() const { return fCurrentFrame; }
Boolean haveParsedAFrame() const { return fHaveParsedAFrame; }
void readAndSaveAFrame();
private:
static void afterGettingSavedFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
struct timeval presentationTime,
unsigned durationInMicroseconds);
void afterGettingSavedFrame1(unsigned frameSize);
static void onSavedFrameClosure(void* clientData);
void onSavedFrameClosure1();
private:
AC3AudioStreamFramer* fUsingSource;
unsigned char* fTo;
unsigned fMaxSize;
Boolean fHaveParsedAFrame;
unsigned char* fSavedFrame;
unsigned fSavedFrameSize;
char fSavedFrameFlag;
// Parameters of the most recently read frame:
AC3FrameParams fCurrentFrame;
};
////////// AC3AudioStreamFramer implementation //////////
AC3AudioStreamFramer::AC3AudioStreamFramer(UsageEnvironment& env,
FramedSource* inputSource,
unsigned char streamCode)
: FramedFilter(env, inputSource), fOurStreamCode(streamCode) {
// Use the current wallclock time as the initial 'presentation time':
gettimeofday(&fNextFramePresentationTime, NULL);
fParser = new AC3AudioStreamParser(this, inputSource);
}
AC3AudioStreamFramer::~AC3AudioStreamFramer() {
delete fParser;
}
AC3AudioStreamFramer*
AC3AudioStreamFramer::createNew(UsageEnvironment& env,
FramedSource* inputSource,
unsigned char streamCode) {
// Need to add source type checking here??? #####
return new AC3AudioStreamFramer(env, inputSource, streamCode);
}
unsigned AC3AudioStreamFramer::samplingRate() {
if (!fParser->haveParsedAFrame()) {
// Because we haven't yet parsed a frame, we don't yet know the input
// stream's sampling rate. So, we first need to read a frame
// (into a special buffer that we keep around for later use).
fParser->readAndSaveAFrame();
}
return fParser->currentFrame().samplingFreq;
}
void AC3AudioStreamFramer::flushInput() {
fParser->flushInput();
}
void AC3AudioStreamFramer::doGetNextFrame() {
fParser->registerReadInterest(fTo, fMaxSize);
parseNextFrame();
}
#define MILLION 1000000
struct timeval AC3AudioStreamFramer::currentFramePlayTime() const {
AC3FrameParams const& fr = fParser->currentFrame();
unsigned const numSamples = 1536;
unsigned const freq = fr.samplingFreq;
// result is numSamples/freq
unsigned const uSeconds = (freq == 0) ? 0
: ((numSamples*2*MILLION)/freq + 1)/2; // rounds to nearest integer
struct timeval result;
result.tv_sec = uSeconds/MILLION;
result.tv_usec = uSeconds%MILLION;
return result;
}
void AC3AudioStreamFramer
::handleNewData(void* clientData, unsigned char* ptr, unsigned size,
struct timeval /*presentationTime*/) {
AC3AudioStreamFramer* framer = (AC3AudioStreamFramer*)clientData;
framer->handleNewData(ptr, size);
}
void AC3AudioStreamFramer
::handleNewData(unsigned char* ptr, unsigned size) {
fParser->testStreamCode(fOurStreamCode, ptr, size);
parseNextFrame();
}
void AC3AudioStreamFramer::parseNextFrame() {
unsigned acquiredFrameSize = fParser->parseFrame(fNumTruncatedBytes);
if (acquiredFrameSize > 0) {
// We were able to acquire a frame from the input.
// It has already been copied to the reader's space.
fFrameSize = acquiredFrameSize;
// Also set the presentation time, and increment it for next time,
// based on the length of this frame:
fPresentationTime = fNextFramePresentationTime;
struct timeval framePlayTime = currentFramePlayTime();
fDurationInMicroseconds = framePlayTime.tv_sec*MILLION + framePlayTime.tv_usec;
fNextFramePresentationTime.tv_usec += framePlayTime.tv_usec;
fNextFramePresentationTime.tv_sec
+= framePlayTime.tv_sec + fNextFramePresentationTime.tv_usec/MILLION;
fNextFramePresentationTime.tv_usec %= MILLION;
// 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 {
// We were unable to parse a complete frame from the input, because:
// - we had to read more data from the source stream, or
// - the source stream has ended.
}
}
////////// AC3AudioStreamParser implementation //////////
static int const kbpsTable[] = {32, 40, 48, 56, 64, 80, 96, 112,
128, 160, 192, 224, 256, 320, 384, 448,
512, 576, 640};
void AC3FrameParams::setParamsFromHeader() {
unsigned char byte4 = hdr1 >> 24;
unsigned char kbpsIndex = (byte4&0x3E) >> 1;
if (kbpsIndex > 18) kbpsIndex = 18;
kbps = kbpsTable[kbpsIndex];
unsigned char samplingFreqIndex = (byte4&0xC0) >> 6;
switch (samplingFreqIndex) {
case 0:
samplingFreq = 48000;
frameSize = 4*kbps;
break;
case 1:
samplingFreq = 44100;
frameSize = 2*(320*kbps/147 + (byte4&1));
break;
case 2:
case 3: // not legal?
samplingFreq = 32000;
frameSize = 6*kbps;
}
}
AC3AudioStreamParser
::AC3AudioStreamParser(AC3AudioStreamFramer* usingSource,
FramedSource* inputSource)
: StreamParser(inputSource, FramedSource::handleClosure, usingSource,
&AC3AudioStreamFramer::handleNewData, usingSource),
fUsingSource(usingSource), fHaveParsedAFrame(False),
fSavedFrame(NULL), fSavedFrameSize(0) {
}
AC3AudioStreamParser::~AC3AudioStreamParser() {
}
void AC3AudioStreamParser::registerReadInterest(unsigned char* to,
unsigned maxSize) {
fTo = to;
fMaxSize = maxSize;
}
void AC3AudioStreamParser
::testStreamCode(unsigned char ourStreamCode,
unsigned char* ptr, unsigned size) {
if (ourStreamCode == 0) return; // we assume that there's no stream code at the beginning of the data
if (size < 4) return;
unsigned char streamCode = *ptr;
if (streamCode == ourStreamCode) {
// Remove the first 4 bytes from the stream:
memmove(ptr, ptr + 4, size - 4);
totNumValidBytes() = totNumValidBytes() - 4;
} else {
// Discard all of the data that was just read:
totNumValidBytes() = totNumValidBytes() - size;
}
}
unsigned AC3AudioStreamParser::parseFrame(unsigned& numTruncatedBytes) {
if (fSavedFrameSize > 0) {
// We've already read and parsed a frame. Use it instead:
memmove(fTo, fSavedFrame, fSavedFrameSize);
delete[] fSavedFrame; fSavedFrame = NULL;
unsigned frameSize = fSavedFrameSize;
fSavedFrameSize = 0;
return frameSize;
}
try {
saveParserState();
// We expect an AC3 audio header (first 2 bytes == 0x0B77) at the start:
while (1) {
unsigned next4Bytes = test4Bytes();
if (next4Bytes>>16 == 0x0B77) break;
skipBytes(1);
saveParserState();
}
fCurrentFrame.hdr0 = get4Bytes();
fCurrentFrame.hdr1 = test4Bytes();
fCurrentFrame.setParamsFromHeader();
fHaveParsedAFrame = True;
// Copy the frame to the requested destination:
unsigned frameSize = fCurrentFrame.frameSize;
if (frameSize > fMaxSize) {
numTruncatedBytes = frameSize - fMaxSize;
frameSize = fMaxSize;
} else {
numTruncatedBytes = 0;
}
fTo[0] = fCurrentFrame.hdr0 >> 24;
fTo[1] = fCurrentFrame.hdr0 >> 16;
fTo[2] = fCurrentFrame.hdr0 >> 8;
fTo[3] = fCurrentFrame.hdr0;
getBytes(&fTo[4], frameSize-4);
skipBytes(numTruncatedBytes);
return frameSize;
} catch (int /*e*/) {
#ifdef DEBUG
fUsingSource->envir() << "AC3AudioStreamParser::parseFrame() EXCEPTION (This is normal behavior - *not* an error)\n";
#endif
return 0; // the parsing got interrupted
}
}
void AC3AudioStreamParser::readAndSaveAFrame() {
unsigned const maxAC3FrameSize = 4000;
fSavedFrame = new unsigned char[maxAC3FrameSize];
fSavedFrameSize = 0;
fSavedFrameFlag = 0;
fUsingSource->getNextFrame(fSavedFrame, maxAC3FrameSize,
afterGettingSavedFrame, this,
onSavedFrameClosure, this);
fUsingSource->envir().taskScheduler().doEventLoop(&fSavedFrameFlag);
}
void AC3AudioStreamParser
::afterGettingSavedFrame(void* clientData, unsigned frameSize,
unsigned /*numTruncatedBytes*/,
struct timeval /*presentationTime*/,
unsigned /*durationInMicroseconds*/) {
AC3AudioStreamParser* parser = (AC3AudioStreamParser*)clientData;
parser->afterGettingSavedFrame1(frameSize);
}
void AC3AudioStreamParser
::afterGettingSavedFrame1(unsigned frameSize) {
fSavedFrameSize = frameSize;
fSavedFrameFlag = ~0;
}
void AC3AudioStreamParser::onSavedFrameClosure(void* clientData) {
AC3AudioStreamParser* parser = (AC3AudioStreamParser*)clientData;
parser->onSavedFrameClosure1();
}
void AC3AudioStreamParser::onSavedFrameClosure1() {
delete[] fSavedFrame; fSavedFrame = NULL;
fSavedFrameSize = 0;
fSavedFrameFlag = ~0;
}