1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
15 | **********/ |
16 | // "liveMedia" |
17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
18 | // Author Bernhard Feiten |
19 | // A filter that breaks up an H.263plus video stream into frames. |
20 | // Based on MPEG4IP/mp4creator/h263.c |
21 | |
22 | #include "H263plusVideoStreamParser.hh" |
23 | #include "H263plusVideoStreamFramer.hh" |
24 | //#include <string.h> |
25 | //#include "GroupsockHelper.hh" |
26 | |
27 | |
28 | H263plusVideoStreamParser::H263plusVideoStreamParser( |
29 | H263plusVideoStreamFramer* usingSource, |
30 | FramedSource* inputSource) |
31 | : StreamParser(inputSource, |
32 | FramedSource::handleClosure, |
33 | usingSource, |
34 | &H263plusVideoStreamFramer::continueReadProcessing, |
35 | usingSource), |
36 | fUsingSource(usingSource), |
37 | fnextTR(0), |
38 | fcurrentPT(0) |
39 | { |
40 | memset(fStates, 0, sizeof(fStates)); |
41 | memset(&fNextInfo, 0, sizeof(fNextInfo)); |
42 | memset(&fCurrentInfo, 0, sizeof(fCurrentInfo)); |
43 | memset(&fMaxBitrateCtx, 0, sizeof(fMaxBitrateCtx)); |
44 | memset(fNextHeader,0, H263_REQUIRE_HEADER_SIZE_BYTES); |
45 | } |
46 | |
47 | /////////////////////////////////////////////////////////////////////////////// |
48 | H263plusVideoStreamParser::~H263plusVideoStreamParser() |
49 | { |
50 | } |
51 | |
52 | /////////////////////////////////////////////////////////////////////////////// |
53 | void H263plusVideoStreamParser::restoreSavedParserState() |
54 | { |
55 | StreamParser::restoreSavedParserState(); |
56 | fTo = fSavedTo; |
57 | fNumTruncatedBytes = fSavedNumTruncatedBytes; |
58 | } |
59 | |
60 | /////////////////////////////////////////////////////////////////////////////// |
61 | void H263plusVideoStreamParser::setParseState() |
62 | { |
63 | fSavedTo = fTo; |
64 | fSavedNumTruncatedBytes = fNumTruncatedBytes; |
65 | saveParserState(); // Needed for the parsing process in StreamParser |
66 | } |
67 | |
68 | |
69 | /////////////////////////////////////////////////////////////////////////////// |
70 | void H263plusVideoStreamParser::registerReadInterest( |
71 | unsigned char* to, |
72 | unsigned maxSize) |
73 | { |
74 | fStartOfFrame = fTo = fSavedTo = to; |
75 | fLimit = to + maxSize; |
76 | fMaxSize = maxSize; |
77 | fNumTruncatedBytes = fSavedNumTruncatedBytes = 0; |
78 | } |
79 | |
80 | /////////////////////////////////////////////////////////////////////////////// |
81 | // parse() , derived from H263Creator of MPEG4IP, h263.c |
82 | unsigned H263plusVideoStreamParser::parse(u_int64_t & currentDuration) |
83 | { |
84 | |
85 | // u_int8_t frameBuffer[H263_BUFFER_SIZE]; // The input buffer |
86 | // Pointer which tells LoadNextH263Object where to read data to |
87 | // u_int8_t* pFrameBuffer = fTo + H263_REQUIRE_HEADER_SIZE_BYTES; |
88 | u_int32_t frameSize; // The current frame size |
89 | // Pointer to receive address of the header data |
90 | // u_int8_t* pCurrentHeader;// = pFrameBuffer; |
91 | // u_int64_t currentDuration; // The current frame's duration |
92 | u_int8_t trDifference; // The current TR difference |
93 | // The previous TR difference |
94 | // u_int8_t prevTrDifference = H263_BASIC_FRAME_RATE; |
95 | // u_int64_t totalDuration = 0;// Duration accumulator |
96 | // u_int64_t avgBitrate; // Average bitrate |
97 | // u_int64_t totalBytes = 0; // Size accumulator |
98 | |
99 | |
100 | try // The get data routines of the class FramedFilter returns an error when |
101 | { // the buffer is empty. This occurs at the beginning and at the end of the file. |
102 | fCurrentInfo = fNextInfo; |
103 | |
104 | // Parse 1 frame |
105 | // For the first time, only the first frame's header is returned. |
106 | // The second time the full first frame is returned |
107 | frameSize = parseH263Frame(); |
108 | |
109 | currentDuration = 0; |
110 | if ((frameSize > 0)){ |
111 | // We were able to acquire a frame from the input. |
112 | |
113 | // Parse the returned frame header (if any) |
114 | if (!ParseShortHeader(fTo, &fNextInfo)) { |
115 | #ifdef DEBUG |
116 | fprintf(stderr,"H263plusVideoStreamParser: Fatal error\n" ); |
117 | #endif |
118 | } |
119 | |
120 | trDifference = GetTRDifference(fNextInfo.tr, fCurrentInfo.tr); |
121 | |
122 | // calculate the current frame duration |
123 | currentDuration = CalculateDuration(trDifference); |
124 | |
125 | // Accumulate the frame's size and duration for avgBitrate calculation |
126 | //totalDuration += currentDuration; |
127 | //totalBytes += frameSize; |
128 | // If needed, recalculate bitrate information |
129 | // if (h263Bitrates) |
130 | //GetMaxBitrate(&fMaxBitrateCtx, frameSize, prevTrDifference); |
131 | //prevTrDifference = trDifference; |
132 | |
133 | setParseState(); // Needed for the parsing process in StreamParser |
134 | } |
135 | } catch (int /*e*/) { |
136 | #ifdef DEBUG |
137 | fprintf(stderr, "H263plusVideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n" ); |
138 | #endif |
139 | frameSize=0; |
140 | } |
141 | |
142 | return frameSize; |
143 | } |
144 | |
145 | |
146 | /////////////////////////////////////////////////////////////////////////////// |
147 | // parseH263Frame derived from LoadNextH263Object of MPEG4IP |
148 | // - service routine that reads a single frame from the input file. |
149 | // It shall fill the input buffer with data up until - and including - the |
150 | // next start code and shall report back both the number of bytes read and a |
151 | // pointer to the next start code. The first call to this function shall only |
152 | // yield a pointer with 0 data bytes and the last call to this function shall |
153 | // only yield data bytes with a NULL pointer as the next header. |
154 | // |
155 | // TODO: This function only supports valid bit streams. Upon error, it fails |
156 | // without the possibility to recover. A Better idea would be to skip frames |
157 | // until a parsable frame is read from the file. |
158 | // |
159 | // Parameters: |
160 | // ppNextHeader - output parameter that upon return points to the location |
161 | // of the next frame's head in the buffer. |
162 | // This pointer shall be NULL for the last frame read. |
163 | // Returns the total number of bytes read. |
164 | // Uses FrameFileSource intantiated by constructor. |
165 | /////////////////////////////////////////////////////////////////////////////// |
166 | int H263plusVideoStreamParser::parseH263Frame( ) |
167 | { |
168 | char row = 0; |
169 | u_int8_t * bufferIndex = fTo; |
170 | // The buffer end which will allow the loop to leave place for |
171 | // the additionalBytesNeeded |
172 | u_int8_t * bufferEnd = fTo + fMaxSize - ADDITIONAL_BYTES_NEEDED - 1; |
173 | |
174 | memcpy(fTo, fNextHeader, H263_REQUIRE_HEADER_SIZE_BYTES); |
175 | bufferIndex += H263_REQUIRE_HEADER_SIZE_BYTES; |
176 | |
177 | |
178 | // The state table and the following loop implements a state machine enabling |
179 | // us to read bytes from the file until (and inclusing) the requested |
180 | // start code (00 00 8X) is found |
181 | |
182 | // Initialize the states array, if it hasn't been initialized yet... |
183 | if (!fStates[0][0]) { |
184 | // One 00 was read |
185 | fStates[0][0] = 1; |
186 | // Two sequential 0x00 ware read |
187 | fStates[1][0] = fStates[2][0] = 2; |
188 | // A full start code was read |
189 | fStates[2][128] = fStates[2][129] = fStates[2][130] = fStates[2][131] = -1; |
190 | } |
191 | |
192 | // Read data from file into the output buffer until either a start code |
193 | // is found, or the end of file has been reached. |
194 | do { |
195 | *bufferIndex = get1Byte(); |
196 | } while ((bufferIndex < bufferEnd) && // We have place in the buffer |
197 | ((row = fStates[(unsigned char)row][*(bufferIndex++)]) != -1)); // Start code was not found |
198 | |
199 | if (row != -1) { |
200 | fprintf(stderr, "%s: Buffer too small (%u)\n" , |
201 | "h263reader:" , bufferEnd - fTo + ADDITIONAL_BYTES_NEEDED); |
202 | return 0; |
203 | } |
204 | |
205 | // Cool ... now we have a start code |
206 | // Now we just have to read the additionalBytesNeeded |
207 | getBytes(bufferIndex, ADDITIONAL_BYTES_NEEDED); |
208 | memcpy(fNextHeader, bufferIndex - H263_STARTCODE_SIZE_BYTES, H263_REQUIRE_HEADER_SIZE_BYTES); |
209 | |
210 | int sz = bufferIndex - fTo - H263_STARTCODE_SIZE_BYTES; |
211 | |
212 | if (sz == 5) // first frame |
213 | memcpy(fTo, fTo+H263_REQUIRE_HEADER_SIZE_BYTES, H263_REQUIRE_HEADER_SIZE_BYTES); |
214 | |
215 | return sz; |
216 | } |
217 | |
218 | |
219 | //////////////////////////////////////////////////////////////////////////////// |
220 | // ParseShortHeader - service routine that accepts a buffer containing a frame |
221 | // header and extracts relevant codec information from it. |
222 | // |
223 | // NOTE: the first bit in the following commnets is 0 (zero). |
224 | // |
225 | // 0 1 2 3 |
226 | // 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 |
227 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
228 | // | PSC (Picture Start Code=22 bits) | (TR=8 bits) | > |
229 | // |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0| |1 0> |
230 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
231 | // < (PTYPE=13 bits) | |
232 | // <. . .|(FMT)|Z|. . . .| |
233 | // +-+-+-+-+-+-+-+-+-+-+-+ |
234 | // -> PTYPE.FMT contains a width/height identification |
235 | // -> PTYPE.Z is 1 for P-Frames, 0 for I-Frames |
236 | // Note: When FMT is 111, there is an extended PTYPE... |
237 | // |
238 | // Inputs: |
239 | // headerBuffer - pointer to the current header buffer |
240 | // outputInfoStruct - pointer to the structure receiving the data |
241 | // Outputs: |
242 | // This function returns a structure of important codec-specific |
243 | // information (The Temporal Reference bits, width & height of the current |
244 | // frame and the sync - or "frame type" - bit. It reports success or |
245 | // failure to the calling function. |
246 | //////////////////////////////////////////////////////////////////////////////// |
247 | bool H263plusVideoStreamParser::( |
248 | u_int8_t *, |
249 | H263INFO *outputInfoStruct) |
250 | { |
251 | u_int8_t fmt = 0; |
252 | // Extract temporal reference (TR) from the buffer (bits 22-29 inclusive) |
253 | outputInfoStruct->tr = (headerBuffer[2] << 6) & 0xC0; // 2 LS bits out of the 3rd byte |
254 | outputInfoStruct->tr |= (headerBuffer[3] >> 2) & 0x3F; // 6 MS bits out of the 4th byte |
255 | // Extract the FMT part of PTYPE from the buffer (bits 35-37 inclusive) |
256 | fmt = (headerBuffer[4] >> 2) & 0x07; // bits 3-5 ouf of the 5th byte |
257 | // If PTYPE is not supported, return a failure notice to the calling function |
258 | // FIXME: PLUSPTYPE is not supported |
259 | if (fmt == 0x07) { |
260 | return false; |
261 | } |
262 | // If PTYPE is supported, calculate the current width and height according to |
263 | // a predefined table |
264 | if (!GetWidthAndHeight(fmt, &(outputInfoStruct->width), |
265 | &(outputInfoStruct->height))) { |
266 | return false; |
267 | } |
268 | // Extract the frame-type bit, which is the 9th bit of PTYPE (bit 38) |
269 | outputInfoStruct->isSyncFrame = !(headerBuffer[4] & 0x02); |
270 | |
271 | return true; |
272 | } |
273 | |
274 | //////////////////////////////////////////////////////////////////////////////// |
275 | // GetMaxBitrate- service routine that accepts frame information and |
276 | // derives bitrate information from it. This function uses a sliding window |
277 | // technique to calculate the maximum bitrates in any window of 1 second |
278 | // inside the file. |
279 | // The sliding window is implemented with a table of bitrates for the last |
280 | // second (30 entries - one entry per TR unit). |
281 | // |
282 | // Inputs: |
283 | // ctx - context for this function |
284 | // frameSize - the size of the current frame in bytes |
285 | // frameTRDiff - the "duration" of the frame in TR units |
286 | // Outputs: |
287 | // This function returns the up-to-date maximum bitrate |
288 | //////////////////////////////////////////////////////////////////////////////// |
289 | void H263plusVideoStreamParser::GetMaxBitrate( MaxBitrate_CTX *ctx, |
290 | u_int32_t frameSize, |
291 | u_int8_t frameTRDiff) |
292 | { |
293 | if (frameTRDiff == 0) |
294 | return; |
295 | |
296 | // Calculate the current frame's bitrate as bits per TR unit (round the result |
297 | // upwards) |
298 | u_int32_t frameBitrate = frameSize * 8 / frameTRDiff + 1; |
299 | |
300 | // for each TRdiff received, |
301 | while (frameTRDiff--) { |
302 | // Subtract the oldest bitrate entry from the current bitrate |
303 | ctx->windowBitrate -= ctx->bitrateTable[ctx->tableIndex]; |
304 | // Update the oldest bitrate entry with the current frame's bitrate |
305 | ctx->bitrateTable[ctx->tableIndex] = frameBitrate; |
306 | // Add the current frame's bitrate to the current bitrate |
307 | ctx->windowBitrate += frameBitrate; |
308 | // Check if we have a new maximum bitrate |
309 | if (ctx->windowBitrate > ctx->maxBitrate) { |
310 | ctx->maxBitrate = ctx->windowBitrate; |
311 | } |
312 | // Advance the table index |
313 | // Wrapping around the bitrateTable size |
314 | ctx->tableIndex = (ctx->tableIndex + 1) % |
315 | ( sizeof(ctx->bitrateTable) / sizeof(ctx->bitrateTable[0]) ); |
316 | } |
317 | } |
318 | |
319 | //////////////////////////////////////////////////////////////////////////////// |
320 | // CalculateDuration - service routine that calculates the current frame's |
321 | // duration in milli-seconds using it's duration in TR units. |
322 | // - In order not to accumulate the calculation error, we are using the TR |
323 | // duration to calculate the current and the next frame's presentation time in |
324 | // milli-seconds. |
325 | // |
326 | // Inputs: trDiff - The current frame's duration in TR units |
327 | // Return: The current frame's duration in milli-seconds |
328 | //////////////////////////////////////////////////////////////////////////////// |
329 | u_int64_t H263plusVideoStreamParser::CalculateDuration(u_int8_t trDiff) |
330 | { |
331 | u_int64_t nextPT; // The next frame's presentation time in milli-seconds |
332 | u_int64_t duration; // The current frame's duration in milli-seconds |
333 | |
334 | fnextTR += trDiff; |
335 | // Calculate the next frame's presentation time, in milli-seconds |
336 | nextPT = (fnextTR * 1001) / H263_BASIC_FRAME_RATE; |
337 | // The frame's duration is the difference between the next presentation |
338 | // time and the current presentation time. |
339 | duration = nextPT - fcurrentPT; |
340 | // "Remember" the next presentation time for the next time this function is called |
341 | fcurrentPT = nextPT; |
342 | |
343 | return duration; |
344 | } |
345 | |
346 | //////////////////////////////////////////////////////////////////////////////// |
347 | bool H263plusVideoStreamParser::GetWidthAndHeight( u_int8_t fmt, |
348 | u_int16_t *width, |
349 | u_int16_t *height) |
350 | { |
351 | // The 'fmt' corresponds to bits 5-7 of the PTYPE |
352 | static struct { |
353 | u_int16_t width; |
354 | u_int16_t height; |
355 | } const dimensionsTable[8] = { |
356 | { 0, 0 }, // 000 - 0 - forbidden, generates an error |
357 | { 128, 96 }, // 001 - 1 - Sub QCIF |
358 | { 176, 144 }, // 010 - 2 - QCIF |
359 | { 352, 288 }, // 011 - 3 - CIF |
360 | { 704, 576 }, // 100 - 4 - 4CIF |
361 | { 1409, 1152 }, // 101 - 5 - 16CIF |
362 | { 0, 0 }, // 110 - 6 - reserved, generates an error |
363 | { 0, 0 } // 111 - 7 - extended, not supported by profile 0 |
364 | }; |
365 | |
366 | if (fmt > 7) |
367 | return false; |
368 | |
369 | *width = dimensionsTable[fmt].width; |
370 | *height = dimensionsTable[fmt].height; |
371 | |
372 | if (*width == 0) |
373 | return false; |
374 | |
375 | return true; |
376 | } |
377 | |
378 | //////////////////////////////////////////////////////////////////////////////// |
379 | u_int8_t H263plusVideoStreamParser::GetTRDifference( |
380 | u_int8_t nextTR, |
381 | u_int8_t currentTR) |
382 | { |
383 | if (currentTR > nextTR) { |
384 | // Wrap around 255... |
385 | return nextTR + (256 - currentTR); |
386 | } else { |
387 | return nextTR - currentTR; |
388 | } |
389 | } |
390 | |
391 | |
392 | |
393 | |
394 | |
395 | |
396 | |
397 | //////////////////////////////////////////////////////////////////////////////// |
398 | //////////////////////////////////////////////////////////////////////////////// |
399 | //////////////////////////////////////////////////////////////////////////////// |
400 | // this is the h263.c file of MPEG4IP mp4creator |
401 | /* |
402 | #include "mp4creator.h" |
403 | |
404 | // Default timescale for H.263 (1000ms) |
405 | #define H263_TIMESCALE 1000 |
406 | // Default H263 frame rate (30fps) |
407 | #define H263_BASIC_FRAME_RATE 30 |
408 | |
409 | // Minimum number of bytes needed to parse an H263 header |
410 | #define H263_REQUIRE_HEADER_SIZE_BYTES 5 |
411 | // Number of bytes the start code requries |
412 | #define H263_STARTCODE_SIZE_BYTES 3 |
413 | // This is the input buffer's size. It should contain |
414 | // 1 frame with the following start code |
415 | #define H263_BUFFER_SIZE 256 * 1024 |
416 | // The default max different (in %) betwqeen max and average bitrates |
417 | #define H263_DEFAULT_CBR_TOLERANCE 10 |
418 | |
419 | // The following structure holds information extracted from each frame's header: |
420 | typedef struct _H263INFO { |
421 | u_int8_t tr; // Temporal Reference, used in duration calculation |
422 | u_int16_t width; // Width of the picture |
423 | u_int16_t height; // Height of the picture |
424 | bool isSyncFrame; // Frame type (true = I frame = "sync" frame) |
425 | } H263INFO; |
426 | |
427 | // Context for the GetMaxBitrate function |
428 | typedef struct _MaxBitrate_CTX { |
429 | u_int32_t bitrateTable[H263_BASIC_FRAME_RATE];// Window of 1 second |
430 | u_int32_t windowBitrate; // The bitrate of the current window |
431 | u_int32_t maxBitrate; // The up-to-date maximum bitrate |
432 | u_int32_t tableIndex; // The next TR unit to update |
433 | } MaxBitrate_CTX; |
434 | |
435 | // Forward declarations: |
436 | static int LoadNextH263Object( FILE *inputFileHandle, |
437 | u_int8_t *frameBuffer, |
438 | u_int32_t *frameBufferSize, |
439 | u_int32_t additionalBytesNeeded, |
440 | u_int8_t **ppNextHeader); |
441 | |
442 | static bool ParseShortHeader( u_int8_t *headerBuffer, |
443 | H263INFO *outputInfoStruct); |
444 | |
445 | static u_int8_t GetTRDifference(u_int8_t nextTR, |
446 | u_int8_t currentTR); |
447 | |
448 | static void GetMaxBitrate( MaxBitrate_CTX *ctx, |
449 | u_int32_t frameSize, |
450 | u_int8_t frameTRDiff); |
451 | |
452 | static MP4Duration CalculateDuration(u_int8_t trDiff); |
453 | |
454 | static bool GetWidthAndHeight( u_int8_t fmt, |
455 | u_int16_t *width, |
456 | u_int16_t *height); |
457 | |
458 | static char states[3][256]; |
459 | / * |
460 | * H263Creator - Main function |
461 | * Inputs: |
462 | * outputFileHandle - The handle of the output file |
463 | * inputFileHandle - The handle of the input file |
464 | * Codec-specific parameters: |
465 | * H263Level - H.263 Level used for this track |
466 | * H263Profile - H.263 Profile used for this track |
467 | * H263Bitrates - A Parameter indicating whether the function |
468 | * should calculate H263 bitrates or not. |
469 | * cbrTolerance - CBR tolerance indicates when to set the |
470 | * average bitrate. |
471 | * Outputs: |
472 | * This function returns either the track ID of the newly added track upon |
473 | * success or a predefined value representing an erroneous state. |
474 | * / |
475 | MP4TrackId H263Creator(MP4FileHandle outputFileHandle, |
476 | FILE* inputFileHandle, |
477 | u_int8_t h263Profile, |
478 | u_int8_t h263Level, |
479 | bool h263Bitrates, |
480 | u_int8_t cbrTolerance) |
481 | { |
482 | H263INFO nextInfo; // Holds information about the next frame |
483 | H263INFO currentInfo;// Holds information about the current frame |
484 | MaxBitrate_CTX maxBitrateCtx;// Context for the GetMaxBitrate function |
485 | memset(&nextInfo, 0, sizeof(nextInfo)); |
486 | memset(¤tInfo, 0, sizeof(currentInfo)); |
487 | memset(&maxBitrateCtx, 0, sizeof(maxBitrateCtx)); |
488 | memset(states, 0, sizeof(states)); |
489 | u_int8_t frameBuffer[H263_BUFFER_SIZE]; // The input buffer |
490 | // Pointer which tells LoadNextH263Object where to read data to |
491 | u_int8_t* pFrameBuffer = frameBuffer + H263_REQUIRE_HEADER_SIZE_BYTES; |
492 | u_int32_t frameSize; // The current frame size |
493 | // Pointer to receive address of the header data |
494 | u_int8_t* pCurrentHeader = pFrameBuffer; |
495 | MP4Duration currentDuration; // The current frame's duration |
496 | u_int8_t trDifference; // The current TR difference |
497 | // The previous TR difference |
498 | u_int8_t prevTrDifference = H263_BASIC_FRAME_RATE; |
499 | MP4Duration totalDuration = 0;// Duration accumulator |
500 | MP4Duration avgBitrate; // Average bitrate |
501 | u_int64_t totalBytes = 0; // Size accumulator |
502 | MP4TrackId trackId = MP4_INVALID_TRACK_ID; // Our MP4 track |
503 | bool stay = true; // loop flag |
504 | |
505 | while (stay) { |
506 | currentInfo = nextInfo; |
507 | memmove(frameBuffer, pCurrentHeader, H263_REQUIRE_HEADER_SIZE_BYTES); |
508 | frameSize = H263_BUFFER_SIZE - H263_REQUIRE_HEADER_SIZE_BYTES; |
509 | // Read 1 frame and the next frame's header from the file. |
510 | // For the first frame, only the first frame's header is returned. |
511 | // For the last frame, only the last frame's data is returned. |
512 | if (! LoadNextH263Object(inputFileHandle, pFrameBuffer, &frameSize, |
513 | H263_REQUIRE_HEADER_SIZE_BYTES - H263_STARTCODE_SIZE_BYTES, |
514 | &pCurrentHeader)) |
515 | break; // Fatal error ... |
516 | |
517 | if (pCurrentHeader) { |
518 | // Parse the returned frame header (if any) |
519 | if (!ParseShortHeader(pCurrentHeader, &nextInfo)) |
520 | break; // Fatal error |
521 | trDifference = GetTRDifference(nextInfo.tr, currentInfo.tr); |
522 | } else { |
523 | // This is the last frame ... we have to fake the trDifference ... |
524 | trDifference = 1; |
525 | // No header data has been read at this iteration, so we have to manually |
526 | // add the frame's header we read at the previous iteration. |
527 | // Note that LoadNextH263Object returns the number of bytes read, which |
528 | // are the current frame's data and the next frame's header |
529 | frameSize += H263_REQUIRE_HEADER_SIZE_BYTES; |
530 | // There is no need for the next iteration ... |
531 | stay = false; |
532 | } |
533 | |
534 | // If this is the first iteration ... |
535 | if (currentInfo.width == 0) { |
536 | // If we have more data than just the header |
537 | if ((frameSize > H263_REQUIRE_HEADER_SIZE_BYTES) || |
538 | !pCurrentHeader) // Or no header at all |
539 | break; // Fatal error |
540 | else |
541 | continue; // We have only the first frame's header ... |
542 | } |
543 | |
544 | if (trackId == MP4_INVALID_TRACK_ID) { |
545 | // If a track has not been added yet, add the track to the file. |
546 | trackId = MP4AddH263VideoTrack(outputFileHandle, H263_TIMESCALE, |
547 | 0, currentInfo.width, currentInfo.height, |
548 | h263Level, h263Profile, 0, 0); |
549 | if (trackId == MP4_INVALID_TRACK_ID) |
550 | break; // Fatal error |
551 | } |
552 | |
553 | // calculate the current frame duration |
554 | currentDuration = CalculateDuration(trDifference); |
555 | // Write the current frame to the file. |
556 | if (!MP4WriteSample(outputFileHandle, trackId, frameBuffer, frameSize, |
557 | currentDuration, 0, currentInfo.isSyncFrame)) |
558 | break; // Fatal error |
559 | |
560 | // Accumulate the frame's size and duration for avgBitrate calculation |
561 | totalDuration += currentDuration; |
562 | totalBytes += frameSize; |
563 | // If needed, recalculate bitrate information |
564 | if (h263Bitrates) |
565 | GetMaxBitrate(&maxBitrateCtx, frameSize, prevTrDifference); |
566 | prevTrDifference = trDifference; |
567 | } // while (stay) |
568 | |
569 | // If this is the last frame, |
570 | if (!stay) { |
571 | // If needed and possible, update bitrate information in the file |
572 | if (h263Bitrates && totalDuration) { |
573 | avgBitrate = (totalBytes * 8 * H263_TIMESCALE) / totalDuration; |
574 | if (cbrTolerance == 0) |
575 | cbrTolerance = H263_DEFAULT_CBR_TOLERANCE; |
576 | // Same as: if (maxBitrate / avgBitrate > (cbrTolerance + 100) / 100.0) |
577 | if (maxBitrateCtx.maxBitrate * 100 > (cbrTolerance + 100) * avgBitrate) |
578 | avgBitrate = 0; |
579 | MP4SetH263Bitrates(outputFileHandle, trackId, |
580 | avgBitrate, maxBitrateCtx.maxBitrate); |
581 | } |
582 | // Return the newly added track ID |
583 | return trackId; |
584 | } |
585 | |
586 | // If we got to here... something went wrong ... |
587 | fprintf(stderr, |
588 | "%s: Could not parse input file, invalid video stream?\n", ProgName); |
589 | // Upon failure, delete the newly added track if it has been added |
590 | if (trackId != MP4_INVALID_TRACK_ID) { |
591 | MP4DeleteTrack(outputFileHandle, trackId); |
592 | } |
593 | return MP4_INVALID_TRACK_ID; |
594 | } |
595 | |
596 | / * |
597 | * LoadNextH263Object - service routine that reads a single frame from the input |
598 | * file. It shall fill the input buffer with data up until - and including - the |
599 | * next start code and shall report back both the number of bytes read and a |
600 | * pointer to the next start code. The first call to this function shall only |
601 | * yield a pointer with 0 data bytes and the last call to this function shall |
602 | * only yield data bytes with a NULL pointer as the next header. |
603 | * |
604 | * TODO: This function only supports valid bit streams. Upon error, it fails |
605 | * without the possibility to recover. A Better idea would be to skip frames |
606 | * until a parsable frame is read from the file. |
607 | * |
608 | * Parameters: |
609 | * inputFileHandle - The handle of the input file |
610 | * frameBuffer - buffer where to place read data |
611 | * frameBufferSize - in/out parameter indicating the size of the buffer on |
612 | * entry and the number of bytes copied to the buffer upon |
613 | * return |
614 | * additionalBytesNeeded - indicates how many additional bytes are to be read |
615 | * from the next frame's header (over the 3 bytes that |
616 | * are already read). |
617 | * NOTE: This number MUST be > 0 |
618 | * ppNextHeader - output parameter that upon return points to the location |
619 | * of the next frame's head in the buffer |
620 | * Outputs: |
621 | * This function returns two pieces of information: |
622 | * 1. The total number of bytes read. |
623 | * 2. A Pointer to the header of the next frame. This pointer shall be NULL |
624 | * for the last frame read. |
625 | * / |
626 | static int LoadNextH263Object( FILE *inputFileHandle, |
627 | u_int8_t *frameBuffer, |
628 | u_int32_t *frameBufferSize, |
629 | u_int32_t additionalBytesNeeded, |
630 | u_int8_t **ppNextHeader) |
631 | { |
632 | // This table and the following loop implements a state machine enabling |
633 | // us to read bytes from the file untill (and inclusing) the requested |
634 | // start code (00 00 8X) is found |
635 | char row = 0; |
636 | u_int8_t *bufferStart = frameBuffer; |
637 | // The buffer end which will allow the loop to leave place for |
638 | // the additionalBytesNeeded |
639 | u_int8_t *bufferEnd = frameBuffer + *frameBufferSize - |
640 | additionalBytesNeeded - 1; |
641 | |
642 | // Initialize the states array, if it hasn't been initialized yet... |
643 | if (!states[0][0]) { |
644 | // One 00 was read |
645 | states[0][0] = 1; |
646 | // Two sequential 0x00 ware read |
647 | states[1][0] = states[2][0] = 2; |
648 | // A full start code was read |
649 | states[2][128] = states[2][129] = states[2][130] = states[2][131] = -1; |
650 | } |
651 | |
652 | // Read data from file into the output buffer until either a start code |
653 | // is found, or the end of file has been reached. |
654 | do { |
655 | if (fread(frameBuffer, 1, 1, inputFileHandle) != 1){ |
656 | // EOF or other error before we got a start code |
657 | *ppNextHeader = NULL; |
658 | *frameBufferSize = frameBuffer - bufferStart; |
659 | return 1; |
660 | } |
661 | } while ((frameBuffer < bufferEnd) && // We have place in the buffer |
662 | ((row = states[row][*(frameBuffer++)]) != -1)); // Start code was not found |
663 | if (row != -1) { |
664 | fprintf(stderr, "%s: Buffer too small (%u)\n", |
665 | ProgName, bufferEnd - bufferStart + additionalBytesNeeded); |
666 | return 0; |
667 | } |
668 | |
669 | // Cool ... now we have a start code |
670 | *ppNextHeader = frameBuffer - H263_STARTCODE_SIZE_BYTES; |
671 | *frameBufferSize = frameBuffer - bufferStart + additionalBytesNeeded; |
672 | |
673 | // Now we just have to read the additionalBytesNeeded |
674 | if(fread(frameBuffer, additionalBytesNeeded, 1, inputFileHandle) != 1) { |
675 | /// We got a start code but can't read additionalBytesNeeded ... that's a fatal error |
676 | fprintf(stderr, "%s: Invalid H263 bitstream\n", ProgName); |
677 | return 0; |
678 | } |
679 | |
680 | return 1; |
681 | } |
682 | |
683 | |
684 | / * |
685 | * ParseShortHeader - service routine that accepts a buffer containing a frame |
686 | * header and extracts relevant codec information from it. |
687 | * |
688 | * NOTE: the first bit in the following commnets is 0 (zero). |
689 | * |
690 | * |
691 | * 0 1 2 3 |
692 | * 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 |
693 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
694 | * | PSC (Picture Start Code=22 bits) | (TR=8 bits) | > |
695 | * |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0| |1 0> |
696 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
697 | * < (PTYPE=13 bits) | |
698 | * <. . .|(FMT)|Z|. . . .| |
699 | * +-+-+-+-+-+-+-+-+-+-+-+ |
700 | * -> PTYPE.FMT contains a width/height identification |
701 | * -> PTYPE.Z is 1 for P-Frames, 0 for I-Frames |
702 | * Note: When FMT is 111, there is an extended PTYPE... |
703 | * |
704 | * Inputs: |
705 | * headerBuffer - pointer to the current header buffer |
706 | * outputInfoStruct - pointer to the structure receiving the data |
707 | * Outputs: |
708 | * This function returns a structure of important codec-specific |
709 | * information (The Temporal Reference bits, width & height of the current |
710 | * frame and the sync - or "frame type" - bit. It reports success or |
711 | * failure to the calling function. |
712 | * / |
713 | static bool ParseShortHeader( u_int8_t *headerBuffer, |
714 | H263INFO *outputInfoStruct) |
715 | { |
716 | u_int8_t fmt = 0; |
717 | // Extract temporal reference (TR) from the buffer (bits 22-29 inclusive) |
718 | outputInfoStruct->tr = (headerBuffer[2] << 6) & 0xC0; // 2 LS bits out of the 3rd byte |
719 | outputInfoStruct->tr |= (headerBuffer[3] >> 2) & 0x3F; // 6 MS bits out of the 4th byte |
720 | // Extract the FMT part of PTYPE from the buffer (bits 35-37 inclusive) |
721 | fmt = (headerBuffer[4] >> 2) & 0x07; // bits 3-5 ouf of the 5th byte |
722 | // If PTYPE is not supported, return a failure notice to the calling function |
723 | // FIXME: PLUSPTYPE is not supported |
724 | if (fmt == 0x07) { |
725 | return false; |
726 | } |
727 | // If PTYPE is supported, calculate the current width and height according to |
728 | // a predefined table |
729 | if (!GetWidthAndHeight(fmt, &(outputInfoStruct->width), |
730 | &(outputInfoStruct->height))) { |
731 | return false; |
732 | } |
733 | // Extract the frame-type bit, which is the 9th bit of PTYPE (bit 38) |
734 | outputInfoStruct->isSyncFrame = !(headerBuffer[4] & 0x02); |
735 | |
736 | return true; |
737 | } |
738 | |
739 | / * |
740 | * GetMaxBitrate- service routine that accepts frame information and |
741 | * derives bitrate information from it. This function uses a sliding window |
742 | * technique to calculate the maximum bitrates in any window of 1 second |
743 | * inside the file. |
744 | * The sliding window is implemented with a table of bitrates for the last |
745 | * second (30 entries - one entry per TR unit). |
746 | * |
747 | * Inputs: |
748 | * ctx - context for this function |
749 | * frameSize - the size of the current frame in bytes |
750 | * frameTRDiff - the "duration" of the frame in TR units |
751 | * Outputs: |
752 | * This function returns the up-to-date maximum bitrate |
753 | * / |
754 | static void GetMaxBitrate( MaxBitrate_CTX *ctx, |
755 | u_int32_t frameSize, |
756 | u_int8_t frameTRDiff) |
757 | { |
758 | if (frameTRDiff == 0) |
759 | return; |
760 | |
761 | // Calculate the current frame's bitrate as bits per TR unit (round the result |
762 | // upwards) |
763 | u_int32_t frameBitrate = frameSize * 8 / frameTRDiff + 1; |
764 | |
765 | // for each TRdiff received, |
766 | while (frameTRDiff--) { |
767 | // Subtract the oldest bitrate entry from the current bitrate |
768 | ctx->windowBitrate -= ctx->bitrateTable[ctx->tableIndex]; |
769 | // Update the oldest bitrate entry with the current frame's bitrate |
770 | ctx->bitrateTable[ctx->tableIndex] = frameBitrate; |
771 | // Add the current frame's bitrate to the current bitrate |
772 | ctx->windowBitrate += frameBitrate; |
773 | // Check if we have a new maximum bitrate |
774 | if (ctx->windowBitrate > ctx->maxBitrate) { |
775 | ctx->maxBitrate = ctx->windowBitrate; |
776 | } |
777 | // Advance the table index |
778 | ctx->tableIndex = (ctx->tableIndex + 1) % |
779 | // Wrapping around the bitrateTable size |
780 | ( sizeof(ctx->bitrateTable) / sizeof(ctx->bitrateTable[0]) ); |
781 | } |
782 | } |
783 | |
784 | / * |
785 | * CalculateDuration - service routine that calculates the current frame's |
786 | * duration in milli-seconds using it's duration in TR units. |
787 | * - In order not to accumulate the calculation error, we are using the TR |
788 | * duration to calculate the current and the next frame's presentation time in |
789 | * milli-seconds. |
790 | * |
791 | * Inputs: |
792 | * trDiff - The current frame's duration in TR units |
793 | * Outputs: |
794 | * The current frame's duration in milli-seconds |
795 | * / |
796 | static MP4Duration CalculateDuration(u_int8_t trDiff) |
797 | { |
798 | static u_int32_t const nextTR = 0; // The next frame's presentation time in TR units |
799 | static MP4Duration const currentPT = 0; // The current frame's presentation time in milli-seconds |
800 | MP4Duration nextPT; // The next frame's presentation time in milli-seconds |
801 | MP4Duration duration; // The current frame's duration in milli-seconds |
802 | |
803 | nextTR += trDiff; |
804 | // Calculate the next frame's presentation time, in milli-seconds |
805 | nextPT = (nextTR * 1001) / H263_BASIC_FRAME_RATE; |
806 | // The frame's duration is the difference between the next presentation |
807 | // time and the current presentation time. |
808 | duration = nextPT - currentPT; |
809 | // "Remember" the next presentation time for the next time this function is |
810 | // called |
811 | currentPT = nextPT; |
812 | |
813 | return duration; |
814 | } |
815 | |
816 | static bool GetWidthAndHeight( u_int8_t fmt, |
817 | u_int16_t *width, |
818 | u_int16_t *height) |
819 | { |
820 | // The 'fmt' corresponds to bits 5-7 of the PTYPE |
821 | static struct { |
822 | u_int16_t width; |
823 | u_int16_t height; |
824 | } const dimensionsTable[8] = { |
825 | { 0, 0 }, // 000 - 0 - forbidden, generates an error |
826 | { 128, 96 }, // 001 - 1 - Sub QCIF |
827 | { 176, 144 }, // 010 - 2 - QCIF |
828 | { 352, 288 }, // 011 - 3 - CIF |
829 | { 704, 576 }, // 100 - 4 - 4CIF |
830 | { 1409, 1152 }, // 101 - 5 - 16CIF |
831 | { 0, 0 }, // 110 - 6 - reserved, generates an error |
832 | { 0, 0 } // 111 - 7 - extended, not supported by profile 0 |
833 | }; |
834 | |
835 | if (fmt > 7) |
836 | return false; |
837 | |
838 | *width = dimensionsTable[fmt].width; |
839 | *height = dimensionsTable[fmt].height; |
840 | |
841 | if (*width == 0) |
842 | return false; |
843 | |
844 | return true; |
845 | } |
846 | |
847 | static u_int8_t GetTRDifference(u_int8_t nextTR, |
848 | u_int8_t currentTR) |
849 | { |
850 | if (currentTR > nextTR) { |
851 | // Wrap around 255... |
852 | return nextTR + (256 - currentTR); |
853 | } else { |
854 | return nextTR - currentTR; |
855 | } |
856 | } |
857 | |
858 | */ |
859 | |
860 | |