blob: b27a6a15b018dabc08c74c6f2d576a64078f283d [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.
// Author Bernhard Feiten
// A filter that breaks up an H.263plus video stream into frames.
// Based on MPEG4IP/mp4creator/h263.c
#include "H263plusVideoStreamParser.hh"
#include "H263plusVideoStreamFramer.hh"
//#include <string.h>
//#include "GroupsockHelper.hh"
H263plusVideoStreamParser::H263plusVideoStreamParser(
H263plusVideoStreamFramer* usingSource,
FramedSource* inputSource)
: StreamParser(inputSource,
FramedSource::handleClosure,
usingSource,
&H263plusVideoStreamFramer::continueReadProcessing,
usingSource),
fUsingSource(usingSource),
fnextTR(0),
fcurrentPT(0)
{
memset(fStates, 0, sizeof(fStates));
memset(&fNextInfo, 0, sizeof(fNextInfo));
memset(&fCurrentInfo, 0, sizeof(fCurrentInfo));
memset(&fMaxBitrateCtx, 0, sizeof(fMaxBitrateCtx));
memset(fNextHeader,0, H263_REQUIRE_HEADER_SIZE_BYTES);
}
///////////////////////////////////////////////////////////////////////////////
H263plusVideoStreamParser::~H263plusVideoStreamParser()
{
}
///////////////////////////////////////////////////////////////////////////////
void H263plusVideoStreamParser::restoreSavedParserState()
{
StreamParser::restoreSavedParserState();
fTo = fSavedTo;
fNumTruncatedBytes = fSavedNumTruncatedBytes;
}
///////////////////////////////////////////////////////////////////////////////
void H263plusVideoStreamParser::setParseState()
{
fSavedTo = fTo;
fSavedNumTruncatedBytes = fNumTruncatedBytes;
saveParserState(); // Needed for the parsing process in StreamParser
}
///////////////////////////////////////////////////////////////////////////////
void H263plusVideoStreamParser::registerReadInterest(
unsigned char* to,
unsigned maxSize)
{
fStartOfFrame = fTo = fSavedTo = to;
fLimit = to + maxSize;
fMaxSize = maxSize;
fNumTruncatedBytes = fSavedNumTruncatedBytes = 0;
}
///////////////////////////////////////////////////////////////////////////////
// parse() , derived from H263Creator of MPEG4IP, h263.c
unsigned H263plusVideoStreamParser::parse(u_int64_t & currentDuration)
{
// u_int8_t frameBuffer[H263_BUFFER_SIZE]; // The input buffer
// Pointer which tells LoadNextH263Object where to read data to
// u_int8_t* pFrameBuffer = fTo + H263_REQUIRE_HEADER_SIZE_BYTES;
u_int32_t frameSize; // The current frame size
// Pointer to receive address of the header data
// u_int8_t* pCurrentHeader;// = pFrameBuffer;
// u_int64_t currentDuration; // The current frame's duration
u_int8_t trDifference; // The current TR difference
// The previous TR difference
// u_int8_t prevTrDifference = H263_BASIC_FRAME_RATE;
// u_int64_t totalDuration = 0;// Duration accumulator
// u_int64_t avgBitrate; // Average bitrate
// u_int64_t totalBytes = 0; // Size accumulator
try // The get data routines of the class FramedFilter returns an error when
{ // the buffer is empty. This occurs at the beginning and at the end of the file.
fCurrentInfo = fNextInfo;
// Parse 1 frame
// For the first time, only the first frame's header is returned.
// The second time the full first frame is returned
frameSize = parseH263Frame();
currentDuration = 0;
if ((frameSize > 0)){
// We were able to acquire a frame from the input.
// Parse the returned frame header (if any)
if (!ParseShortHeader(fTo, &fNextInfo)) {
#ifdef DEBUG
fprintf(stderr,"H263plusVideoStreamParser: Fatal error\n");
#endif
}
trDifference = GetTRDifference(fNextInfo.tr, fCurrentInfo.tr);
// calculate the current frame duration
currentDuration = CalculateDuration(trDifference);
// Accumulate the frame's size and duration for avgBitrate calculation
//totalDuration += currentDuration;
//totalBytes += frameSize;
// If needed, recalculate bitrate information
// if (h263Bitrates)
//GetMaxBitrate(&fMaxBitrateCtx, frameSize, prevTrDifference);
//prevTrDifference = trDifference;
setParseState(); // Needed for the parsing process in StreamParser
}
} catch (int /*e*/) {
#ifdef DEBUG
fprintf(stderr, "H263plusVideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");
#endif
frameSize=0;
}
return frameSize;
}
///////////////////////////////////////////////////////////////////////////////
// parseH263Frame derived from LoadNextH263Object of MPEG4IP
// - service routine that reads a single frame from the input file.
// It shall fill the input buffer with data up until - and including - the
// next start code and shall report back both the number of bytes read and a
// pointer to the next start code. The first call to this function shall only
// yield a pointer with 0 data bytes and the last call to this function shall
// only yield data bytes with a NULL pointer as the next header.
//
// TODO: This function only supports valid bit streams. Upon error, it fails
// without the possibility to recover. A Better idea would be to skip frames
// until a parsable frame is read from the file.
//
// Parameters:
// ppNextHeader - output parameter that upon return points to the location
// of the next frame's head in the buffer.
// This pointer shall be NULL for the last frame read.
// Returns the total number of bytes read.
// Uses FrameFileSource intantiated by constructor.
///////////////////////////////////////////////////////////////////////////////
int H263plusVideoStreamParser::parseH263Frame( )
{
char row = 0;
u_int8_t * bufferIndex = fTo;
// The buffer end which will allow the loop to leave place for
// the additionalBytesNeeded
u_int8_t * bufferEnd = fTo + fMaxSize - ADDITIONAL_BYTES_NEEDED - 1;
memcpy(fTo, fNextHeader, H263_REQUIRE_HEADER_SIZE_BYTES);
bufferIndex += H263_REQUIRE_HEADER_SIZE_BYTES;
// The state table and the following loop implements a state machine enabling
// us to read bytes from the file until (and inclusing) the requested
// start code (00 00 8X) is found
// Initialize the states array, if it hasn't been initialized yet...
if (!fStates[0][0]) {
// One 00 was read
fStates[0][0] = 1;
// Two sequential 0x00 ware read
fStates[1][0] = fStates[2][0] = 2;
// A full start code was read
fStates[2][128] = fStates[2][129] = fStates[2][130] = fStates[2][131] = -1;
}
// Read data from file into the output buffer until either a start code
// is found, or the end of file has been reached.
do {
*bufferIndex = get1Byte();
} while ((bufferIndex < bufferEnd) && // We have place in the buffer
((row = fStates[(unsigned char)row][*(bufferIndex++)]) != -1)); // Start code was not found
if (row != -1) {
fprintf(stderr, "%s: Buffer too small (%u)\n",
"h263reader:", bufferEnd - fTo + ADDITIONAL_BYTES_NEEDED);
return 0;
}
// Cool ... now we have a start code
// Now we just have to read the additionalBytesNeeded
getBytes(bufferIndex, ADDITIONAL_BYTES_NEEDED);
memcpy(fNextHeader, bufferIndex - H263_STARTCODE_SIZE_BYTES, H263_REQUIRE_HEADER_SIZE_BYTES);
int sz = bufferIndex - fTo - H263_STARTCODE_SIZE_BYTES;
if (sz == 5) // first frame
memcpy(fTo, fTo+H263_REQUIRE_HEADER_SIZE_BYTES, H263_REQUIRE_HEADER_SIZE_BYTES);
return sz;
}
////////////////////////////////////////////////////////////////////////////////
// ParseShortHeader - service routine that accepts a buffer containing a frame
// header and extracts relevant codec information from it.
//
// NOTE: the first bit in the following commnets is 0 (zero).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PSC (Picture Start Code=22 bits) | (TR=8 bits) | >
// |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0| |1 0>
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// < (PTYPE=13 bits) |
// <. . .|(FMT)|Z|. . . .|
// +-+-+-+-+-+-+-+-+-+-+-+
// -> PTYPE.FMT contains a width/height identification
// -> PTYPE.Z is 1 for P-Frames, 0 for I-Frames
// Note: When FMT is 111, there is an extended PTYPE...
//
// Inputs:
// headerBuffer - pointer to the current header buffer
// outputInfoStruct - pointer to the structure receiving the data
// Outputs:
// This function returns a structure of important codec-specific
// information (The Temporal Reference bits, width & height of the current
// frame and the sync - or "frame type" - bit. It reports success or
// failure to the calling function.
////////////////////////////////////////////////////////////////////////////////
bool H263plusVideoStreamParser::ParseShortHeader(
u_int8_t *headerBuffer,
H263INFO *outputInfoStruct)
{
u_int8_t fmt = 0;
// Extract temporal reference (TR) from the buffer (bits 22-29 inclusive)
outputInfoStruct->tr = (headerBuffer[2] << 6) & 0xC0; // 2 LS bits out of the 3rd byte
outputInfoStruct->tr |= (headerBuffer[3] >> 2) & 0x3F; // 6 MS bits out of the 4th byte
// Extract the FMT part of PTYPE from the buffer (bits 35-37 inclusive)
fmt = (headerBuffer[4] >> 2) & 0x07; // bits 3-5 ouf of the 5th byte
// If PTYPE is not supported, return a failure notice to the calling function
// FIXME: PLUSPTYPE is not supported
if (fmt == 0x07) {
return false;
}
// If PTYPE is supported, calculate the current width and height according to
// a predefined table
if (!GetWidthAndHeight(fmt, &(outputInfoStruct->width),
&(outputInfoStruct->height))) {
return false;
}
// Extract the frame-type bit, which is the 9th bit of PTYPE (bit 38)
outputInfoStruct->isSyncFrame = !(headerBuffer[4] & 0x02);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// GetMaxBitrate- service routine that accepts frame information and
// derives bitrate information from it. This function uses a sliding window
// technique to calculate the maximum bitrates in any window of 1 second
// inside the file.
// The sliding window is implemented with a table of bitrates for the last
// second (30 entries - one entry per TR unit).
//
// Inputs:
// ctx - context for this function
// frameSize - the size of the current frame in bytes
// frameTRDiff - the "duration" of the frame in TR units
// Outputs:
// This function returns the up-to-date maximum bitrate
////////////////////////////////////////////////////////////////////////////////
void H263plusVideoStreamParser::GetMaxBitrate( MaxBitrate_CTX *ctx,
u_int32_t frameSize,
u_int8_t frameTRDiff)
{
if (frameTRDiff == 0)
return;
// Calculate the current frame's bitrate as bits per TR unit (round the result
// upwards)
u_int32_t frameBitrate = frameSize * 8 / frameTRDiff + 1;
// for each TRdiff received,
while (frameTRDiff--) {
// Subtract the oldest bitrate entry from the current bitrate
ctx->windowBitrate -= ctx->bitrateTable[ctx->tableIndex];
// Update the oldest bitrate entry with the current frame's bitrate
ctx->bitrateTable[ctx->tableIndex] = frameBitrate;
// Add the current frame's bitrate to the current bitrate
ctx->windowBitrate += frameBitrate;
// Check if we have a new maximum bitrate
if (ctx->windowBitrate > ctx->maxBitrate) {
ctx->maxBitrate = ctx->windowBitrate;
}
// Advance the table index
// Wrapping around the bitrateTable size
ctx->tableIndex = (ctx->tableIndex + 1) %
( sizeof(ctx->bitrateTable) / sizeof(ctx->bitrateTable[0]) );
}
}
////////////////////////////////////////////////////////////////////////////////
// CalculateDuration - service routine that calculates the current frame's
// duration in milli-seconds using it's duration in TR units.
// - In order not to accumulate the calculation error, we are using the TR
// duration to calculate the current and the next frame's presentation time in
// milli-seconds.
//
// Inputs: trDiff - The current frame's duration in TR units
// Return: The current frame's duration in milli-seconds
////////////////////////////////////////////////////////////////////////////////
u_int64_t H263plusVideoStreamParser::CalculateDuration(u_int8_t trDiff)
{
u_int64_t nextPT; // The next frame's presentation time in milli-seconds
u_int64_t duration; // The current frame's duration in milli-seconds
fnextTR += trDiff;
// Calculate the next frame's presentation time, in milli-seconds
nextPT = (fnextTR * 1001) / H263_BASIC_FRAME_RATE;
// The frame's duration is the difference between the next presentation
// time and the current presentation time.
duration = nextPT - fcurrentPT;
// "Remember" the next presentation time for the next time this function is called
fcurrentPT = nextPT;
return duration;
}
////////////////////////////////////////////////////////////////////////////////
bool H263plusVideoStreamParser::GetWidthAndHeight( u_int8_t fmt,
u_int16_t *width,
u_int16_t *height)
{
// The 'fmt' corresponds to bits 5-7 of the PTYPE
static struct {
u_int16_t width;
u_int16_t height;
} const dimensionsTable[8] = {
{ 0, 0 }, // 000 - 0 - forbidden, generates an error
{ 128, 96 }, // 001 - 1 - Sub QCIF
{ 176, 144 }, // 010 - 2 - QCIF
{ 352, 288 }, // 011 - 3 - CIF
{ 704, 576 }, // 100 - 4 - 4CIF
{ 1409, 1152 }, // 101 - 5 - 16CIF
{ 0, 0 }, // 110 - 6 - reserved, generates an error
{ 0, 0 } // 111 - 7 - extended, not supported by profile 0
};
if (fmt > 7)
return false;
*width = dimensionsTable[fmt].width;
*height = dimensionsTable[fmt].height;
if (*width == 0)
return false;
return true;
}
////////////////////////////////////////////////////////////////////////////////
u_int8_t H263plusVideoStreamParser::GetTRDifference(
u_int8_t nextTR,
u_int8_t currentTR)
{
if (currentTR > nextTR) {
// Wrap around 255...
return nextTR + (256 - currentTR);
} else {
return nextTR - currentTR;
}
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// this is the h263.c file of MPEG4IP mp4creator
/*
#include "mp4creator.h"
// Default timescale for H.263 (1000ms)
#define H263_TIMESCALE 1000
// Default H263 frame rate (30fps)
#define H263_BASIC_FRAME_RATE 30
// Minimum number of bytes needed to parse an H263 header
#define H263_REQUIRE_HEADER_SIZE_BYTES 5
// Number of bytes the start code requries
#define H263_STARTCODE_SIZE_BYTES 3
// This is the input buffer's size. It should contain
// 1 frame with the following start code
#define H263_BUFFER_SIZE 256 * 1024
// The default max different (in %) betwqeen max and average bitrates
#define H263_DEFAULT_CBR_TOLERANCE 10
// The following structure holds information extracted from each frame's header:
typedef struct _H263INFO {
u_int8_t tr; // Temporal Reference, used in duration calculation
u_int16_t width; // Width of the picture
u_int16_t height; // Height of the picture
bool isSyncFrame; // Frame type (true = I frame = "sync" frame)
} H263INFO;
// Context for the GetMaxBitrate function
typedef struct _MaxBitrate_CTX {
u_int32_t bitrateTable[H263_BASIC_FRAME_RATE];// Window of 1 second
u_int32_t windowBitrate; // The bitrate of the current window
u_int32_t maxBitrate; // The up-to-date maximum bitrate
u_int32_t tableIndex; // The next TR unit to update
} MaxBitrate_CTX;
// Forward declarations:
static int LoadNextH263Object( FILE *inputFileHandle,
u_int8_t *frameBuffer,
u_int32_t *frameBufferSize,
u_int32_t additionalBytesNeeded,
u_int8_t **ppNextHeader);
static bool ParseShortHeader( u_int8_t *headerBuffer,
H263INFO *outputInfoStruct);
static u_int8_t GetTRDifference(u_int8_t nextTR,
u_int8_t currentTR);
static void GetMaxBitrate( MaxBitrate_CTX *ctx,
u_int32_t frameSize,
u_int8_t frameTRDiff);
static MP4Duration CalculateDuration(u_int8_t trDiff);
static bool GetWidthAndHeight( u_int8_t fmt,
u_int16_t *width,
u_int16_t *height);
static char states[3][256];
/ *
* H263Creator - Main function
* Inputs:
* outputFileHandle - The handle of the output file
* inputFileHandle - The handle of the input file
* Codec-specific parameters:
* H263Level - H.263 Level used for this track
* H263Profile - H.263 Profile used for this track
* H263Bitrates - A Parameter indicating whether the function
* should calculate H263 bitrates or not.
* cbrTolerance - CBR tolerance indicates when to set the
* average bitrate.
* Outputs:
* This function returns either the track ID of the newly added track upon
* success or a predefined value representing an erroneous state.
* /
MP4TrackId H263Creator(MP4FileHandle outputFileHandle,
FILE* inputFileHandle,
u_int8_t h263Profile,
u_int8_t h263Level,
bool h263Bitrates,
u_int8_t cbrTolerance)
{
H263INFO nextInfo; // Holds information about the next frame
H263INFO currentInfo;// Holds information about the current frame
MaxBitrate_CTX maxBitrateCtx;// Context for the GetMaxBitrate function
memset(&nextInfo, 0, sizeof(nextInfo));
memset(&currentInfo, 0, sizeof(currentInfo));
memset(&maxBitrateCtx, 0, sizeof(maxBitrateCtx));
memset(states, 0, sizeof(states));
u_int8_t frameBuffer[H263_BUFFER_SIZE]; // The input buffer
// Pointer which tells LoadNextH263Object where to read data to
u_int8_t* pFrameBuffer = frameBuffer + H263_REQUIRE_HEADER_SIZE_BYTES;
u_int32_t frameSize; // The current frame size
// Pointer to receive address of the header data
u_int8_t* pCurrentHeader = pFrameBuffer;
MP4Duration currentDuration; // The current frame's duration
u_int8_t trDifference; // The current TR difference
// The previous TR difference
u_int8_t prevTrDifference = H263_BASIC_FRAME_RATE;
MP4Duration totalDuration = 0;// Duration accumulator
MP4Duration avgBitrate; // Average bitrate
u_int64_t totalBytes = 0; // Size accumulator
MP4TrackId trackId = MP4_INVALID_TRACK_ID; // Our MP4 track
bool stay = true; // loop flag
while (stay) {
currentInfo = nextInfo;
memmove(frameBuffer, pCurrentHeader, H263_REQUIRE_HEADER_SIZE_BYTES);
frameSize = H263_BUFFER_SIZE - H263_REQUIRE_HEADER_SIZE_BYTES;
// Read 1 frame and the next frame's header from the file.
// For the first frame, only the first frame's header is returned.
// For the last frame, only the last frame's data is returned.
if (! LoadNextH263Object(inputFileHandle, pFrameBuffer, &frameSize,
H263_REQUIRE_HEADER_SIZE_BYTES - H263_STARTCODE_SIZE_BYTES,
&pCurrentHeader))
break; // Fatal error ...
if (pCurrentHeader) {
// Parse the returned frame header (if any)
if (!ParseShortHeader(pCurrentHeader, &nextInfo))
break; // Fatal error
trDifference = GetTRDifference(nextInfo.tr, currentInfo.tr);
} else {
// This is the last frame ... we have to fake the trDifference ...
trDifference = 1;
// No header data has been read at this iteration, so we have to manually
// add the frame's header we read at the previous iteration.
// Note that LoadNextH263Object returns the number of bytes read, which
// are the current frame's data and the next frame's header
frameSize += H263_REQUIRE_HEADER_SIZE_BYTES;
// There is no need for the next iteration ...
stay = false;
}
// If this is the first iteration ...
if (currentInfo.width == 0) {
// If we have more data than just the header
if ((frameSize > H263_REQUIRE_HEADER_SIZE_BYTES) ||
!pCurrentHeader) // Or no header at all
break; // Fatal error
else
continue; // We have only the first frame's header ...
}
if (trackId == MP4_INVALID_TRACK_ID) {
// If a track has not been added yet, add the track to the file.
trackId = MP4AddH263VideoTrack(outputFileHandle, H263_TIMESCALE,
0, currentInfo.width, currentInfo.height,
h263Level, h263Profile, 0, 0);
if (trackId == MP4_INVALID_TRACK_ID)
break; // Fatal error
}
// calculate the current frame duration
currentDuration = CalculateDuration(trDifference);
// Write the current frame to the file.
if (!MP4WriteSample(outputFileHandle, trackId, frameBuffer, frameSize,
currentDuration, 0, currentInfo.isSyncFrame))
break; // Fatal error
// Accumulate the frame's size and duration for avgBitrate calculation
totalDuration += currentDuration;
totalBytes += frameSize;
// If needed, recalculate bitrate information
if (h263Bitrates)
GetMaxBitrate(&maxBitrateCtx, frameSize, prevTrDifference);
prevTrDifference = trDifference;
} // while (stay)
// If this is the last frame,
if (!stay) {
// If needed and possible, update bitrate information in the file
if (h263Bitrates && totalDuration) {
avgBitrate = (totalBytes * 8 * H263_TIMESCALE) / totalDuration;
if (cbrTolerance == 0)
cbrTolerance = H263_DEFAULT_CBR_TOLERANCE;
// Same as: if (maxBitrate / avgBitrate > (cbrTolerance + 100) / 100.0)
if (maxBitrateCtx.maxBitrate * 100 > (cbrTolerance + 100) * avgBitrate)
avgBitrate = 0;
MP4SetH263Bitrates(outputFileHandle, trackId,
avgBitrate, maxBitrateCtx.maxBitrate);
}
// Return the newly added track ID
return trackId;
}
// If we got to here... something went wrong ...
fprintf(stderr,
"%s: Could not parse input file, invalid video stream?\n", ProgName);
// Upon failure, delete the newly added track if it has been added
if (trackId != MP4_INVALID_TRACK_ID) {
MP4DeleteTrack(outputFileHandle, trackId);
}
return MP4_INVALID_TRACK_ID;
}
/ *
* LoadNextH263Object - service routine that reads a single frame from the input
* file. It shall fill the input buffer with data up until - and including - the
* next start code and shall report back both the number of bytes read and a
* pointer to the next start code. The first call to this function shall only
* yield a pointer with 0 data bytes and the last call to this function shall
* only yield data bytes with a NULL pointer as the next header.
*
* TODO: This function only supports valid bit streams. Upon error, it fails
* without the possibility to recover. A Better idea would be to skip frames
* until a parsable frame is read from the file.
*
* Parameters:
* inputFileHandle - The handle of the input file
* frameBuffer - buffer where to place read data
* frameBufferSize - in/out parameter indicating the size of the buffer on
* entry and the number of bytes copied to the buffer upon
* return
* additionalBytesNeeded - indicates how many additional bytes are to be read
* from the next frame's header (over the 3 bytes that
* are already read).
* NOTE: This number MUST be > 0
* ppNextHeader - output parameter that upon return points to the location
* of the next frame's head in the buffer
* Outputs:
* This function returns two pieces of information:
* 1. The total number of bytes read.
* 2. A Pointer to the header of the next frame. This pointer shall be NULL
* for the last frame read.
* /
static int LoadNextH263Object( FILE *inputFileHandle,
u_int8_t *frameBuffer,
u_int32_t *frameBufferSize,
u_int32_t additionalBytesNeeded,
u_int8_t **ppNextHeader)
{
// This table and the following loop implements a state machine enabling
// us to read bytes from the file untill (and inclusing) the requested
// start code (00 00 8X) is found
char row = 0;
u_int8_t *bufferStart = frameBuffer;
// The buffer end which will allow the loop to leave place for
// the additionalBytesNeeded
u_int8_t *bufferEnd = frameBuffer + *frameBufferSize -
additionalBytesNeeded - 1;
// Initialize the states array, if it hasn't been initialized yet...
if (!states[0][0]) {
// One 00 was read
states[0][0] = 1;
// Two sequential 0x00 ware read
states[1][0] = states[2][0] = 2;
// A full start code was read
states[2][128] = states[2][129] = states[2][130] = states[2][131] = -1;
}
// Read data from file into the output buffer until either a start code
// is found, or the end of file has been reached.
do {
if (fread(frameBuffer, 1, 1, inputFileHandle) != 1){
// EOF or other error before we got a start code
*ppNextHeader = NULL;
*frameBufferSize = frameBuffer - bufferStart;
return 1;
}
} while ((frameBuffer < bufferEnd) && // We have place in the buffer
((row = states[row][*(frameBuffer++)]) != -1)); // Start code was not found
if (row != -1) {
fprintf(stderr, "%s: Buffer too small (%u)\n",
ProgName, bufferEnd - bufferStart + additionalBytesNeeded);
return 0;
}
// Cool ... now we have a start code
*ppNextHeader = frameBuffer - H263_STARTCODE_SIZE_BYTES;
*frameBufferSize = frameBuffer - bufferStart + additionalBytesNeeded;
// Now we just have to read the additionalBytesNeeded
if(fread(frameBuffer, additionalBytesNeeded, 1, inputFileHandle) != 1) {
/// We got a start code but can't read additionalBytesNeeded ... that's a fatal error
fprintf(stderr, "%s: Invalid H263 bitstream\n", ProgName);
return 0;
}
return 1;
}
/ *
* ParseShortHeader - service routine that accepts a buffer containing a frame
* header and extracts relevant codec information from it.
*
* NOTE: the first bit in the following commnets is 0 (zero).
*
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | PSC (Picture Start Code=22 bits) | (TR=8 bits) | >
* |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0| |1 0>
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* < (PTYPE=13 bits) |
* <. . .|(FMT)|Z|. . . .|
* +-+-+-+-+-+-+-+-+-+-+-+
* -> PTYPE.FMT contains a width/height identification
* -> PTYPE.Z is 1 for P-Frames, 0 for I-Frames
* Note: When FMT is 111, there is an extended PTYPE...
*
* Inputs:
* headerBuffer - pointer to the current header buffer
* outputInfoStruct - pointer to the structure receiving the data
* Outputs:
* This function returns a structure of important codec-specific
* information (The Temporal Reference bits, width & height of the current
* frame and the sync - or "frame type" - bit. It reports success or
* failure to the calling function.
* /
static bool ParseShortHeader( u_int8_t *headerBuffer,
H263INFO *outputInfoStruct)
{
u_int8_t fmt = 0;
// Extract temporal reference (TR) from the buffer (bits 22-29 inclusive)
outputInfoStruct->tr = (headerBuffer[2] << 6) & 0xC0; // 2 LS bits out of the 3rd byte
outputInfoStruct->tr |= (headerBuffer[3] >> 2) & 0x3F; // 6 MS bits out of the 4th byte
// Extract the FMT part of PTYPE from the buffer (bits 35-37 inclusive)
fmt = (headerBuffer[4] >> 2) & 0x07; // bits 3-5 ouf of the 5th byte
// If PTYPE is not supported, return a failure notice to the calling function
// FIXME: PLUSPTYPE is not supported
if (fmt == 0x07) {
return false;
}
// If PTYPE is supported, calculate the current width and height according to
// a predefined table
if (!GetWidthAndHeight(fmt, &(outputInfoStruct->width),
&(outputInfoStruct->height))) {
return false;
}
// Extract the frame-type bit, which is the 9th bit of PTYPE (bit 38)
outputInfoStruct->isSyncFrame = !(headerBuffer[4] & 0x02);
return true;
}
/ *
* GetMaxBitrate- service routine that accepts frame information and
* derives bitrate information from it. This function uses a sliding window
* technique to calculate the maximum bitrates in any window of 1 second
* inside the file.
* The sliding window is implemented with a table of bitrates for the last
* second (30 entries - one entry per TR unit).
*
* Inputs:
* ctx - context for this function
* frameSize - the size of the current frame in bytes
* frameTRDiff - the "duration" of the frame in TR units
* Outputs:
* This function returns the up-to-date maximum bitrate
* /
static void GetMaxBitrate( MaxBitrate_CTX *ctx,
u_int32_t frameSize,
u_int8_t frameTRDiff)
{
if (frameTRDiff == 0)
return;
// Calculate the current frame's bitrate as bits per TR unit (round the result
// upwards)
u_int32_t frameBitrate = frameSize * 8 / frameTRDiff + 1;
// for each TRdiff received,
while (frameTRDiff--) {
// Subtract the oldest bitrate entry from the current bitrate
ctx->windowBitrate -= ctx->bitrateTable[ctx->tableIndex];
// Update the oldest bitrate entry with the current frame's bitrate
ctx->bitrateTable[ctx->tableIndex] = frameBitrate;
// Add the current frame's bitrate to the current bitrate
ctx->windowBitrate += frameBitrate;
// Check if we have a new maximum bitrate
if (ctx->windowBitrate > ctx->maxBitrate) {
ctx->maxBitrate = ctx->windowBitrate;
}
// Advance the table index
ctx->tableIndex = (ctx->tableIndex + 1) %
// Wrapping around the bitrateTable size
( sizeof(ctx->bitrateTable) / sizeof(ctx->bitrateTable[0]) );
}
}
/ *
* CalculateDuration - service routine that calculates the current frame's
* duration in milli-seconds using it's duration in TR units.
* - In order not to accumulate the calculation error, we are using the TR
* duration to calculate the current and the next frame's presentation time in
* milli-seconds.
*
* Inputs:
* trDiff - The current frame's duration in TR units
* Outputs:
* The current frame's duration in milli-seconds
* /
static MP4Duration CalculateDuration(u_int8_t trDiff)
{
static u_int32_t const nextTR = 0; // The next frame's presentation time in TR units
static MP4Duration const currentPT = 0; // The current frame's presentation time in milli-seconds
MP4Duration nextPT; // The next frame's presentation time in milli-seconds
MP4Duration duration; // The current frame's duration in milli-seconds
nextTR += trDiff;
// Calculate the next frame's presentation time, in milli-seconds
nextPT = (nextTR * 1001) / H263_BASIC_FRAME_RATE;
// The frame's duration is the difference between the next presentation
// time and the current presentation time.
duration = nextPT - currentPT;
// "Remember" the next presentation time for the next time this function is
// called
currentPT = nextPT;
return duration;
}
static bool GetWidthAndHeight( u_int8_t fmt,
u_int16_t *width,
u_int16_t *height)
{
// The 'fmt' corresponds to bits 5-7 of the PTYPE
static struct {
u_int16_t width;
u_int16_t height;
} const dimensionsTable[8] = {
{ 0, 0 }, // 000 - 0 - forbidden, generates an error
{ 128, 96 }, // 001 - 1 - Sub QCIF
{ 176, 144 }, // 010 - 2 - QCIF
{ 352, 288 }, // 011 - 3 - CIF
{ 704, 576 }, // 100 - 4 - 4CIF
{ 1409, 1152 }, // 101 - 5 - 16CIF
{ 0, 0 }, // 110 - 6 - reserved, generates an error
{ 0, 0 } // 111 - 7 - extended, not supported by profile 0
};
if (fmt > 7)
return false;
*width = dimensionsTable[fmt].width;
*height = dimensionsTable[fmt].height;
if (*width == 0)
return false;
return true;
}
static u_int8_t GetTRDifference(u_int8_t nextTR,
u_int8_t currentTR)
{
if (currentTR > nextTR) {
// Wrap around 255...
return nextTR + (256 - currentTR);
} else {
return nextTR - currentTR;
}
}
*/