| /********** |
| 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(¤tInfo, 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; |
| } |
| } |
| |
| */ |
| |