/**********
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-2026 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 (%ld)\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;
  }
}

*/

