blob: b5725dd9e947099a9441ef2ecafa5cd945304087 [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 MPEG (1,2) audio elementary stream into frames
// Implementation
#include "MPEG1or2AudioStreamFramer.hh"
#include "StreamParser.hh"
#include "MP3Internals.hh"
#include <GroupsockHelper.hh>
////////// MPEG1or2AudioStreamParser definition //////////
class MPEG1or2AudioStreamParser: public StreamParser {
public:
MPEG1or2AudioStreamParser(MPEG1or2AudioStreamFramer* usingSource,
FramedSource* inputSource);
virtual ~MPEG1or2AudioStreamParser();
public:
unsigned parse(unsigned& numTruncatedBytes);
// returns the size of the frame that was acquired, or 0 if none was
void registerReadInterest(unsigned char* to, unsigned maxSize);
MP3FrameParams const& currentFrame() const { return fCurrentFrame; }
private:
unsigned char* fTo;
unsigned fMaxSize;
// Parameters of the most recently read frame:
MP3FrameParams fCurrentFrame; // also works for layer I or II
};
////////// MPEG1or2AudioStreamFramer implementation //////////
MPEG1or2AudioStreamFramer
::MPEG1or2AudioStreamFramer(UsageEnvironment& env, FramedSource* inputSource,
Boolean syncWithInputSource)
: FramedFilter(env, inputSource),
fSyncWithInputSource(syncWithInputSource) {
reset();
fParser = new MPEG1or2AudioStreamParser(this, inputSource);
}
MPEG1or2AudioStreamFramer::~MPEG1or2AudioStreamFramer() {
delete fParser;
}
MPEG1or2AudioStreamFramer*
MPEG1or2AudioStreamFramer::createNew(UsageEnvironment& env,
FramedSource* inputSource,
Boolean syncWithInputSource) {
// Need to add source type checking here??? #####
return new MPEG1or2AudioStreamFramer(env, inputSource, syncWithInputSource);
}
void MPEG1or2AudioStreamFramer::flushInput() {
reset();
fParser->flushInput();
}
void MPEG1or2AudioStreamFramer::reset() {
// Use the current wallclock time as the initial 'presentation time':
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
resetPresentationTime(timeNow);
}
void MPEG1or2AudioStreamFramer
::resetPresentationTime(struct timeval newPresentationTime) {
fNextFramePresentationTime = newPresentationTime;
}
void MPEG1or2AudioStreamFramer::doGetNextFrame() {
fParser->registerReadInterest(fTo, fMaxSize);
continueReadProcessing();
}
#define MILLION 1000000
static unsigned const numSamplesByLayer[4] = {0, 384, 1152, 1152};
struct timeval MPEG1or2AudioStreamFramer::currentFramePlayTime() const {
MP3FrameParams const& fr = fParser->currentFrame();
unsigned const numSamples = numSamplesByLayer[fr.layer];
struct timeval result;
unsigned const freq = fr.samplingFreq*(1 + fr.isMPEG2);
if (freq == 0) {
result.tv_sec = 0;
result.tv_usec = 0;
return result;
}
// result is numSamples/freq
unsigned const uSeconds
= ((numSamples*2*MILLION)/freq + 1)/2; // rounds to nearest integer
result.tv_sec = uSeconds/MILLION;
result.tv_usec = uSeconds%MILLION;
return result;
}
void MPEG1or2AudioStreamFramer
::continueReadProcessing(void* clientData,
unsigned char* /*ptr*/, unsigned /*size*/,
struct timeval presentationTime) {
MPEG1or2AudioStreamFramer* framer = (MPEG1or2AudioStreamFramer*)clientData;
if (framer->fSyncWithInputSource) {
framer->resetPresentationTime(presentationTime);
}
framer->continueReadProcessing();
}
void MPEG1or2AudioStreamFramer::continueReadProcessing() {
unsigned acquiredFrameSize = fParser->parse(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.
}
}
////////// MPEG1or2AudioStreamParser implementation //////////
MPEG1or2AudioStreamParser
::MPEG1or2AudioStreamParser(MPEG1or2AudioStreamFramer* usingSource,
FramedSource* inputSource)
: StreamParser(inputSource, FramedSource::handleClosure, usingSource,
&MPEG1or2AudioStreamFramer::continueReadProcessing, usingSource) {
}
MPEG1or2AudioStreamParser::~MPEG1or2AudioStreamParser() {
}
void MPEG1or2AudioStreamParser::registerReadInterest(unsigned char* to,
unsigned maxSize) {
fTo = to;
fMaxSize = maxSize;
}
unsigned MPEG1or2AudioStreamParser::parse(unsigned& numTruncatedBytes) {
try {
saveParserState();
// We expect a MPEG audio header (first 11 bits set to 1) at the start:
while (((fCurrentFrame.hdr = test4Bytes())&0xFFE00000) != 0xFFE00000) {
skipBytes(1);
saveParserState();
}
fCurrentFrame.setParamsFromHeader();
// Copy the frame to the requested destination:
unsigned frameSize = fCurrentFrame.frameSize + 4; // include header
if (frameSize > fMaxSize) {
numTruncatedBytes = frameSize - fMaxSize;
frameSize = fMaxSize;
} else {
numTruncatedBytes = 0;
}
getBytes(fTo, frameSize);
skipBytes(numTruncatedBytes);
return frameSize;
} catch (int /*e*/) {
#ifdef DEBUG
fprintf(stderr, "MPEG1or2AudioStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");
#endif
return 0; // the parsing got interrupted
}
}