211 lines
7.4 KiB
C++
211 lines
7.4 KiB
C++
|
/**********
|
||
|
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.
|
||
|
// Abstract class for parsing a byte stream
|
||
|
// Implementation
|
||
|
|
||
|
#include "StreamParser.hh"
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#define BANK_SIZE 80000
|
||
|
|
||
|
void StreamParser::flushInput() {
|
||
|
fCurParserIndex = fSavedParserIndex = 0;
|
||
|
fSavedRemainingUnparsedBits = fRemainingUnparsedBits = 0;
|
||
|
fTotNumValidBytes = 0;
|
||
|
}
|
||
|
|
||
|
StreamParser::StreamParser(FramedSource* inputSource,
|
||
|
FramedSource::onCloseFunc* onInputCloseFunc,
|
||
|
void* onInputCloseClientData,
|
||
|
clientContinueFunc* clientContinueFunc,
|
||
|
void* clientContinueClientData)
|
||
|
: fInputSource(inputSource), fClientOnInputCloseFunc(onInputCloseFunc),
|
||
|
fClientOnInputCloseClientData(onInputCloseClientData),
|
||
|
fClientContinueFunc(clientContinueFunc),
|
||
|
fClientContinueClientData(clientContinueClientData),
|
||
|
fSavedParserIndex(0), fSavedRemainingUnparsedBits(0),
|
||
|
fCurParserIndex(0), fRemainingUnparsedBits(0),
|
||
|
fTotNumValidBytes(0), fHaveSeenEOF(False) {
|
||
|
fBank[0] = new unsigned char[BANK_SIZE];
|
||
|
fBank[1] = new unsigned char[BANK_SIZE];
|
||
|
fCurBankNum = 0;
|
||
|
fCurBank = fBank[fCurBankNum];
|
||
|
|
||
|
fLastSeenPresentationTime.tv_sec = 0; fLastSeenPresentationTime.tv_usec = 0;
|
||
|
}
|
||
|
|
||
|
StreamParser::~StreamParser() {
|
||
|
delete[] fBank[0]; delete[] fBank[1];
|
||
|
}
|
||
|
|
||
|
void StreamParser::saveParserState() {
|
||
|
fSavedParserIndex = fCurParserIndex;
|
||
|
fSavedRemainingUnparsedBits = fRemainingUnparsedBits;
|
||
|
}
|
||
|
|
||
|
void StreamParser::restoreSavedParserState() {
|
||
|
fCurParserIndex = fSavedParserIndex;
|
||
|
fRemainingUnparsedBits = fSavedRemainingUnparsedBits;
|
||
|
}
|
||
|
|
||
|
void StreamParser::skipBits(unsigned numBits) {
|
||
|
if (numBits <= fRemainingUnparsedBits) {
|
||
|
fRemainingUnparsedBits -= numBits;
|
||
|
} else {
|
||
|
numBits -= fRemainingUnparsedBits;
|
||
|
|
||
|
unsigned numBytesToExamine = (numBits+7)/8; // round up
|
||
|
ensureValidBytes(numBytesToExamine);
|
||
|
fCurParserIndex += numBytesToExamine;
|
||
|
|
||
|
fRemainingUnparsedBits = 8*numBytesToExamine - numBits;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned StreamParser::getBits(unsigned numBits) {
|
||
|
if (numBits <= fRemainingUnparsedBits) {
|
||
|
unsigned char lastByte = *lastParsed();
|
||
|
lastByte >>= (fRemainingUnparsedBits - numBits);
|
||
|
fRemainingUnparsedBits -= numBits;
|
||
|
|
||
|
return (unsigned)lastByte &~ ((~0u)<<numBits);
|
||
|
} else {
|
||
|
unsigned char lastByte;
|
||
|
if (fRemainingUnparsedBits > 0) {
|
||
|
lastByte = *lastParsed();
|
||
|
} else {
|
||
|
lastByte = 0;
|
||
|
}
|
||
|
|
||
|
unsigned remainingBits = numBits - fRemainingUnparsedBits; // > 0
|
||
|
|
||
|
// For simplicity, read the next 4 bytes, even though we might not
|
||
|
// need all of them here:
|
||
|
unsigned result = test4Bytes();
|
||
|
|
||
|
result >>= (32 - remainingBits);
|
||
|
result |= (lastByte << remainingBits);
|
||
|
if (numBits < 32) result &=~ ((~0u)<<numBits);
|
||
|
|
||
|
unsigned const numRemainingBytes = (remainingBits+7)/8;
|
||
|
fCurParserIndex += numRemainingBytes;
|
||
|
fRemainingUnparsedBits = 8*numRemainingBytes - remainingBits;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned StreamParser::bankSize() const {
|
||
|
return BANK_SIZE;
|
||
|
}
|
||
|
|
||
|
#define NO_MORE_BUFFERED_INPUT 1
|
||
|
|
||
|
void StreamParser::ensureValidBytes1(unsigned numBytesNeeded) {
|
||
|
// We need to read some more bytes from the input source.
|
||
|
// First, clarify how much data to ask for:
|
||
|
unsigned maxInputFrameSize = fInputSource->maxFrameSize();
|
||
|
if (maxInputFrameSize > numBytesNeeded) numBytesNeeded = maxInputFrameSize;
|
||
|
|
||
|
// First, check whether these new bytes would overflow the current
|
||
|
// bank. If so, start using a new bank now.
|
||
|
if (fCurParserIndex + numBytesNeeded > BANK_SIZE) {
|
||
|
// Swap banks, but save any still-needed bytes from the old bank:
|
||
|
unsigned numBytesToSave = fTotNumValidBytes - fSavedParserIndex;
|
||
|
unsigned char const* from = &curBank()[fSavedParserIndex];
|
||
|
|
||
|
fCurBankNum = (fCurBankNum + 1)%2;
|
||
|
fCurBank = fBank[fCurBankNum];
|
||
|
memmove(curBank(), from, numBytesToSave);
|
||
|
fCurParserIndex = fCurParserIndex - fSavedParserIndex;
|
||
|
fSavedParserIndex = 0;
|
||
|
fTotNumValidBytes = numBytesToSave;
|
||
|
}
|
||
|
|
||
|
// ASSERT: fCurParserIndex + numBytesNeeded > fTotNumValidBytes
|
||
|
// && fCurParserIndex + numBytesNeeded <= BANK_SIZE
|
||
|
if (fCurParserIndex + numBytesNeeded > BANK_SIZE) {
|
||
|
// If this happens, it means that we have too much saved parser state.
|
||
|
// To fix this, increase BANK_SIZE as appropriate.
|
||
|
fInputSource->envir() << "StreamParser internal error ("
|
||
|
<< fCurParserIndex << " + "
|
||
|
<< numBytesNeeded << " > "
|
||
|
<< BANK_SIZE << ")\n";
|
||
|
fInputSource->envir().internalError();
|
||
|
}
|
||
|
|
||
|
// Try to read as many new bytes as will fit in the current bank:
|
||
|
unsigned maxNumBytesToRead = BANK_SIZE - fTotNumValidBytes;
|
||
|
fInputSource->getNextFrame(&curBank()[fTotNumValidBytes],
|
||
|
maxNumBytesToRead,
|
||
|
afterGettingBytes, this,
|
||
|
onInputClosure, this);
|
||
|
|
||
|
throw NO_MORE_BUFFERED_INPUT;
|
||
|
}
|
||
|
|
||
|
void StreamParser::afterGettingBytes(void* clientData,
|
||
|
unsigned numBytesRead,
|
||
|
unsigned /*numTruncatedBytes*/,
|
||
|
struct timeval presentationTime,
|
||
|
unsigned /*durationInMicroseconds*/){
|
||
|
StreamParser* parser = (StreamParser*)clientData;
|
||
|
if (parser != NULL) parser->afterGettingBytes1(numBytesRead, presentationTime);
|
||
|
}
|
||
|
|
||
|
void StreamParser::afterGettingBytes1(unsigned numBytesRead, struct timeval presentationTime) {
|
||
|
// Sanity check: Make sure we didn't get too many bytes for our bank:
|
||
|
if (fTotNumValidBytes + numBytesRead > BANK_SIZE) {
|
||
|
fInputSource->envir()
|
||
|
<< "StreamParser::afterGettingBytes() warning: read "
|
||
|
<< numBytesRead << " bytes; expected no more than "
|
||
|
<< BANK_SIZE - fTotNumValidBytes << "\n";
|
||
|
}
|
||
|
|
||
|
fLastSeenPresentationTime = presentationTime;
|
||
|
|
||
|
unsigned char* ptr = &curBank()[fTotNumValidBytes];
|
||
|
fTotNumValidBytes += numBytesRead;
|
||
|
|
||
|
// Continue our original calling source where it left off:
|
||
|
restoreSavedParserState();
|
||
|
// Sigh... this is a crock; things would have been a lot simpler
|
||
|
// here if we were using threads, with synchronous I/O...
|
||
|
fClientContinueFunc(fClientContinueClientData, ptr, numBytesRead, presentationTime);
|
||
|
}
|
||
|
|
||
|
void StreamParser::onInputClosure(void* clientData) {
|
||
|
StreamParser* parser = (StreamParser*)clientData;
|
||
|
if (parser != NULL) parser->onInputClosure1();
|
||
|
}
|
||
|
|
||
|
void StreamParser::onInputClosure1() {
|
||
|
if (!fHaveSeenEOF) {
|
||
|
// We're hitting EOF for the first time. Set our 'EOF' flag, and continue parsing, as if we'd just read 0 bytes of data.
|
||
|
// This allows the parser to re-parse any remaining unparsed data (perhaps while testing for EOF at the end):
|
||
|
fHaveSeenEOF = True;
|
||
|
afterGettingBytes1(0, fLastSeenPresentationTime);
|
||
|
} else {
|
||
|
// We're hitting EOF for the second time. Now, we handle the source input closure:
|
||
|
fHaveSeenEOF = False;
|
||
|
if (fClientOnInputCloseFunc != NULL) (*fClientOnInputCloseFunc)(fClientOnInputCloseClientData);
|
||
|
}
|
||
|
}
|