1 /*
2 * yamdi.c
3 *
4 * Copyright (c) 2007+, Ingo Oppermann
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * -----------------------------------------------------------------------------
32 *
33 * Compile with:
34 * gcc yamdi.c -o yamdi -Wall -O2
35 *
36 * -----------------------------------------------------------------------------
37 */
38
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <inttypes.h>
45 #include <errno.h>
46
47 #ifdef __MINGW32__
48 #define off_t _off64_t
49 #define fseeko(stream, offset, origin) fseeko64(stream, offset, origin)
50 #define ftello(stream) ftello64(stream)
51 #endif
52
53 #define YAMDI_VERSION "1.9"
54
55 #define YAMDI_OK 0
56 #define YAMDI_ERROR 1
57 #define YAMDI_FILE_TOO_SMALL 2
58 #define YAMDI_INVALID_SIGNATURE 3
59 #define YAMDI_INVALID_FLVVERSION 4
60 #define YAMDI_INVALID_DATASIZE 5
61 #define YAMDI_READ_ERROR 6
62 #define YAMDI_INVALID_PREVIOUSTAGSIZE 7
63 #define YAMDI_OUT_OF_MEMORY 8
64 #define YAMDI_H264_USELESS_NALU 9
65 #define YAMDI_RENAME_OUTPUT 10
66 #define YAMDI_INVALID_TAGTYPE 11
67
68 #define FLV_SIZE_HEADER 9
69 #define FLV_SIZE_PREVIOUSTAGSIZE 4
70 #define FLV_SIZE_TAGHEADER 11
71
72 #define FLV_TAG_AUDIO 8
73 #define FLV_TAG_VIDEO 9
74 #define FLV_TAG_SCRIPTDATA 18
75
76 #define FLV_PACKET_H263VIDEO 2
77 #define FLV_PACKET_SCREENVIDEO 3
78 #define FLV_PACKET_VP6VIDEO 4
79 #define FLV_PACKET_VP6ALPHAVIDEO 5
80 #define FLV_PACKET_SCREENV2VIDEO 6
81 #define FLV_PACKET_H264VIDEO 7
82
83 #define FLV_UI32(x) (unsigned int)(((*(x)) << 24) + ((*(x + 1)) << 16) + ((*(x + 2)) << 8) + (*(x + 3)))
84 #define FLV_UI24(x) (unsigned int)(((*(x)) << 16) + ((*(x + 1)) << 8) + (*(x + 2)))
85 #define FLV_UI16(x) (unsigned int)(((*(x)) << 8) + (*(x + 1)))
86 #define FLV_UI8(x) (unsigned int)(*(x))
87 #define FLV_TIMESTAMP(x) (int)(((*(x + 3)) << 24) + ((*(x)) << 16) + ((*(x + 1)) << 8) + (*(x + 2)))
88
89 typedef struct {
90 unsigned char *data;
91 size_t size;
92 size_t used;
93 } buffer_t;
94
95 typedef struct {
96 off_t offset; // Offset from the beginning of the file
97
98 // FLV spec v10
99 unsigned int tagtype;
100 size_t datasize; // Size of the data contained in this tag
101 int timestamp;
102 short keyframe; // Is this tag a keyframe?
103
104 size_t tagsize; // Size of the whole tag including header and data
105 } FLVTag_t;
106
107 typedef struct {
108 size_t nflvtags;
109 FLVTag_t *flvtag;
110 } FLVIndex_t;
111
112 typedef struct {
113 FLVIndex_t index;
114
115 int hascuepoints;
116 int canseektoend; // Set to 1 if the last video frame is a keyframe
117
118 short hasaudio;
119 struct {
120 short analyzed; // Are the audio specs complete and valid?
121
122 // Audio specs
123 short codecid;
124 short samplerate;
125 short samplesize;
126 short delay;
127 short stereo;
128
129 // Calculated values
130 size_t ntags; // # of audio tags
131 double datarate; // datasize / duration
132 uint64_t datasize; // Size of the audio data
133 uint64_t size; // Size of the audio tags (header + data)
134 int keyframerate; // Store every x tags a keyframe. Only used for -a
135 int keyframedistance; // The time between two keyframes. Only used for -a
136
137 int lasttimestamp;
138 size_t lastframeindex;
139 } audio;
140
141 short hasvideo;
142 struct {
143 short analyzed; // Are the video specs complete and valid?
144
145 // Video specs
146 short codecid;
147 int height;
148 int width;
149
150 // Calculated values
151 size_t ntags; // # of video tags
152 double framerate; // ntags / duration
153 double datarate; // datasize / duration
154 uint64_t datasize; // Size of the video data
155 uint64_t size; // Size of the video tags (header + data)
156
157 int lasttimestamp;
158 size_t lastframeindex;
159 } video;
160
161 short haskeyframes;
162 struct {
163 size_t lastkeyframeindex;
164 int lastkeyframetimestamp;
165 off_t lastkeyframelocation;
166
167 size_t nkeyframes; // # of key frames
168 off_t *keyframelocations; // Array of the filepositions of the keyframes (in the target file!)
169 int *keyframetimestamps; // Array of the timestamps of the keyframes
170 } keyframes;
171
172 uint64_t datasize; // Size of all audio and video tags (header + data + FLV_SIZE_PREVIOUSTAGSIZE)
173 uint64_t filesize; // [sic!]
174
175 int lasttimestamp;
176
177 int lastsecond;
178 size_t lastsecondindex;
179
180 struct {
181 char creator[256]; // -c
182
183 short addonlastkeyframe; // -k
184 short addonlastsecond; // -s, -l (deprecated)
185 short addonmetadata; // defaults to 1, -M does change it
186 short addaudiokeyframes; // -a
187
188 short keepmetadata; // -m (not implemented)
189 short stripmetadata; // -M
190
191 short xmlomitkeyframes; // -X
192
193 short overwriteinput; // -w
194 } options;
195
196 buffer_t onmetadata;
197 buffer_t onlastkeyframe;
198 buffer_t onlastsecond;
199 } FLV_t;
200
201 typedef struct {
202 unsigned char *bytes;
203 size_t length;
204 size_t byte;
205 short bit;
206 } bitstream_t;
207
208 typedef struct {
209 short valid;
210 int width;
211 int height;
212 } h264data_t;
213
214 int validateFLV(FILE *fp);
215 int initFLV(FLV_t *flv);
216 int indexFLV(FLV_t *flv, FILE *fp);
217 int finalizeFLV(FLV_t *flv, FILE *fp);
218 int writeFLV(FILE *out, FLV_t *flv, FILE *fp);
219 int freeFLV(FLV_t *flv);
220
221 void storeFLVFromStdin(FILE *fp);
222 int readFLVTag(FLVTag_t *flvtag, off_t offset, FILE *fp);
223 int readFLVTagData(unsigned char *ptr, size_t size, FLVTag_t *flvtag, FILE *stream);
224
225 int analyzeFLV(FLV_t *flv, FILE *fp);
226 int analyzeFLVH263VideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp);
227 int analyzeFLVH264VideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp);
228 int analyzeFLVScreenVideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp);
229 int analyzeFLVVP6VideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp);
230 int analyzeFLVVP6AlphaVideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp);
231
232 int createFLVEvents(FLV_t *flv);
233 int createFLVEventOnMetaData(FLV_t *flv);
234 int createFLVEventOnLastKeyframe(FLV_t *flv);
235 int createFLVEventOnLastSecond(FLV_t *flv);
236
237 int writeBufferFLVScriptDataTag(buffer_t *buffer, int timestamp, size_t datasize);
238 int writeBufferFLVPreviousTagSize(buffer_t *buffer, size_t tagsize);
239 int writeBufferFLVScriptDataValueArray(buffer_t *buffer, const char *name, size_t len);
240 int writeBufferFLVScriptDataECMAArray(buffer_t *buffer, const char *name, size_t len);
241 int writeBufferFLVScriptDataVariableArray(buffer_t *buffer, const char *name);
242 int writeBufferFLVScriptDataVariableArrayEnd(buffer_t *buffer);
243 int writeBufferFLVScriptDataValueString(buffer_t *buffer, const char *name, const char *value);
244 int writeBufferFLVScriptDataValueBool(buffer_t *buffer, const char *name, int value);
245 int writeBufferFLVScriptDataValueDouble(buffer_t *buffer, const char *name, double value);
246 int writeBufferFLVScriptDataObject(buffer_t *buffer);
247 int writeBufferFLVScriptDataString(buffer_t *buffer, const char *s);
248 int writeBufferFLVScriptDataLongString(buffer_t *buffer, const char *s);
249 int writeBufferFLVBool(buffer_t *buffer, int value);
250 int writeBufferFLVDouble(buffer_t *buffer, double v);
251
252 int writeFLVHeader(FILE *fp, int hasaudio, int hasvideo);
253 int writeFLVDataTag(FILE *fp, int type, int timestamp, size_t datasize);
254 int writeFLVPreviousTagSize(FILE *fp, size_t tagsize);
255
256 void writeXMLMetadata(FILE *fp, const char *infile, const char *outfile, FLV_t *flv);
257
258 int readBytes(unsigned char *ptr, size_t size, FILE *stream);
259
260 int readH264NALUnit(h264data_t *h264data, unsigned char *nalu, int length);
261 void readH264SPS(h264data_t *h264data, bitstream_t *bitstream);
262
263 unsigned int readCodedU(bitstream_t *bitstream, int nbits, const char *name);
264 unsigned int readCodedUE(bitstream_t *bitstream, const char *name);
265 int readCodedSE(bitstream_t *bitstream, const char *name);
266
267 int readBits(bitstream_t *bitstream, int nbits);
268 int readBit(bitstream_t *bitstream);
269
270 int bufferInit(buffer_t *buffer);
271 int bufferFree(buffer_t *buffer);
272 int bufferReset(buffer_t *buffer);
273 int bufferAppendBuffer(buffer_t *dst, buffer_t *src);
274 int bufferAppendString(buffer_t *dst, const unsigned char *string);
275 int bufferAppendBytes(buffer_t *dst, const unsigned char *bytes, size_t nbytes);
276
277 int isBigEndian(void);
278
279 void printUsage(void);
280
main(int argc,char ** argv)281 int main(int argc, char **argv) {
282 FILE *fp_infile = NULL, *fp_outfile = NULL, *fp_xmloutfile = NULL;
283 int c, unlink_infile = 0;
284 char *infile, *outfile, *xmloutfile, *tempfile, *creator;
285 FLV_t flv;
286
287 #ifdef DEBUG
288 fprintf(stderr, "[core] sizeof size_t = %d\n", (int)sizeof(size_t));
289 fprintf(stderr, "[core] sizeof off_t = %d\n", (int)sizeof(off_t));
290 fprintf(stderr, "[core] sizeof uint64_t = %d\n", (int)sizeof(uint64_t));
291 fprintf(stderr, "[core] sizeof long long = %d\n", (int)sizeof(long long));
292 fprintf(stderr, "[core] sizeof double = %d\n", (int)sizeof(double));
293 #endif
294
295 opterr = 0;
296
297 infile = NULL;
298 outfile = NULL;
299 xmloutfile = NULL;
300 tempfile = NULL;
301 creator = NULL;
302
303 initFLV(&flv);
304
305 while((c = getopt(argc, argv, ":i:o:x:t:c:a:lskMXwh")) != -1) {
306 switch(c) {
307 case 'i':
308 infile = optarg;
309 break;
310 case 'o':
311 outfile = optarg;
312 break;
313 case 'x':
314 xmloutfile = optarg;
315 break;
316 case 't':
317 tempfile = optarg;
318 break;
319 case 'c':
320 strncpy(flv.options.creator, optarg, sizeof(flv.options.creator));
321 break;
322 case 'l':
323 case 's':
324 flv.options.addonlastsecond = 1;
325 break;
326 case 'k':
327 flv.options.addonlastkeyframe = 1;
328 break;
329 case 'a':
330 flv.options.addaudiokeyframes = 1;
331 flv.audio.keyframedistance = (int)strtol(optarg, (char **)NULL, 10);
332 if(flv.audio.keyframedistance <= 0) {
333 flv.audio.keyframedistance = 0;
334 flv.options.addaudiokeyframes = 0;
335 }
336 break;
337 /*
338 case 'm':
339 flv.options.keepmetadata = 1;
340 break;
341 */
342 case 'M':
343 flv.options.stripmetadata = 1;
344 break;
345 case 'X':
346 flv.options.xmlomitkeyframes = 1;
347 break;
348 case 'w':
349 flv.options.overwriteinput = 1;
350 break;
351 case 'h':
352 printUsage();
353 exit(YAMDI_ERROR);
354 break;
355 case ':':
356 fprintf(stderr, "The option -%c expects a parameter. -h for help.\n", optopt);
357 exit(YAMDI_ERROR);
358 break;
359 case '?':
360 fprintf(stderr, "Unknown option: -%c. -h for help.\n", optopt);
361 exit(YAMDI_ERROR);
362 break;
363 default:
364 printUsage();
365 exit(YAMDI_ERROR);
366 break;
367 }
368 }
369
370 if(infile == NULL) {
371 fprintf(stderr, "Please use -i to provide an input file. -h for help.\n");
372 exit(YAMDI_ERROR);
373 }
374
375 if(outfile == NULL && xmloutfile == NULL) {
376 fprintf(stderr, "Please use -o or -x to provide at least one output file. -h for help.\n");
377 exit(YAMDI_ERROR);
378 }
379
380 if(tempfile == NULL && !strcmp(infile, "-")) {
381 fprintf(stderr, "Please use -t to specify a temporary file. -h for help.\n");
382 exit(YAMDI_ERROR);
383 }
384
385 // Check input file
386 if(!strcmp(infile, "-")) { // Read from stdin
387 // tempfile is available
388 // Check if the possible outfiles collide with the tempfile
389 if(outfile != NULL) {
390 if(!strcmp(tempfile, outfile)) {
391 fprintf(stderr, "The temporary file and the output file must not be the same.\n");
392 exit(YAMDI_ERROR);
393 }
394 }
395
396 if(xmloutfile != NULL) {
397 if(!strcmp(tempfile, xmloutfile)) {
398 fprintf(stderr, "The temporary file and the XML output file must not be the same.\n");
399 exit(YAMDI_ERROR);
400 }
401 }
402 }
403 else { // Read from file
404 // infile is available
405 // Check if the possible outfile collide with the infile
406 if(outfile != NULL) {
407 if(!strcmp(infile, outfile)) {
408 fprintf(stderr, "The input file and the output file must not be the same.\n");
409 exit(YAMDI_ERROR);
410 }
411 }
412
413 if(xmloutfile != NULL) {
414 if(!strcmp(infile, xmloutfile)) {
415 fprintf(stderr, "The input file and the XML output file must not be the same.\n");
416 exit(YAMDI_ERROR);
417 }
418 }
419 }
420
421 // Check output file
422 if(outfile != NULL) {
423 if(xmloutfile != NULL) {
424 if(!strcmp(outfile, xmloutfile)) {
425 fprintf(stderr, "The output file and the XML output file must not be the same.\n");
426 exit(YAMDI_ERROR);
427 }
428 }
429 }
430
431 // All checks are done. Open the files.
432
433 // Open the inputfile
434 // Store data to tempfile if inputfile is stdin
435 if(!strcmp(infile, "-")) {
436 fp_infile = fopen(tempfile, "wb");
437 if(fp_infile == NULL) {
438 fprintf(stderr, "Couldn't open the tempfile %s.\n", tempfile);
439 exit(YAMDI_ERROR);
440 }
441
442 // Store stdin to temporary file
443 storeFLVFromStdin(fp_infile);
444
445 // Close temporary file
446 fclose(fp_infile);
447
448 // Mimic normal input file, but don't forget to remove the temporary file
449 infile = tempfile;
450 unlink_infile = 1;
451 }
452
453 fp_infile = fopen(infile, "rb");
454 if(fp_infile == NULL) {
455 if(unlink_infile == 1)
456 unlink(infile);
457
458 exit(YAMDI_ERROR);
459 }
460
461 // Check if we have a valid FLV file
462 if(validateFLV(fp_infile) != YAMDI_OK) {
463 fclose(fp_infile);
464
465 if(unlink_infile == 1)
466 unlink(infile);
467
468 exit(YAMDI_ERROR);
469 }
470
471 // Open the outfile
472 fp_outfile = NULL;
473 if(outfile != NULL) {
474 if(strcmp(outfile, "-")) {
475 fp_outfile = fopen(outfile, "wb");
476 if(fp_outfile == NULL) {
477 fprintf(stderr, "Couldn't open %s.\n", outfile);
478
479 if(unlink_infile == 1)
480 unlink(infile);
481
482 exit(YAMDI_ERROR);
483 }
484 }
485 else
486 fp_outfile = stdout;
487 }
488
489 // Open the XML outputfile
490 fp_xmloutfile = NULL;
491 if(xmloutfile != NULL) {
492 if(strcmp(xmloutfile, "-")) {
493 fp_xmloutfile = fopen(xmloutfile, "wb");
494 if(fp_xmloutfile == NULL) {
495 fprintf(stderr, "Couldn't open %s.\n", xmloutfile);
496
497 if(unlink_infile == 1)
498 unlink(infile);
499
500 exit(YAMDI_ERROR);
501 }
502 }
503 else
504 fp_xmloutfile = stdout;
505 }
506
507 // Check the options
508 if(flv.options.stripmetadata == 1) {
509 flv.options.addonlastkeyframe = 0;
510 flv.options.addonlastsecond = 0;
511 flv.options.addonmetadata = 0;
512 }
513 else
514 flv.options.addonmetadata = 1;
515
516 // Create an index of the FLV file
517 if(indexFLV(&flv, fp_infile) != YAMDI_OK) {
518 fclose(fp_infile);
519
520 if(unlink_infile == 1)
521 unlink(infile);
522
523 exit(YAMDI_ERROR);
524 }
525
526 if(analyzeFLV(&flv, fp_infile) != YAMDI_OK) {
527 fclose(fp_infile);
528
529 if(unlink_infile == 1)
530 unlink(infile);
531
532 exit(YAMDI_ERROR);
533 }
534
535 if(finalizeFLV(&flv, fp_infile) != YAMDI_OK) {
536 fclose(fp_infile);
537
538 if(unlink_infile == 1)
539 unlink(infile);
540
541 exit(YAMDI_ERROR);
542 }
543
544 #ifdef DEBUG
545 fprintf(stderr, "[FLV] onmetadata = %d bytes (%d bytes allocated)\n", flv.onmetadata.used, flv.onmetadata.size);
546 fprintf(stderr, "[FLV] onlastsecond = %d bytes (%d bytes allocated)\n", flv.onlastsecond.used, flv.onlastsecond.size);
547 fprintf(stderr, "[FLV] onlastkeyframe = %d bytes (%d bytes allocated)\n", flv.onlastkeyframe.used, flv.onlastkeyframe.size);
548 #endif
549
550 if(fp_outfile != NULL)
551 writeFLV(fp_outfile, &flv, fp_infile);
552
553 if(fp_xmloutfile != NULL)
554 writeXMLMetadata(fp_xmloutfile, infile, outfile, &flv);
555
556 fclose(fp_infile);
557
558 // Remove the input file if it is the temporary file
559 if(unlink_infile == 1)
560 unlink(infile);
561
562 if(fp_outfile != NULL && fp_outfile != stdout)
563 fclose(fp_outfile);
564
565 if(fp_xmloutfile != NULL && fp_xmloutfile != stdout)
566 fclose(fp_xmloutfile);
567
568 if(flv.options.overwriteinput == 1 && strcmp(infile, "-") && outfile != NULL && strcmp(outfile, "-")) {
569 if(rename(outfile, infile) != 0)
570 exit(YAMDI_RENAME_OUTPUT);
571 }
572
573 freeFLV(&flv);
574
575 return YAMDI_OK;
576 }
577
validateFLV(FILE * fp)578 int validateFLV(FILE *fp) {
579 unsigned char buffer[FLV_SIZE_HEADER + FLV_SIZE_PREVIOUSTAGSIZE];
580 off_t filesize;
581
582 fseeko(fp, 0, SEEK_END);
583 filesize = ftello(fp);
584
585 // Check for minimal FLV file length
586 if(filesize < (FLV_SIZE_HEADER + FLV_SIZE_PREVIOUSTAGSIZE))
587 return YAMDI_FILE_TOO_SMALL;
588
589 rewind(fp);
590
591 if(readBytes(buffer, FLV_SIZE_HEADER + FLV_SIZE_PREVIOUSTAGSIZE, fp) != YAMDI_OK)
592 return YAMDI_READ_ERROR;
593
594 // Check the FLV signature
595 if(buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V')
596 return YAMDI_INVALID_SIGNATURE;
597
598 // Check the FLV version
599 if(FLV_UI8(&buffer[3]) != 1)
600 return YAMDI_INVALID_FLVVERSION;
601
602 // Check the DataOffset
603 if(FLV_UI32(&buffer[5]) != FLV_SIZE_HEADER)
604 return YAMDI_INVALID_DATASIZE;
605
606 // Check the PreviousTagSize0 value
607 if(FLV_UI32(&buffer[FLV_SIZE_HEADER]) != 0)
608 return YAMDI_INVALID_PREVIOUSTAGSIZE;
609
610 return YAMDI_OK;
611 }
612
initFLV(FLV_t * flv)613 int initFLV(FLV_t *flv) {
614 if(flv == NULL)
615 return YAMDI_ERROR;
616
617 memset(flv, 0, sizeof(FLV_t));
618
619 return YAMDI_OK;
620 }
621
indexFLV(FLV_t * flv,FILE * fp)622 int indexFLV(FLV_t *flv, FILE *fp) {
623 off_t offset;
624 size_t nflvtags;
625 FLVTag_t flvtag;
626
627 #ifdef DEBUG
628 fprintf(stderr, "[FLV] indexing file ...\n");
629 #endif
630
631 // Count how many tags are there in this FLV
632 offset = FLV_SIZE_HEADER + FLV_SIZE_PREVIOUSTAGSIZE;
633 nflvtags = 0;
634 while(readFLVTag(&flvtag, offset, fp) == YAMDI_OK) {
635 offset += (flvtag.tagsize + FLV_SIZE_PREVIOUSTAGSIZE);
636
637 nflvtags++;
638 }
639
640 flv->index.nflvtags = nflvtags;
641
642 #ifdef DEBUG
643 fprintf(stderr, "[FLV] nflvtags = %d\n", flv->index.nflvtags);
644 #endif
645
646 if(nflvtags == 0)
647 return YAMDI_OK;
648
649 // Allocate memory for the tag metadata index
650 flv->index.flvtag = (FLVTag_t *)calloc(flv->index.nflvtags, sizeof(FLVTag_t));
651 if(flv->index.flvtag == NULL)
652 return YAMDI_OUT_OF_MEMORY;
653
654 // Store the tag metadata in the index
655 offset = FLV_SIZE_HEADER + FLV_SIZE_PREVIOUSTAGSIZE;
656 nflvtags = 0;
657 while(readFLVTag(&flvtag, offset, fp) == YAMDI_OK) {
658 flv->index.flvtag[nflvtags].offset = flvtag.offset;
659 flv->index.flvtag[nflvtags].tagtype = flvtag.tagtype;
660 flv->index.flvtag[nflvtags].datasize = flvtag.datasize;
661 flv->index.flvtag[nflvtags].timestamp = flvtag.timestamp;
662 flv->index.flvtag[nflvtags].tagsize = flvtag.tagsize;
663
664 offset += (flv->index.flvtag[nflvtags].tagsize + FLV_SIZE_PREVIOUSTAGSIZE);
665
666 nflvtags++;
667 #ifdef DEBUG
668 if((nflvtags % 100) == 0)
669 fprintf(stderr, "[FLV] storing metadata (tag %d of %d)\r", nflvtags, flv->index.nflvtags);
670 #endif
671 }
672
673 #ifdef DEBUG
674 fprintf(stderr, "[FLV] storing metadata (tag %d of %d)\n", nflvtags, flv->index.nflvtags);
675 #endif
676
677 return YAMDI_OK;
678 }
679
storeFLVFromStdin(FILE * fp)680 void storeFLVFromStdin(FILE *fp) {
681 char buf[4096];
682 size_t bytes;
683
684 while((bytes = fread(buf, 1, sizeof(buf), stdin)) > 0)
685 fwrite(buf, 1, bytes, fp);
686
687 return;
688 }
689
freeFLV(FLV_t * flv)690 int freeFLV(FLV_t *flv) {
691 if(flv->index.nflvtags != 0)
692 free(flv->index.flvtag);
693
694 if(flv->keyframes.keyframelocations != NULL)
695 free(flv->keyframes.keyframelocations);
696
697 if(flv->keyframes.keyframetimestamps != NULL)
698 free(flv->keyframes.keyframetimestamps);
699
700 bufferFree(&flv->onmetadata);
701 bufferFree(&flv->onlastsecond);
702 bufferFree(&flv->onlastkeyframe);
703
704 memset(flv, 0, sizeof(FLV_t));
705
706 return YAMDI_OK;
707 }
708
analyzeFLV(FLV_t * flv,FILE * fp)709 int analyzeFLV(FLV_t *flv, FILE *fp) {
710 int rv;
711 size_t i, index;
712 unsigned char flags;
713 FLVTag_t *flvtag;
714
715 #ifdef DEBUG
716 fprintf(stderr, "[FLV] analyzing FLV ...\n");
717 #endif
718
719 for(i = 0; i < flv->index.nflvtags; i++) {
720 flvtag = &flv->index.flvtag[i];
721
722 if(flvtag->tagtype == FLV_TAG_AUDIO) {
723 flv->hasaudio = 1;
724
725 flv->audio.ntags++;
726 flv->audio.datasize += flvtag->datasize;
727 flv->audio.size += flvtag->tagsize;
728
729 flv->audio.lasttimestamp = flvtag->timestamp;
730 flv->audio.lastframeindex = i;
731
732 readFLVTagData(&flags, 1, flvtag, fp);
733
734 if(flv->audio.analyzed == 0) {
735 // SoundFormat
736 flv->audio.codecid = (flags >> 4) & 0xf;
737
738 // SoundRate
739 flv->audio.samplerate = (flags >> 2) & 0x3;
740
741 // SoundSize
742 flv->audio.samplesize = (flags >> 1) & 0x1;
743
744 // SoundType
745 flv->audio.stereo = flags & 0x1;
746
747 if(flv->audio.codecid == 4 || flv->audio.codecid == 5 || flv->audio.codecid == 6) {
748 // Nellymoser
749 flv->audio.stereo = 0;
750 }
751 else if(flv->audio.codecid == 10) {
752 // AAC
753 flv->audio.samplerate = 3;
754 flv->audio.stereo = 1;
755 }
756
757 flv->audio.analyzed = 1;
758 }
759 }
760 else if(flvtag->tagtype == FLV_TAG_VIDEO) {
761 flv->hasvideo = 1;
762
763 flv->video.ntags++;
764 flv->video.datasize += flvtag->datasize;
765 flv->video.size += flvtag->tagsize;
766
767 flv->video.lasttimestamp = flvtag->timestamp;
768 flv->video.lastframeindex = i;
769
770 readFLVTagData(&flags, 1, flvtag, fp);
771
772 // Keyframes
773 flvtag->keyframe = (flags >> 4) & 0xf;
774 if(flvtag->keyframe == 1) {
775 flv->canseektoend = 1;
776 flv->keyframes.nkeyframes++;
777 flv->keyframes.lastkeyframeindex = i;
778 }
779 else
780 flv->canseektoend = 0;
781
782 if(flvtag->keyframe == 1 && flv->video.analyzed == 0) {
783 // Video Codec
784 flv->video.codecid = flags & 0xf;
785
786 switch(flv->video.codecid) {
787 case FLV_PACKET_H263VIDEO:
788 rv = analyzeFLVH263VideoPacket(flv, flvtag, fp);
789 break;
790 case FLV_PACKET_SCREENVIDEO:
791 rv = analyzeFLVScreenVideoPacket(flv, flvtag, fp);
792 break;
793 case FLV_PACKET_VP6VIDEO:
794 rv = analyzeFLVVP6VideoPacket(flv, flvtag, fp);
795 break;
796 case FLV_PACKET_VP6ALPHAVIDEO:
797 rv = analyzeFLVVP6AlphaVideoPacket(flv, flvtag, fp);
798 break;
799 case FLV_PACKET_SCREENV2VIDEO:
800 rv = analyzeFLVScreenVideoPacket(flv, flvtag, fp);
801 break;
802 case FLV_PACKET_H264VIDEO:
803 rv = analyzeFLVH264VideoPacket(flv, flvtag, fp);
804 break;
805 default:
806 rv = YAMDI_ERROR;
807 break;
808 }
809
810 if(rv == YAMDI_OK)
811 flv->video.analyzed = 1;
812 }
813 }
814
815 flv->lasttimestamp = flvtag->timestamp;
816
817 #ifdef DEBUG
818 if((i % 100) == 0)
819 fprintf(stderr, "[FLV] analyzing FLV (tag %d of %d)\r", i, flv->index.nflvtags);
820 #endif
821 }
822
823 #ifdef DEBUG
824 fprintf(stderr, "[FLV] analyzing FLV (tag %d of %d)\n", i, flv->index.nflvtags);
825
826 fprintf(stderr, "[FLV] lasttimestamp = %d ms\n", flv->lasttimestamp);
827 #endif
828
829 // Calculate the last second
830 if(flv->lasttimestamp >= 1000) {
831 flv->lastsecond = flv->lasttimestamp - 1000;
832 for(i = (flv->index.nflvtags - 1); i >= 0; i--) {
833 flvtag = &flv->index.flvtag[i];
834
835 if(flvtag->timestamp <= flv->lastsecond) {
836 flv->lastsecond += 1;
837 flv->lastsecondindex = i;
838
839 break;
840 }
841 }
842 }
843 else
844 flv->options.addonlastsecond = 0;
845
846 #ifdef DEBUG
847 fprintf(stderr, "[FLV] lastsecond = %d ms\n", flv->lastsecond);
848 fprintf(stderr, "[FLV] lastsecondindex = %d\n", flv->lastsecondindex);
849 #endif
850
851 // Calculate audio datarate
852 if(flv->audio.datasize != 0)
853 flv->audio.datarate = (double)flv->audio.datasize * 8.0 / 1024.0 / (double)flv->audio.lasttimestamp * 1000.0;
854
855 #ifdef DEBUG
856 fprintf(stderr, "[FLV] audio.codecid = %d\n", flv->audio.codecid);
857 fprintf(stderr, "[FLV] audio.lasttimestamp = %d ms\n", flv->audio.lasttimestamp);
858 fprintf(stderr, "[FLV] audio.lastframeindex = %d\n", flv->audio.lastframeindex);
859 fprintf(stderr, "[FLV] audio.ntags = %d\n", flv->audio.ntags);
860 fprintf(stderr, "[FLV] audio.datasize = %" PRIu64 " kb\n", flv->audio.datasize);
861 fprintf(stderr, "[FLV] audio.datarate = %f kbit/s\n", flv->audio.datarate);
862 #endif
863
864 // Calculate video framerate
865 if(flv->video.ntags != 0)
866 flv->video.framerate = (double)flv->video.ntags / (double)flv->video.lasttimestamp * 1000.0;
867
868 // Calculate video datarate
869 if(flv->video.datasize != 0)
870 flv->video.datarate = (double)flv->video.datasize * 8.0 / 1024.0 / (double)flv->lasttimestamp * 1000.0;
871
872 #ifdef DEBUG
873 fprintf(stderr, "[FLV] video.codecid = %d\n", flv->video.codecid);
874 fprintf(stderr, "[FLV] video.lasttimestamp = %d ms\n", flv->video.lasttimestamp);
875 fprintf(stderr, "[FLV] video.lastframeindex = %d\n", flv->video.lastframeindex);
876 fprintf(stderr, "[FLV] video.ntags = %d\n", flv->video.ntags);
877 fprintf(stderr, "[FLV] video.framerate = %f fps\n", flv->video.framerate);
878 fprintf(stderr, "[FLV] video.datasize = %" PRIu64 " kb\n", flv->video.datasize);
879 fprintf(stderr, "[FLV] video.datarate = %f kbit/s\n", flv->video.datarate);
880 fprintf(stderr, "[FLV] video.width = %d\n", flv->video.width);
881 fprintf(stderr, "[FLV] video.height = %d\n", flv->video.height);
882 #endif
883
884 // Calculate datasize
885 flv->datasize = flv->audio.size + (flv->audio.ntags * FLV_SIZE_PREVIOUSTAGSIZE) + flv->video.size + (flv->video.ntags * FLV_SIZE_PREVIOUSTAGSIZE);
886
887 #ifdef DEBUG
888 fprintf(stderr, "[FLV] datasize = %" PRIu64 " kb\n", flv->datasize);
889 #endif
890
891 // Fake keyframes if we have only audio and the corresponding option has been set
892 if(flv->options.addaudiokeyframes == 1 && flv->hasaudio == 1 && flv->hasvideo == 0) {
893 // Add a keyframe at least every x milliseconds
894 flv->audio.keyframerate = (int)((double)flv->audio.keyframedistance / (double)flv->audio.lasttimestamp * (double)flv->audio.ntags);
895
896 // If every frame is longer than the intervalthen add every frame a keyframe
897 if(flv->audio.keyframerate == 0)
898 flv->audio.keyframerate = 1;
899 else if(flv->audio.keyframerate >= flv->audio.ntags)
900 flv->audio.keyframerate = flv->audio.ntags;
901
902 #ifdef DEBUG
903 fprintf(stderr, "[FLV] audio.keyframerate = %d\n", flv->audio.keyframerate);
904 #endif
905
906 // Mark all audio tags that should be considered as a keyframe
907 index = 0;
908 for(i = 0; i < flv->index.nflvtags; i++) {
909 flvtag = &flv->index.flvtag[i];
910
911 if(flvtag->tagtype == FLV_TAG_AUDIO) {
912 if((index % flv->audio.keyframerate) == 0) {
913 flvtag->keyframe = 1;
914 flv->keyframes.nkeyframes++;
915 }
916
917 index++;
918 }
919 }
920
921 // Add an extra keyframe for the last frame
922 if(flv->index.flvtag[flv->audio.lastframeindex].keyframe == 0) {
923 flv->index.flvtag[flv->audio.lastframeindex].keyframe = 1;
924 flv->keyframes.nkeyframes++;
925 }
926
927 flv->canseektoend = 1;
928 flv->keyframes.lastkeyframeindex = flv->audio.lastframeindex;
929 }
930 else
931 flv->options.addaudiokeyframes = 0;
932
933 #ifdef DEBUG
934 fprintf(stderr, "[FLV] keyframes.nkeyframes = %d\n", flv->keyframes.nkeyframes);
935 fprintf(stderr, "[FLV] keyframes.lastkeyframeindex = %d\n", flv->keyframes.lastkeyframeindex);
936 #endif
937
938 // Allocate some memory for the keyframe index
939 if(flv->keyframes.nkeyframes != 0) {
940 flv->haskeyframes = 1;
941
942 flv->keyframes.keyframelocations = (off_t *)calloc(flv->keyframes.nkeyframes, sizeof(off_t));
943 if(flv->keyframes.keyframelocations == NULL)
944 return YAMDI_OUT_OF_MEMORY;
945
946 flv->keyframes.keyframetimestamps = (int *)calloc(flv->keyframes.nkeyframes, sizeof(int));
947 if(flv->keyframes.keyframetimestamps == NULL)
948 return YAMDI_OUT_OF_MEMORY;
949 }
950
951 return YAMDI_OK;
952 }
953
finalizeFLV(FLV_t * flv,FILE * fp)954 int finalizeFLV(FLV_t *flv, FILE *fp) {
955 size_t i, index;
956 FLVTag_t *flvtag;
957
958 // 2 passes
959 // 1. create onmetadata event to get the size of it (it doesn't matter if the values are not yet correct)
960 // 2. calculate the new keyframelocations and the final filesize
961 // pay attention to the size of these events
962 // onmetadata
963 // onlastsecond
964 // onlastkeyframe
965 // (oncuepoint) keep them from the input flv?
966 // 3. recreate the onmetadata event with the correct values
967 // filesize
968 // keyframelocations
969 // lastkeyframelocation
970
971 // Create the metadata tags. Even though we don't have all values,
972 // the size will not change. We need the size.
973 createFLVEvents(flv);
974
975 // Start calculating the final filesize
976 flv->filesize = 0;
977
978 // FLV header + PreviousTagSize
979 flv->filesize += FLV_SIZE_HEADER + FLV_SIZE_PREVIOUSTAGSIZE;
980
981 // onMetaData event
982 if(flv->options.addonmetadata == 1)
983 flv->filesize += flv->onmetadata.used;
984
985 // Calculate the final filesize and update the keyframe index
986 index = 0;
987 for(i = 0; i < flv->index.nflvtags; i++) {
988 flvtag = &flv->index.flvtag[i];
989
990 // Skip every script tag (subject to change if we want to keep existing events)
991 if(flvtag->tagtype != FLV_TAG_AUDIO && flvtag->tagtype != FLV_TAG_VIDEO)
992 continue;
993
994 // Take care of the onlastsecond event
995 if(flv->options.addonlastsecond == 1 && flv->lastsecondindex == i)
996 flv->filesize += flv->onlastsecond.used;
997
998 // Update the keyframe index only if there are keyframes ...
999 if(flv->haskeyframes == 1) {
1000 if(flvtag->tagtype == FLV_TAG_VIDEO || flvtag->tagtype == FLV_TAG_AUDIO) {
1001 // Keyframes
1002 if(flvtag->keyframe == 1) {
1003 // Take care of the onlastkeyframe event
1004 if(flv->options.addonlastkeyframe == 1 && flv->keyframes.lastkeyframeindex == i)
1005 flv->filesize += flv->onlastkeyframe.used;
1006
1007 flv->keyframes.keyframelocations[index] = flv->filesize;
1008 flv->keyframes.keyframetimestamps[index] = flvtag->timestamp;
1009
1010 index++;
1011 }
1012 }
1013 }
1014
1015 flv->filesize += flvtag->tagsize + FLV_SIZE_PREVIOUSTAGSIZE;
1016 }
1017
1018 if(flv->haskeyframes == 1) {
1019 flv->keyframes.lastkeyframetimestamp = flv->keyframes.keyframetimestamps[flv->keyframes.nkeyframes - 1];
1020 flv->keyframes.lastkeyframelocation = flv->keyframes.keyframelocations[flv->keyframes.nkeyframes - 1];
1021 }
1022
1023 #ifdef DEBUG
1024 fprintf(stderr, "[FLV] keyframes.lastkeyframetimestamp = %d ms\n", flv->keyframes.lastkeyframetimestamp);
1025 fprintf(stderr, "[FLV] keyframes.lastkeyframelocation = %" PRIi64 "\n", flv->keyframes.lastkeyframelocation);
1026
1027 fprintf(stderr, "[FLV] filesize = %" PRIu64 " kb\n", flv->filesize);
1028 #endif
1029
1030 // Create the metadata tags with the correct values
1031 createFLVEvents(flv);
1032
1033 return YAMDI_OK;
1034 }
1035
writeFLV(FILE * out,FLV_t * flv,FILE * fp)1036 int writeFLV(FILE *out, FLV_t *flv, FILE *fp) {
1037 size_t i, datasize = 0;
1038 unsigned char *data = NULL, *d;
1039 FLVTag_t *flvtag;
1040
1041 if(fp == NULL)
1042 return YAMDI_ERROR;
1043
1044 // Write the header
1045 writeFLVHeader(out, flv->hasaudio, flv->hasvideo);
1046 writeFLVPreviousTagSize(out, 0);
1047
1048 // Write the onMetaData tag
1049 if(flv->options.addonmetadata == 1)
1050 fwrite(flv->onmetadata.data, flv->onmetadata.used, 1, out);
1051
1052 // Copy the audio and video tags
1053 for(i = 0; i < flv->index.nflvtags; i++) {
1054 flvtag = &flv->index.flvtag[i];
1055
1056 // Skip every script tag (subject to change if we want to keep existing events)
1057 if(flvtag->tagtype != FLV_TAG_AUDIO && flvtag->tagtype != FLV_TAG_VIDEO)
1058 continue;
1059
1060 // Write the onlastsecond event
1061 if(flv->options.addonlastsecond == 1 && flv->lastsecondindex == i)
1062 fwrite(flv->onlastsecond.data, flv->onlastsecond.used, 1, out);
1063
1064 // Write the onlastkeyframe event
1065 if(flv->options.addonlastkeyframe == 1 && flv->keyframes.lastkeyframeindex == i)
1066 fwrite(flv->onlastkeyframe.data, flv->onlastkeyframe.used, 1, out);
1067
1068 writeFLVDataTag(out, flvtag->tagtype, flvtag->timestamp, flvtag->datasize);
1069
1070 // Read the data
1071 if(flvtag->datasize > datasize) {
1072 d = (unsigned char *)realloc(data, flvtag->datasize);
1073 if(d == NULL)
1074 return YAMDI_OUT_OF_MEMORY;
1075
1076 data = d;
1077 datasize = flvtag->datasize;
1078 }
1079
1080 if(readFLVTagData(data, flvtag->datasize, flvtag, fp) != YAMDI_OK)
1081 return YAMDI_READ_ERROR;
1082
1083 fwrite(data, flvtag->datasize, 1, out);
1084
1085 writeFLVPreviousTagSize(out, flvtag->tagsize);
1086 }
1087
1088 if(data != NULL)
1089 free(data);
1090
1091 // We are done!
1092
1093 return YAMDI_OK;
1094 }
1095
writeFLVHeader(FILE * fp,int hasaudio,int hasvideo)1096 int writeFLVHeader(FILE *fp, int hasaudio, int hasvideo) {
1097 unsigned char bytes[FLV_SIZE_HEADER];
1098
1099 // Signature
1100 bytes[0] = 'F';
1101 bytes[1] = 'L';
1102 bytes[2] = 'V';
1103
1104 // Version
1105 bytes[3] = 1;
1106
1107 // Flags
1108 bytes[4] = 0;
1109
1110 if(hasaudio == 1)
1111 bytes[4] |= 0x4;
1112
1113 if(hasvideo == 1)
1114 bytes[4] |= 0x1;
1115
1116 // DataOffset
1117 bytes[5] = ((FLV_SIZE_HEADER >> 24) & 0xff);
1118 bytes[6] = ((FLV_SIZE_HEADER >> 16) & 0xff);
1119 bytes[7] = ((FLV_SIZE_HEADER >> 8) & 0xff);
1120 bytes[8] = ((FLV_SIZE_HEADER >> 0) & 0xff);
1121
1122 fwrite(bytes, FLV_SIZE_HEADER, 1, fp);
1123
1124 return YAMDI_OK;
1125 }
1126
createFLVEvents(FLV_t * flv)1127 int createFLVEvents(FLV_t *flv) {
1128 if(flv->options.addonmetadata == 1)
1129 createFLVEventOnMetaData(flv);
1130
1131 if(flv->options.addonlastkeyframe == 1)
1132 createFLVEventOnLastKeyframe(flv);
1133
1134 if(flv->options.addonlastsecond == 1)
1135 createFLVEventOnLastSecond(flv);
1136
1137 return YAMDI_OK;
1138 }
1139
createFLVEventOnMetaData(FLV_t * flv)1140 int createFLVEventOnMetaData(FLV_t *flv) {
1141 int pass = 0;
1142 size_t i, length = 0;
1143 buffer_t b;
1144
1145 bufferInit(&b);
1146
1147 onmetadatapass:
1148 bufferReset(&b);
1149
1150 // ScriptDataObject
1151 writeBufferFLVScriptDataObject(&b);
1152
1153 writeBufferFLVScriptDataECMAArray(&b, "onMetaData", length);
1154
1155 length = 0;
1156
1157 if(strlen(flv->options.creator) != 0) {
1158 writeBufferFLVScriptDataValueString(&b, "creator", flv->options.creator); length++;
1159 }
1160
1161 writeBufferFLVScriptDataValueString(&b, "metadatacreator", "Yet Another Metadata Injector for FLV - Version " YAMDI_VERSION "\0"); length++;
1162 writeBufferFLVScriptDataValueBool(&b, "hasKeyframes", flv->haskeyframes); length++;
1163 writeBufferFLVScriptDataValueBool(&b, "hasVideo", flv->hasvideo); length++;
1164 writeBufferFLVScriptDataValueBool(&b, "hasAudio", flv->hasaudio); length++;
1165 writeBufferFLVScriptDataValueBool(&b, "hasMetadata", 1); length++;
1166 writeBufferFLVScriptDataValueBool(&b, "canSeekToEnd", flv->canseektoend); length++;
1167
1168 writeBufferFLVScriptDataValueDouble(&b, "duration", (double)flv->lasttimestamp / 1000.0); length++;
1169 writeBufferFLVScriptDataValueDouble(&b, "datasize", (double)flv->datasize); length++;
1170
1171 if(flv->hasvideo == 1) {
1172 writeBufferFLVScriptDataValueDouble(&b, "videosize", (double)flv->video.size); length++;
1173 writeBufferFLVScriptDataValueDouble(&b, "framerate", (double)flv->video.framerate); length++;
1174 writeBufferFLVScriptDataValueDouble(&b, "videodatarate", (double)flv->video.datarate); length++;
1175
1176 if(flv->video.analyzed == 1) {
1177 writeBufferFLVScriptDataValueDouble(&b, "videocodecid", (double)flv->video.codecid); length++;
1178 writeBufferFLVScriptDataValueDouble(&b, "width", (double)flv->video.width); length++;
1179 writeBufferFLVScriptDataValueDouble(&b, "height", (double)flv->video.height); length++;
1180 }
1181 }
1182
1183 if(flv->hasaudio == 1) {
1184 writeBufferFLVScriptDataValueDouble(&b, "audiosize", (double)flv->audio.size); length++;
1185 writeBufferFLVScriptDataValueDouble(&b, "audiodatarate", (double)flv->audio.datarate); length++;
1186
1187 if(flv->audio.analyzed == 1) {
1188 writeBufferFLVScriptDataValueDouble(&b, "audiocodecid", (double)flv->audio.codecid); length++;
1189 writeBufferFLVScriptDataValueDouble(&b, "audiosamplerate", (double)flv->audio.samplerate); length++;
1190 writeBufferFLVScriptDataValueDouble(&b, "audiosamplesize", (double)flv->audio.samplesize); length++;
1191 writeBufferFLVScriptDataValueBool(&b, "stereo", flv->audio.stereo); length++;
1192 }
1193 }
1194
1195 writeBufferFLVScriptDataValueDouble(&b, "filesize", (double)flv->filesize); length++;
1196 writeBufferFLVScriptDataValueDouble(&b, "lasttimestamp", (double)flv->lasttimestamp / 1000.0); length++;
1197
1198 if(flv->haskeyframes == 1) {
1199 writeBufferFLVScriptDataValueDouble(&b, "lastkeyframetimestamp", (double)flv->keyframes.lastkeyframetimestamp / 1000.0); length++;
1200 writeBufferFLVScriptDataValueDouble(&b, "lastkeyframelocation", (double)flv->keyframes.lastkeyframelocation); length++;
1201
1202 writeBufferFLVScriptDataVariableArray(&b, "keyframes"); length++;
1203
1204 writeBufferFLVScriptDataValueArray(&b, "filepositions", flv->keyframes.nkeyframes);
1205
1206 for(i = 0; i < flv->keyframes.nkeyframes; i++)
1207 writeBufferFLVScriptDataValueDouble(&b, NULL, (double)flv->keyframes.keyframelocations[i]);
1208
1209 writeBufferFLVScriptDataValueArray(&b, "times", flv->keyframes.nkeyframes);
1210
1211 for(i = 0; i < flv->keyframes.nkeyframes; i++)
1212 writeBufferFLVScriptDataValueDouble(&b, NULL, (double)flv->keyframes.keyframetimestamps[i] / 1000.0);
1213
1214 writeBufferFLVScriptDataVariableArrayEnd(&b);
1215 }
1216
1217 writeBufferFLVScriptDataVariableArrayEnd(&b);
1218
1219 if(pass == 0) {
1220 pass = 1;
1221 goto onmetadatapass;
1222 }
1223
1224 // Write the onMetaData tag
1225 bufferReset(&flv->onmetadata);
1226
1227 writeBufferFLVScriptDataTag(&flv->onmetadata, 0, b.used);
1228 bufferAppendBuffer(&flv->onmetadata, &b);
1229 writeBufferFLVPreviousTagSize(&flv->onmetadata, flv->onmetadata.used);
1230
1231 bufferFree(&b);
1232
1233 return YAMDI_OK;
1234 }
1235
createFLVEventOnLastSecond(FLV_t * flv)1236 int createFLVEventOnLastSecond(FLV_t *flv) {
1237 buffer_t b;
1238
1239 bufferInit(&b);
1240
1241 // ScriptDataObject
1242 writeBufferFLVScriptDataObject(&b);
1243
1244 writeBufferFLVScriptDataECMAArray(&b, "onLastSecond", 0);
1245 writeBufferFLVScriptDataVariableArrayEnd(&b);
1246
1247 // Write the onLastSecond tag
1248 bufferReset(&flv->onlastsecond);
1249
1250 writeBufferFLVScriptDataTag(&flv->onlastsecond, flv->lastsecond, b.used);
1251 bufferAppendBuffer(&flv->onlastsecond, &b);
1252 writeBufferFLVPreviousTagSize(&flv->onlastsecond, flv->onlastsecond.used);
1253
1254 bufferFree(&b);
1255
1256 return YAMDI_OK;
1257 }
1258
createFLVEventOnLastKeyframe(FLV_t * flv)1259 int createFLVEventOnLastKeyframe(FLV_t *flv) {
1260 buffer_t b;
1261
1262 bufferInit(&b);
1263
1264 // ScriptDataObject
1265 writeBufferFLVScriptDataObject(&b);
1266
1267 writeBufferFLVScriptDataECMAArray(&b, "onLastKeyframe", 0);
1268 writeBufferFLVScriptDataVariableArrayEnd(&b);
1269
1270 // Write the onLastKeyframe tag
1271 bufferReset(&flv->onlastkeyframe);
1272
1273 writeBufferFLVScriptDataTag(&flv->onlastkeyframe, flv->keyframes.lastkeyframetimestamp - 1, b.used);
1274 bufferAppendBuffer(&flv->onlastkeyframe, &b);
1275 writeBufferFLVPreviousTagSize(&flv->onlastkeyframe, flv->onlastkeyframe.used);
1276
1277 bufferFree(&b);
1278
1279 return YAMDI_OK;
1280 }
1281
writeBufferFLVScriptDataTag(buffer_t * buffer,int timestamp,size_t datasize)1282 int writeBufferFLVScriptDataTag(buffer_t *buffer, int timestamp, size_t datasize) {
1283 unsigned char bytes[FLV_SIZE_TAGHEADER];
1284
1285 bytes[ 0] = FLV_TAG_SCRIPTDATA;
1286
1287 // DataSize
1288 bytes[ 1] = ((datasize >> 16) & 0xff);
1289 bytes[ 2] = ((datasize >> 8) & 0xff);
1290 bytes[ 3] = ((datasize >> 0) & 0xff);
1291
1292 // Timestamp
1293 bytes[ 4] = ((timestamp >> 16) & 0xff);
1294 bytes[ 5] = ((timestamp >> 8) & 0xff);
1295 bytes[ 6] = ((timestamp >> 0) & 0xff);
1296
1297 // TimestampExtended
1298 bytes[ 7] = ((timestamp >> 24) & 0xff);
1299
1300 // StreamID
1301 bytes[ 8] = 0;
1302 bytes[ 9] = 0;
1303 bytes[10] = 0;
1304
1305 bufferAppendBytes(buffer, bytes, FLV_SIZE_TAGHEADER);
1306
1307 return YAMDI_OK;
1308 }
1309
writeFLVDataTag(FILE * fp,int type,int timestamp,size_t datasize)1310 int writeFLVDataTag(FILE *fp, int type, int timestamp, size_t datasize) {
1311 unsigned char bytes[FLV_SIZE_TAGHEADER];
1312
1313 bytes[ 0] = type;
1314
1315 // DataSize
1316 bytes[ 1] = ((datasize >> 16) & 0xff);
1317 bytes[ 2] = ((datasize >> 8) & 0xff);
1318 bytes[ 3] = ((datasize >> 0) & 0xff);
1319
1320 // Timestamp
1321 bytes[ 4] = ((timestamp >> 16) & 0xff);
1322 bytes[ 5] = ((timestamp >> 8) & 0xff);
1323 bytes[ 6] = ((timestamp >> 0) & 0xff);
1324
1325 // TimestampExtended
1326 bytes[ 7] = ((timestamp >> 24) & 0xff);
1327
1328 // StreamID
1329 bytes[ 8] = 0;
1330 bytes[ 9] = 0;
1331 bytes[10] = 0;
1332
1333 fwrite(bytes, FLV_SIZE_TAGHEADER, 1, fp);
1334
1335 return YAMDI_OK;
1336 }
1337
writeBufferFLVPreviousTagSize(buffer_t * buffer,size_t tagsize)1338 int writeBufferFLVPreviousTagSize(buffer_t *buffer, size_t tagsize) {
1339 unsigned char bytes[4];
1340
1341 bytes[0] = ((tagsize >> 24) & 0xff);
1342 bytes[1] = ((tagsize >> 16) & 0xff);
1343 bytes[2] = ((tagsize >> 8) & 0xff);
1344 bytes[3] = ((tagsize >> 0) & 0xff);
1345
1346 bufferAppendBytes(buffer, bytes, 4);
1347
1348 return YAMDI_OK;
1349 }
1350
writeFLVPreviousTagSize(FILE * fp,size_t tagsize)1351 int writeFLVPreviousTagSize(FILE *fp, size_t tagsize) {
1352 unsigned char bytes[4];
1353
1354 bytes[0] = ((tagsize >> 24) & 0xff);
1355 bytes[1] = ((tagsize >> 16) & 0xff);
1356 bytes[2] = ((tagsize >> 8) & 0xff);
1357 bytes[3] = ((tagsize >> 0) & 0xff);
1358
1359 fwrite(bytes, 4, 1, fp);
1360
1361 return YAMDI_OK;
1362 }
1363
analyzeFLVH263VideoPacket(FLV_t * flv,FLVTag_t * flvtag,FILE * fp)1364 int analyzeFLVH263VideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp) {
1365 int startcode, picturesize;
1366 unsigned char *buffer, data[10];
1367
1368 readFLVTagData(data, sizeof(data), flvtag, fp);
1369 // Skip the VIDEODATA header
1370 buffer = &data[1];
1371
1372 // 8bit |pppppppp|pppppppp|pvvvvvrr|rrrrrrss|swwwwwww|whhhhhhh|h
1373 // 16bit |pppppppp|pppppppp|pvvvvvrr|rrrrrrss|swwwwwww|wwwwwwww|whhhhhhh|hhhhhhhh|h
1374
1375 startcode = FLV_UI24(buffer) >> 7;
1376 if(startcode != 1)
1377 return YAMDI_ERROR;
1378
1379 picturesize = ((buffer[3] & 0x3) << 1) + ((buffer[4] >> 7) & 0x1);
1380
1381 switch(picturesize) {
1382 case 0: // Custom 8bit
1383 flv->video.width = ((buffer[4] & 0x7f) << 1) + ((buffer[5] >> 7) & 0x1);
1384 flv->video.height = ((buffer[5] & 0x7f) << 1) + ((buffer[6] >> 7) & 0x1);
1385 break;
1386 case 1: // Custom 16bit
1387 flv->video.width = ((buffer[4] & 0x7f) << 9) + (buffer[5] << 1) + ((buffer[6] >> 7) & 0x1);
1388 flv->video.height = ((buffer[6] & 0x7f) << 9) + (buffer[7] << 1) + ((buffer[8] >> 7) & 0x1);
1389 break;
1390 case 2: // CIF
1391 flv->video.width = 352.0;
1392 flv->video.height = 288.0;
1393 break;
1394 case 3: // QCIF
1395 flv->video.width = 176.0;
1396 flv->video.height = 144.0;
1397 break;
1398 case 4: // SQCIF
1399 flv->video.width = 128.0;
1400 flv->video.height = 96.0;
1401 break;
1402 case 5:
1403 flv->video.width = 320.0;
1404 flv->video.height = 240.0;
1405 break;
1406 case 6:
1407 flv->video.width = 160.0;
1408 flv->video.height = 120.0;
1409 break;
1410 default:
1411 break;
1412 }
1413
1414 return YAMDI_OK;
1415 }
1416
analyzeFLVScreenVideoPacket(FLV_t * flv,FLVTag_t * flvtag,FILE * fp)1417 int analyzeFLVScreenVideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp) {
1418 unsigned char *buffer, data[5];
1419
1420 // |1111wwww|wwwwwwww|2222hhhh|hhhhhhhh|
1421
1422 readFLVTagData(data, sizeof(data), flvtag, fp);
1423 // Skip the VIDEODATA header
1424 buffer = &data[1];
1425
1426 flv->video.width = ((buffer[0] & 0xf) << 8) + buffer[1];
1427 flv->video.height = ((buffer[2] & 0xf) << 8) + buffer[3];
1428
1429 return YAMDI_OK;
1430 }
1431
analyzeFLVVP6VideoPacket(FLV_t * flv,FLVTag_t * flvtag,FILE * fp)1432 int analyzeFLVVP6VideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp) {
1433 int offset = 3; // default buffer offset for dim_y
1434 unsigned char *buffer, data[10];
1435
1436 readFLVTagData(data, sizeof(data), flvtag, fp);
1437
1438 #if DEBUG
1439 fprintf(stderr, "\n");
1440
1441 fprintf(stderr, "[VP6] ");
1442 int i = 0;
1443 for(i = 0; i < sizeof(data); i++)
1444 fprintf(stderr, "%d=%d ", i, data[i]);
1445 fprintf(stderr, "\n");
1446 #endif
1447
1448 // Skip the VIDEODATA header
1449 buffer = &data[1];
1450
1451 // VP6FLVVIDEOPACKET header (as described in the SWF Specs v10, page 249)
1452 int hadjust = ((buffer[0] >> 4) & 0x0f);
1453 int vadjust = (buffer[0] & 0x0f);
1454
1455 // Raw vp6 video data (http://wiki.multimedia.cx/index.php?title=On2_VP6)
1456 int frame_mode = ((buffer[1] >> 7) & 0x01);
1457
1458 if(frame_mode != 0) // We need an iframe
1459 return YAMDI_ERROR;
1460
1461 int marker = (buffer[1] & 0x01); // Without checking this value, we support all VP6 variants
1462
1463 int version2 = ((buffer[2] >> 1) & 0x03);
1464
1465 #if DEBUG
1466 int version = ((buffer[2] >> 3) & 0x1f);
1467 int interlace = (buffer[2] & 0x01);
1468
1469 fprintf(stderr, "[VP6] marker = %d\n", marker);
1470 fprintf(stderr, "[VP6] version = %d\n", version);
1471 fprintf(stderr, "[VP6] version2 = %d\n", version2);
1472 fprintf(stderr, "[VP6] interlace = %d\n", interlace);
1473 #endif
1474
1475 // In these cases there are 2 more bytes that we have to skip
1476 if(marker == 1 || version2 == 0)
1477 offset += 2;
1478
1479 #if DEBUG
1480 fprintf(stderr, "[VP6] offset = %d\n", offset);
1481 fprintf(stderr, "[VP6] dim_y = %d\n", buffer[offset]);
1482 fprintf(stderr, "[VP6] dim_x = %d\n", buffer[offset + 1]);
1483 fprintf(stderr, "[VP6] render_y = %d\n", buffer[offset + 2]);
1484 fprintf(stderr, "[VP6] render_x = %d\n", buffer[offset + 3]);
1485 #endif
1486
1487 // Now offset points to the resolution values: [dim_y, dim_x, render_y, render_x]
1488 // Values represent macroblocks
1489 flv->video.height = (buffer[offset + 0] * 16) - vadjust;
1490 flv->video.width = (buffer[offset + 1] * 16) - hadjust;
1491
1492 return YAMDI_OK;
1493 }
1494
analyzeFLVVP6AlphaVideoPacket(FLV_t * flv,FLVTag_t * flvtag,FILE * fp)1495 int analyzeFLVVP6AlphaVideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp) {
1496 unsigned char *buffer, data[9];
1497
1498 readFLVTagData(data, sizeof(data), flvtag, fp);
1499 // Skip the VIDEODATA header
1500 buffer = &data[1];
1501
1502 flv->video.width = buffer[7] * 16 - (buffer[0] >> 4);
1503 flv->video.height = buffer[6] * 16 - (buffer[0] & 0x0f);
1504
1505 return YAMDI_OK;
1506 }
1507
analyzeFLVH264VideoPacket(FLV_t * flv,FLVTag_t * flvtag,FILE * fp)1508 int analyzeFLVH264VideoPacket(FLV_t *flv, FLVTag_t *flvtag, FILE *fp) {
1509 int avcpackettype;
1510 int i, length, offset, nSPS;
1511 unsigned char *avcc;
1512 unsigned char *buffer, data[flvtag->datasize];
1513 h264data_t h264data;
1514
1515 readFLVTagData(data, sizeof(data), flvtag, fp);
1516 // Skip the VIDEODATA header
1517 buffer = &data[1];
1518
1519 avcpackettype = buffer[0];
1520
1521 #ifdef DEBUG
1522 fprintf(stderr, "[FLV] AVCPacketType = %d\n", avcpackettype);
1523 #endif
1524
1525 if(avcpackettype != 0)
1526 return YAMDI_ERROR;
1527
1528 // AVCDecoderConfigurationRecord (14496-15, 5.2.4.1.1)
1529 avcc = (unsigned char *)&buffer[4];
1530
1531 nSPS = avcc[5] & 0x1f;
1532
1533 #ifdef DEBUG
1534 fprintf(stderr, "[AVC/H.264] AVCDecoderConfigurationRecord\n");
1535 fprintf(stderr, "[AVC/H.264] configurationVersion = %d\n", avcc[0]);
1536 fprintf(stderr, "[AVC/H.264] AVCProfileIndication = %d\n", avcc[1]);
1537 fprintf(stderr, "[AVC/H.264] profile_compatibility = %d\n", avcc[2]);
1538 fprintf(stderr, "[AVC/H.264] AVCLevelIndication = %d\n", avcc[3]);
1539 fprintf(stderr, "[AVC/H.264] lengthSizeMinusOne = %d\n", avcc[4] & 0x3);
1540 fprintf(stderr, "[AVC/H.264] numOfSequenceParameterSets = %d\n", nSPS);
1541 #endif
1542
1543 offset = 6;
1544 for(i = 0; i < nSPS; i++) {
1545 length = (avcc[offset] << 8) + avcc[offset + 1];
1546 #ifdef DEBUG
1547 fprintf(stderr, "[AVC/H.264]\tsequenceParameterSetLength = %d bit\n", 8 * length);
1548 #endif
1549 memset(&h264data, 0, sizeof(h264data_t));
1550 if(readH264NALUnit(&h264data, &avcc[offset + 2], length) == YAMDI_OK)
1551 break;
1552
1553 offset += (2 + length);
1554 }
1555
1556 // There would be some Picture Parameter Sets, but we don't need them. Bail out.
1557 /*
1558 int nPPS = avcc[offset++];
1559 fprintf(stderr, "numOfPictureParameterSets = %d\n", nPPS);
1560
1561 for(i = 0; i < nPPS; i++) {
1562 length = (avcc[offset] << 8) + avcc[offset + 1];
1563 #ifdef DEBUG
1564 fprintf(stderr, "[AVC/H.264]\tpictureParameterSetLength = %d bit\n", 8 * length);
1565 #endif
1566 readH264NALUnit(&avcc[offset + 2], length);
1567
1568 offset += (2 + length);
1569 }
1570 */
1571
1572 if(h264data.valid == 0)
1573 return YAMDI_ERROR;
1574
1575 flv->video.width = h264data.width;
1576 flv->video.height = h264data.height;
1577
1578 return YAMDI_OK;
1579 }
1580
writeBufferFLVScriptDataObject(buffer_t * buffer)1581 int writeBufferFLVScriptDataObject(buffer_t *buffer) {
1582 unsigned char type = 2;
1583
1584 bufferAppendBytes(buffer, &type, 1);
1585
1586 return YAMDI_OK;
1587 }
1588
writeBufferFLVScriptDataECMAArray(buffer_t * buffer,const char * name,size_t len)1589 int writeBufferFLVScriptDataECMAArray(buffer_t *buffer, const char *name, size_t len) {
1590 unsigned char type, bytes[4];
1591
1592 writeBufferFLVScriptDataString(buffer, name);
1593
1594 type = 8; // ECMAArray
1595 bufferAppendBytes(buffer, &type, 1);
1596
1597 bytes[0] = ((len >> 24) & 0xff);
1598 bytes[1] = ((len >> 16) & 0xff);
1599 bytes[2] = ((len >> 8) & 0xff);
1600 bytes[3] = ((len >> 0) & 0xff);
1601
1602 bufferAppendBytes(buffer, bytes, 4);
1603
1604 return YAMDI_OK;
1605 }
1606
writeBufferFLVScriptDataValueArray(buffer_t * buffer,const char * name,size_t len)1607 int writeBufferFLVScriptDataValueArray(buffer_t *buffer, const char *name, size_t len) {
1608 unsigned char type, bytes[4];
1609
1610 writeBufferFLVScriptDataString(buffer, name);
1611
1612 type = 10; // Value Array
1613 bufferAppendBytes(buffer, &type, 1);
1614
1615 bytes[0] = ((len >> 24) & 0xff);
1616 bytes[1] = ((len >> 16) & 0xff);
1617 bytes[2] = ((len >> 8) & 0xff);
1618 bytes[3] = ((len >> 0) & 0xff);
1619
1620 bufferAppendBytes(buffer, bytes, 4);
1621
1622 return YAMDI_OK;
1623 }
1624
writeBufferFLVScriptDataVariableArray(buffer_t * buffer,const char * name)1625 int writeBufferFLVScriptDataVariableArray(buffer_t *buffer, const char *name) {
1626 unsigned char type;
1627
1628 writeBufferFLVScriptDataString(buffer, name);
1629
1630 type = 3; // Variable Array
1631 bufferAppendBytes(buffer, &type, 1);
1632
1633 return YAMDI_OK;
1634 }
1635
writeBufferFLVScriptDataVariableArrayEnd(buffer_t * buffer)1636 int writeBufferFLVScriptDataVariableArrayEnd(buffer_t *buffer) {
1637 unsigned char bytes[3];
1638
1639 bytes[0] = 0;
1640 bytes[1] = 0;
1641 bytes[2] = 9;
1642
1643 bufferAppendBytes(buffer, bytes, 3);
1644
1645 return YAMDI_OK;
1646 }
1647
writeBufferFLVScriptDataValueString(buffer_t * buffer,const char * name,const char * value)1648 int writeBufferFLVScriptDataValueString(buffer_t *buffer, const char *name, const char *value) {
1649 unsigned char type;
1650
1651 if(name != NULL)
1652 writeBufferFLVScriptDataString(buffer, name);
1653
1654 type = 2; // DataString
1655 bufferAppendBytes(buffer, &type, 1);
1656
1657 writeBufferFLVScriptDataString(buffer, value);
1658
1659 return YAMDI_OK;
1660 }
1661
writeBufferFLVScriptDataValueBool(buffer_t * buffer,const char * name,int value)1662 int writeBufferFLVScriptDataValueBool(buffer_t *buffer, const char *name, int value) {
1663 unsigned char type;
1664
1665 if(name != NULL)
1666 writeBufferFLVScriptDataString(buffer, name);
1667
1668 type = 1; // Bool
1669 bufferAppendBytes(buffer, &type, 1);
1670
1671 writeBufferFLVBool(buffer, value);
1672
1673 return YAMDI_OK;
1674 }
1675
writeBufferFLVScriptDataValueDouble(buffer_t * buffer,const char * name,double value)1676 int writeBufferFLVScriptDataValueDouble(buffer_t *buffer, const char *name, double value) {
1677 unsigned char type;
1678
1679 if(name != NULL)
1680 writeBufferFLVScriptDataString(buffer, name);
1681
1682 type = 0; // Double
1683 bufferAppendBytes(buffer, &type, 1);
1684
1685 writeBufferFLVDouble(buffer, value);
1686
1687 return YAMDI_OK;
1688 }
1689
writeBufferFLVScriptDataString(buffer_t * buffer,const char * s)1690 int writeBufferFLVScriptDataString(buffer_t *buffer, const char *s) {
1691 size_t len;
1692 unsigned char bytes[2];
1693
1694 len = strlen(s);
1695
1696 // critical, if only DataString is expected?
1697 if(len > 0xffff)
1698 writeBufferFLVScriptDataLongString(buffer, s);
1699 else {
1700 bytes[0] = ((len >> 8) & 0xff);
1701 bytes[1] = ((len >> 0) & 0xff);
1702
1703 bufferAppendBytes(buffer, bytes, 2);
1704 bufferAppendString(buffer, (unsigned char*)s);
1705 }
1706
1707 return YAMDI_OK;
1708 }
1709
writeBufferFLVScriptDataLongString(buffer_t * buffer,const char * s)1710 int writeBufferFLVScriptDataLongString(buffer_t *buffer, const char *s) {
1711 size_t len;
1712 unsigned char bytes[4];
1713
1714 len = strlen(s);
1715
1716 if(len > 0xffffffff)
1717 len = 0xffffffff;
1718
1719 bytes[0] = ((len >> 24) & 0xff);
1720 bytes[1] = ((len >> 16) & 0xff);
1721 bytes[2] = ((len >> 8) & 0xff);
1722 bytes[3] = ((len >> 0) & 0xff);
1723
1724 bufferAppendBytes(buffer, bytes, 4);
1725 bufferAppendString(buffer, (unsigned char *)s);
1726
1727 return YAMDI_OK;
1728 }
1729
writeBufferFLVBool(buffer_t * buffer,int value)1730 int writeBufferFLVBool(buffer_t *buffer, int value) {
1731 unsigned char b;
1732
1733 b = (value & 1);
1734
1735 bufferAppendBytes(buffer, &b, 1);
1736
1737 return YAMDI_OK;
1738 }
1739
writeBufferFLVDouble(buffer_t * buffer,double value)1740 int writeBufferFLVDouble(buffer_t *buffer, double value) {
1741 union {
1742 unsigned char dc[8];
1743 double dd;
1744 } d;
1745 unsigned char b[8];
1746
1747 d.dd = value;
1748
1749 if(isBigEndian()) {
1750 b[0] = d.dc[0];
1751 b[1] = d.dc[1];
1752 b[2] = d.dc[2];
1753 b[3] = d.dc[3];
1754 b[4] = d.dc[4];
1755 b[5] = d.dc[5];
1756 b[6] = d.dc[6];
1757 b[7] = d.dc[7];
1758 }
1759 else {
1760 b[0] = d.dc[7];
1761 b[1] = d.dc[6];
1762 b[2] = d.dc[5];
1763 b[3] = d.dc[4];
1764 b[4] = d.dc[3];
1765 b[5] = d.dc[2];
1766 b[6] = d.dc[1];
1767 b[7] = d.dc[0];
1768 }
1769
1770 bufferAppendBytes(buffer, b, 8);
1771
1772 return YAMDI_OK;
1773 }
1774
readFLVTag(FLVTag_t * flvtag,off_t offset,FILE * fp)1775 int readFLVTag(FLVTag_t *flvtag, off_t offset, FILE *fp) {
1776 int rv;
1777 unsigned char buffer[FLV_SIZE_TAGHEADER];
1778
1779 memset(flvtag, 0, sizeof(FLVTag_t));
1780
1781 rv = fseeko(fp, offset, SEEK_SET);
1782 if(rv != 0) {
1783 #ifdef DEBUG
1784 fprintf(stderr, "[FLV] %s\n", strerror(errno));
1785 #endif
1786 return YAMDI_READ_ERROR;
1787 }
1788
1789 flvtag->offset = offset;
1790
1791 // Read the header
1792 if(readBytes(buffer, FLV_SIZE_TAGHEADER, fp) != YAMDI_OK)
1793 return YAMDI_READ_ERROR;
1794
1795 flvtag->tagtype = FLV_UI8(buffer);
1796
1797 // Assuming only known tags. Otherwise we only process the
1798 // input file up to this point. It is not possible to detect
1799 // where the next valid tag could be.
1800 switch(flvtag->tagtype) {
1801 case FLV_TAG_VIDEO:
1802 case FLV_TAG_AUDIO:
1803 case FLV_TAG_SCRIPTDATA:
1804 break;
1805 default:
1806 return YAMDI_INVALID_TAGTYPE;
1807 }
1808
1809 flvtag->datasize = (size_t)FLV_UI24(&buffer[1]);
1810 flvtag->timestamp = FLV_TIMESTAMP(&buffer[4]);
1811
1812 // Skip the data
1813 readBytes(NULL, flvtag->datasize, fp);
1814
1815 // Read the previous tag size
1816 readBytes(buffer, FLV_SIZE_PREVIOUSTAGSIZE, fp);
1817
1818 // Check the previous tag size
1819 // This is too picky. We don't need it.
1820 /*
1821 if(FLV_UI32(buffer) != (FLV_SIZE_TAGHEADER + flvtag->datasize))
1822 return YAMDI_INVALID_PREVIOUSTAGSIZE;
1823 */
1824
1825 flvtag->tagsize = FLV_SIZE_TAGHEADER + flvtag->datasize;
1826
1827 return YAMDI_OK;
1828 }
1829
readFLVTagData(unsigned char * ptr,size_t size,FLVTag_t * flvtag,FILE * stream)1830 int readFLVTagData(unsigned char *ptr, size_t size, FLVTag_t *flvtag, FILE *stream) {
1831 // check for size <= flvtag->datasize?
1832
1833 fseeko(stream, flvtag->offset + FLV_SIZE_TAGHEADER, SEEK_SET);
1834
1835 return readBytes(ptr, size, stream);
1836 }
1837
readBytes(unsigned char * ptr,size_t size,FILE * stream)1838 int readBytes(unsigned char *ptr, size_t size, FILE *stream) {
1839 size_t bytesread;
1840
1841 if(ptr == NULL) {
1842 fseeko(stream, (off_t)size, SEEK_CUR);
1843 return YAMDI_OK;
1844 }
1845
1846 bytesread = fread(ptr, 1, size, stream);
1847 if(bytesread < size)
1848 return YAMDI_READ_ERROR;
1849
1850 return YAMDI_OK;
1851 }
1852
readH264NALUnit(h264data_t * h264data,unsigned char * nalu,int length)1853 int readH264NALUnit(h264data_t * h264data, unsigned char *nalu, int length) {
1854 int i, numBytesInRBSP;
1855 int nal_unit_type;
1856 bitstream_t bitstream;
1857
1858 // See 14496-10, 7.3.1
1859 #ifdef DEBUG
1860 fprintf(stderr, "[AVC/H.264]\tNALU Header: %02x\n", nalu[0]);
1861 fprintf(stderr, "[AVC/H.264]\t\tforbidden_zero_bit = %d\n", (nalu[0] >> 7) & 0x1);
1862 fprintf(stderr, "[AVC/H.264]\t\tnal_ref_idc = %d\n", (nalu[0] >> 5) & 0x3);
1863 fprintf(stderr, "[AVC/H.264]\t\tnal_unit_type = %d\n", nalu[0] & 0x1f);
1864 fprintf(stderr, "[AVC/H.264]\tRBSP: ");
1865 for(i = 1; i < length; i++)
1866 fprintf(stderr, "%02x ", nalu[i]);
1867 fprintf(stderr, "\n");
1868 #endif
1869
1870 nal_unit_type = nalu[0] & 0x1f;
1871
1872 // We are only interested in NALUnits of type 7 (sequence parameter set, SPS)
1873 if(nal_unit_type != 7)
1874 return YAMDI_H264_USELESS_NALU;
1875
1876 bitstream.bytes = (unsigned char *)calloc(1, length - 1);
1877
1878 numBytesInRBSP = 0;
1879 for(i = 1; i < length; i++) {
1880 if(i + 2 < length && nalu[i] == 0x00 && nalu[i + 1] == 0x00 && nalu[i + 2] == 0x03) {
1881 bitstream.bytes[numBytesInRBSP++] = nalu[i];
1882 bitstream.bytes[numBytesInRBSP++] = nalu[i + 1];
1883
1884 i += 2;
1885 }
1886 else
1887 bitstream.bytes[numBytesInRBSP++] = nalu[i];
1888 }
1889
1890 bitstream.length = numBytesInRBSP;
1891 bitstream.byte = 0;
1892 bitstream.bit = 0;
1893
1894 #ifdef DEBUG
1895 fprintf(stderr, "[AVC/H.264]\tSODB: ");
1896 for(i = 0; i < bitstream.length; i++)
1897 fprintf(stderr, "%02x ", bitstream.bytes[i]);
1898 fprintf(stderr, "\n");
1899 #endif
1900
1901 readH264SPS(h264data, &bitstream);
1902
1903 free(bitstream.bytes);
1904
1905 return YAMDI_OK;
1906 }
1907
readH264SPS(h264data_t * h264data,bitstream_t * bitstream)1908 void readH264SPS(h264data_t *h264data, bitstream_t *bitstream) {
1909 int i, j;
1910 unsigned int profile_idc;
1911 unsigned int chroma_format_idc = 1, separate_color_plane_flag = 0;
1912
1913 unsigned int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1;
1914 unsigned int frame_mbs_only_flag;
1915 unsigned int frame_cropping_flag;
1916 unsigned int frame_crop_left_offset = 0, frame_crop_right_offset = 0, frame_crop_top_offset = 0, frame_crop_bottom_offset = 0;
1917
1918 unsigned int chromaArrayType;
1919 /*
1920 We need these values from SPS
1921
1922 chroma_format_idc
1923 separate_color_plane_flag
1924 pic_width_in_mbs_minus1
1925 pic_height_in_map_units_minus1
1926 frame_mbs_only_flag
1927 frame_cropping_flag
1928 frame_crop_left_offset
1929 frame_crop_right_offset
1930 frame_crop_top_offset
1931 frame_crop_bottom_offset
1932 */
1933
1934 profile_idc = readCodedU(bitstream, 8, "profile_idc");
1935 readCodedU(bitstream, 1, "constraint_set0_flag");
1936 readCodedU(bitstream, 1, "constraint_set1_flag");
1937 readCodedU(bitstream, 1, "constraint_set2_flag");
1938 readCodedU(bitstream, 1, "constraint_set3_flag");
1939 readCodedU(bitstream, 4, "reserved_zero_4bits");
1940 readCodedU(bitstream, 8, "level_idc");
1941 readCodedUE(bitstream, "seq_parameter_set_id");
1942
1943 if(
1944 profile_idc == 100 ||
1945 profile_idc == 110 ||
1946 profile_idc == 122 ||
1947 profile_idc == 244 ||
1948 profile_idc == 44 ||
1949 profile_idc == 83 ||
1950 profile_idc == 86
1951 ) {
1952 chroma_format_idc = readCodedUE(bitstream, "chroma_format_idc");
1953
1954 if(chroma_format_idc == 3)
1955 separate_color_plane_flag = readCodedU(bitstream, 1, "separate_color_plane_flag");
1956
1957 readCodedUE(bitstream, "bit_depth_luma_minus8");
1958 readCodedUE(bitstream, "bit_depth_chroma_minus8");
1959 readCodedU(bitstream, 1, "qpprime_y_zero_transform_bypass_flag");
1960
1961 unsigned int seq_scaling_matrix_present_flag = readCodedU(bitstream, 1, "seq_scaling_matrix_present_flag");
1962 if(seq_scaling_matrix_present_flag == 1) {
1963 int sizeOfScalingList, delta_scale, lastScale, nextScale;
1964
1965 int seq_scaling_matrix_count = (chroma_format_idc != 3) ? 8 : 12;
1966 unsigned int seq_scaling_list_present_flag;
1967 for(i = 0; i < seq_scaling_matrix_count; i++) {
1968 seq_scaling_list_present_flag = readCodedU(bitstream, 1, "seq_scaling_list_present_flag");
1969
1970 if(seq_scaling_list_present_flag == 1) {
1971 sizeOfScalingList = (i < 6) ? 16 : 64;
1972 lastScale = nextScale = 8;
1973 for(j = 0; j < sizeOfScalingList; j++) {
1974 if(nextScale != 0) {
1975 delta_scale = readCodedSE(bitstream, "delta_scale");
1976
1977 nextScale = (lastScale + delta_scale + 256) % 256;
1978 }
1979
1980 lastScale = (nextScale == 0) ? lastScale : nextScale;
1981 }
1982 }
1983 }
1984 }
1985 }
1986
1987 readCodedUE(bitstream, "log2_max_frame_num_minus4");
1988 unsigned int pic_order_cnt_type = readCodedUE(bitstream, "pic_order_cnt_type");
1989
1990 if(pic_order_cnt_type == 1) {
1991 readCodedU(bitstream, 1, "delta_pic_order_always_zero_flag");
1992 readCodedSE(bitstream, "offset_for_non_ref_pic");
1993 readCodedSE(bitstream, "offset_for_top_to_bottom_field");
1994
1995 unsigned int num_ref_frames_in_pic_order_cnt_cycle = readCodedUE(bitstream, "num_ref_frames_in_pic_order_cnt_cycle");
1996 for(i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++)
1997 readCodedSE(bitstream, "offset_for_ref_frame");
1998 }
1999 else if(pic_order_cnt_type == 0)
2000 readCodedUE(bitstream, "log2_max_pic_order_cnt_lsb_minus4");
2001
2002 readCodedUE(bitstream, "max_num_ref_frames");
2003 readCodedU(bitstream, 1, "gaps_in_frame_num_value_allowed_flag");
2004
2005 pic_width_in_mbs_minus1 = readCodedUE(bitstream, "pic_width_in_mbs_minus1");
2006 pic_height_in_map_units_minus1 = readCodedUE(bitstream, "pic_height_in_map_units_minus1");
2007
2008 frame_mbs_only_flag = readCodedU(bitstream, 1, "frame_mbs_only_flag");
2009 if(frame_mbs_only_flag == 0)
2010 readCodedU(bitstream, 1, "mb_adaptive_frame_field_flag");
2011
2012 readCodedU(bitstream, 1, "direct_8x8_inference_flag");
2013
2014 frame_cropping_flag = readCodedU(bitstream, 1, "frame_cropping_flag");
2015 if(frame_cropping_flag == 1) {
2016 frame_crop_left_offset = readCodedUE(bitstream, "frame_crop_left_offset");
2017 frame_crop_right_offset = readCodedUE(bitstream, "frame_crop_right_offset");
2018 frame_crop_top_offset = readCodedUE(bitstream, "frame_crop_top_offset");
2019 frame_crop_bottom_offset = readCodedUE(bitstream, "frame_crop_bottom_offset");
2020 }
2021
2022 readCodedU(bitstream, 1, "vui_parameters_present_flag");
2023
2024 // and so on ... VUI is not interesting for us. We have everything we need.
2025
2026 // Now we have enough information to compute the width and height of this video stream
2027
2028 unsigned int picWidthInMbs = (pic_width_in_mbs_minus1 + 1);
2029 unsigned int picHeightInMapUnits = (pic_height_in_map_units_minus1 + 1);
2030 unsigned int frameHeightInMbs = (2 - frame_mbs_only_flag) * picHeightInMapUnits;
2031
2032 unsigned int width = picWidthInMbs * 16;
2033 unsigned int height = frameHeightInMbs * 16;
2034
2035 #ifdef DEBUG
2036 fprintf(stderr, "[AVC/H.264] width = %u (pre crop)\n", width);
2037 fprintf(stderr, "[AVC/H.264] height = %u (pre crop)\n", height);
2038 #endif
2039
2040 // Cropping
2041
2042 int cropLeft, cropRight;
2043 int cropTop, cropBottom;
2044
2045 if(frame_cropping_flag == 1) {
2046 // See 14496-10, Table 6-1
2047 int subWidthC[4] = {1, 2, 2, 1};
2048 int subHeightC[4] = {1, 2, 1, 1};
2049
2050 unsigned int cropUnitX, cropUnitY;
2051
2052 if(separate_color_plane_flag == 0)
2053 chromaArrayType = chroma_format_idc;
2054 else
2055 chromaArrayType = 0;
2056
2057 if(chromaArrayType == 0) {
2058 cropUnitX = 1;
2059 cropUnitY = 2 - frame_mbs_only_flag;
2060 }
2061 else {
2062 cropUnitX = subWidthC[chroma_format_idc];
2063 cropUnitY = subHeightC[chroma_format_idc] * (2 - frame_mbs_only_flag);
2064 }
2065
2066 cropLeft = cropUnitX * frame_crop_left_offset;
2067 cropRight = cropUnitX * frame_crop_right_offset;
2068 cropTop = cropUnitY * frame_crop_top_offset;
2069 cropBottom = cropUnitY * frame_crop_bottom_offset;
2070 }
2071 else {
2072 cropLeft = 0;
2073 cropRight = 0;
2074 cropTop = 0;
2075 cropBottom = 0;
2076 }
2077
2078 width = width - cropLeft - cropRight;
2079 height = height - cropTop - cropBottom;
2080
2081 #ifdef DEBUG
2082 fprintf(stderr, "[AVC/H.264] width = %u\n", width);
2083 fprintf(stderr, "[AVC/H.264] height = %u\n", height);
2084 #endif
2085
2086 h264data->valid = 1;
2087 h264data->width = width;
2088 h264data->height = height;
2089
2090 return;
2091 }
2092
readCodedU(bitstream_t * bitstream,int nbits,const char * name)2093 unsigned int readCodedU(bitstream_t *bitstream, int nbits, const char *name) {
2094 // unsigned integer with n bits
2095 unsigned int bits = (unsigned int)readBits(bitstream, nbits);
2096
2097 #ifdef DEBUG
2098 if(name != NULL)
2099 fprintf(stderr, "[AVC/H.264]\t\t%s = %u\n", name, bits);
2100 #endif
2101
2102 return bits;
2103 }
2104
readCodedUE(bitstream_t * bitstream,const char * name)2105 unsigned int readCodedUE(bitstream_t *bitstream, const char *name) {
2106 // unsigned integer Exp-Golomb coded (see 14496-10, 9.1)
2107 int leadingZeroBits = -1;
2108 int bit;
2109 unsigned int codeNum = 0;
2110
2111 for(bit = 0; bit == 0; leadingZeroBits++) {
2112 bit = readBit(bitstream);
2113 if (bitstream->byte == bitstream->length) break;
2114 }
2115
2116 codeNum = ((1 << leadingZeroBits) - 1 + (unsigned int)readBits(bitstream, leadingZeroBits));
2117
2118 #ifdef DEBUG
2119 if(name != NULL)
2120 fprintf(stderr, "[AVC/H.264]\t\t%s = %u\n", name, codeNum);
2121 #endif
2122
2123 return codeNum;
2124 }
2125
readCodedSE(bitstream_t * bitstream,const char * name)2126 int readCodedSE(bitstream_t *bitstream, const char *name) {
2127 // signed integer Exp-Golomb coded (see 14496-10, 9.1 and 9.1.1)
2128 unsigned int codeNum;
2129 int codeNumSigned, sign;
2130
2131 codeNum = readCodedUE(bitstream, NULL);
2132
2133 sign = (codeNum % 2) + 1;
2134 codeNumSigned = codeNum >> 1;
2135 if(sign == 0)
2136 codeNumSigned++;
2137 else
2138 codeNumSigned *= -1;
2139
2140 #ifdef DEBUG
2141 if(name != NULL)
2142 fprintf(stderr, "[AVC/H.264]\t\t%s = %d\n", name, codeNumSigned);
2143 #endif
2144
2145 return codeNumSigned;
2146 }
2147
readBits(bitstream_t * bitstream,int nbits)2148 int readBits(bitstream_t *bitstream, int nbits) {
2149 int i, rv = 0;
2150
2151 for(i = 0; i < nbits; i++) {
2152 rv = (rv << 1);
2153 rv += readBit(bitstream);
2154 }
2155
2156 return rv;
2157 }
2158
readBit(bitstream_t * bitstream)2159 int readBit(bitstream_t *bitstream) {
2160 int bit;
2161
2162 if(bitstream->byte == bitstream->length)
2163 return 0;
2164
2165 bit = (bitstream->bytes[bitstream->byte] >> (7 - bitstream->bit)) & 0x01;
2166
2167 bitstream->bit++;
2168 if(bitstream->bit == 8) {
2169 bitstream->byte++;
2170 bitstream->bit = 0;
2171 }
2172
2173 return bit;
2174 }
2175
bufferInit(buffer_t * buffer)2176 int bufferInit(buffer_t *buffer) {
2177 if(buffer == NULL)
2178 return YAMDI_ERROR;
2179
2180 buffer->data = NULL;
2181 buffer->size = 0;
2182 buffer->used = 0;
2183
2184 return YAMDI_OK;
2185 }
2186
bufferFree(buffer_t * buffer)2187 int bufferFree(buffer_t *buffer) {
2188 if(buffer == NULL)
2189 return YAMDI_ERROR;
2190
2191 if(buffer->data != NULL) {
2192 free(buffer->data);
2193 buffer->data = NULL;
2194 }
2195
2196 return YAMDI_OK;
2197 }
2198
bufferReset(buffer_t * buffer)2199 int bufferReset(buffer_t *buffer) {
2200 if(buffer == NULL)
2201 return YAMDI_ERROR;
2202
2203 buffer->used = 0;
2204
2205 return YAMDI_OK;
2206 }
2207
bufferAppendString(buffer_t * dst,const unsigned char * string)2208 int bufferAppendString(buffer_t *dst, const unsigned char *string) {
2209 if(string == NULL)
2210 return YAMDI_OK;
2211
2212 return bufferAppendBytes(dst, string, strlen((char *)string));
2213 }
2214
bufferAppendBuffer(buffer_t * dst,buffer_t * src)2215 int bufferAppendBuffer(buffer_t *dst, buffer_t *src) {
2216 if(src == NULL)
2217 return YAMDI_OK;
2218
2219 return bufferAppendBytes(dst, src->data, src->used);
2220 }
2221
bufferAppendBytes(buffer_t * dst,const unsigned char * bytes,size_t nbytes)2222 int bufferAppendBytes(buffer_t *dst, const unsigned char *bytes, size_t nbytes) {
2223 size_t size;
2224 unsigned char *data;
2225
2226 if(dst == NULL)
2227 return YAMDI_ERROR;
2228
2229 if(bytes == NULL)
2230 return YAMDI_OK;
2231
2232 if(nbytes == 0)
2233 return YAMDI_OK;
2234
2235 // Check if we have to increase the buffer size
2236 if(dst->size < dst->used + nbytes) {
2237 // Pre-allocating some memory. Round up to the next 1024 bound
2238 size = ((dst->used + nbytes) / 1024 + 1) * 1024;
2239
2240 data = (unsigned char *)realloc(dst->data, size);
2241 if(data == NULL)
2242 return YAMDI_ERROR;
2243
2244 dst->data = data;
2245 dst->size = size;
2246 }
2247
2248 // Copy the stuff into the buffer
2249 memcpy(&dst->data[dst->used], bytes, nbytes);
2250
2251 dst->used += nbytes;
2252
2253 return YAMDI_OK;
2254 }
2255
isBigEndian(void)2256 int isBigEndian(void) {
2257 long one = 1;
2258 return !(*((char *)(&one)));
2259 }
2260
writeXMLMetadata(FILE * fp,const char * infile,const char * outfile,FLV_t * flv)2261 void writeXMLMetadata(FILE *fp, const char *infile, const char *outfile, FLV_t *flv) {
2262 size_t i;
2263
2264 fprintf(fp, "<?xml version='1.0' encoding='UTF-8'?>\n");
2265 fprintf(fp, "<fileset>\n");
2266
2267 if(outfile != NULL)
2268 fprintf(fp, "<flv name=\"%s\">\n", outfile);
2269 else
2270 fprintf(fp, "<flv name=\"%s\">\n", infile);
2271
2272 fprintf(fp, "<hasKeyframes>%s</hasKeyframes>\n", (flv->haskeyframes != 0) ? "true" : "false");
2273 fprintf(fp, "<hasVideo>%s</hasVideo>\n", (flv->hasvideo != 0) ? "true" : "false");
2274 fprintf(fp, "<hasAudio>%s</hasAudio>\n", (flv->hasaudio != 0) ? "true" : "false");
2275 fprintf(fp, "<hasMetadata>true</hasMetadata>\n");
2276 fprintf(fp, "<hasCuePoints>%s</hasCuePoints>\n", (flv->hascuepoints != 0) ? "true" : "false");
2277 fprintf(fp, "<canSeekToEnd>%s</canSeekToEnd>\n", (flv->canseektoend != 0) ? "true" : "false");
2278
2279 fprintf(fp, "<audiocodecid>%d</audiocodecid>\n", flv->audio.codecid);
2280 fprintf(fp, "<audiosamplerate>%d</audiosamplerate>\n", flv->audio.samplerate);
2281 fprintf(fp, "<audiodatarate>%d</audiodatarate>\n", (int)flv->audio.datarate);
2282 fprintf(fp, "<audiosamplesize>%d</audiosamplesize>\n", flv->audio.samplesize);
2283 fprintf(fp, "<audiodelay>%.2f</audiodelay>\n", (double)flv->audio.delay);
2284 fprintf(fp, "<stereo>%s</stereo>\n", (flv->audio.stereo != 0) ? "true" : "false");
2285
2286 fprintf(fp, "<videocodecid>%d</videocodecid>\n", flv->video.codecid);
2287 fprintf(fp, "<framerate>%.2f</framerate>\n", flv->video.framerate);
2288 fprintf(fp, "<videodatarate>%d</videodatarate>\n", (int)flv->video.datarate);
2289 fprintf(fp, "<height>%d</height>\n", (int)flv->video.height);
2290 fprintf(fp, "<width>%d</width>\n", (int)flv->video.width);
2291
2292 fprintf(fp, "<datasize>%" PRIu64 "</datasize>\n", flv->datasize);
2293 fprintf(fp, "<audiosize>%" PRIu64 "</audiosize>\n", flv->audio.size);
2294 fprintf(fp, "<videosize>%" PRIu64 "</videosize>\n", flv->video.size);
2295 fprintf(fp, "<filesize>%" PRIu64 "</filesize>\n", flv->filesize);
2296
2297 fprintf(fp, "<lasttimestamp>%.2f</lasttimestamp>\n", (double)flv->lasttimestamp / 1000.0);
2298 fprintf(fp, "<lastvideoframetimestamp>%.2f</lastvideoframetimestamp>\n", (double)flv->video.lasttimestamp / 1000.0);
2299 fprintf(fp, "<lastkeyframetimestamp>%.2f</lastkeyframetimestamp>\n", (double)flv->keyframes.lastkeyframetimestamp / 1000.0);
2300 fprintf(fp, "<lastkeyframelocation>%" PRIu64 "</lastkeyframelocation>\n", (uint64_t)flv->keyframes.lastkeyframelocation);
2301
2302 if(flv->options.xmlomitkeyframes == 0) {
2303 fprintf(fp, "<keyframes>\n");
2304 fprintf(fp, "<times>\n");
2305
2306 for(i = 0; i < flv->keyframes.nkeyframes; i++)
2307 fprintf(fp, "<value id=\"%" PRIu64 "\">%.2f</value>\n", (uint64_t)i, (double)flv->keyframes.keyframetimestamps[i] / 1000.0);
2308
2309 fprintf(fp, "</times>\n");
2310 fprintf(fp, "<filepositions>\n");
2311
2312 for(i = 0; i < flv->keyframes.nkeyframes; i++)
2313 fprintf(fp, "<value id=\"%" PRIu64 "\">%" PRIu64 "</value>\n", (uint64_t)i, (uint64_t)flv->keyframes.keyframelocations[i]);
2314
2315 fprintf(fp, "</filepositions>\n");
2316 fprintf(fp, "</keyframes>\n");
2317 }
2318
2319 fprintf(fp, "<duration>%.2f</duration>\n", (double)flv->lasttimestamp / 1000.0);
2320 fprintf(fp, "</flv>\n");
2321 fprintf(fp, "</fileset>\n");
2322
2323 return;
2324 }
2325
printUsage(void)2326 void printUsage(void) {
2327 fprintf(stderr, "NAME\n");
2328 fprintf(stderr, "\tyamdi -- Yet Another Metadata Injector for FLV\n");
2329 fprintf(stderr, "\tVersion: " YAMDI_VERSION "\n");
2330 fprintf(stderr, "\n");
2331
2332 fprintf(stderr, "SYNOPSIS\n");
2333 fprintf(stderr, "\tyamdi -i input file [-x xml file | -o output file [-x xml file]]\n");
2334 fprintf(stderr, "\t [-t temporary file] [-c creator] [-a interval] [-skMXw] [-h]\n");
2335 fprintf(stderr, "\n");
2336
2337 fprintf(stderr, "DESCRIPTION\n");
2338 fprintf(stderr, "\tyamdi is a metadata injector for FLV files.\n");
2339 fprintf(stderr, "\n");
2340 fprintf(stderr, "\tOptions:\n");
2341 fprintf(stderr, "\n");
2342 fprintf(stderr, "\t-i\tThe source FLV file. If the file name is '-' the input\n");
2343 fprintf(stderr, "\t\tfile will be read from stdin. Use the -t option to specify\n");
2344 fprintf(stderr, "\t\ta temporary file.\n");
2345 fprintf(stderr, "\n");
2346 fprintf(stderr, "\t-o\tThe resulting FLV file with the metatags. If the file\n");
2347 fprintf(stderr, "\t\tname is '-' the output will be written to stdout.\n");
2348 fprintf(stderr, "\n");
2349 fprintf(stderr, "\t-x\tAn XML file with the resulting metadata information. If the\n");
2350 fprintf(stderr, "\t\toutput file is ommited, only metadata will be generated.\n");
2351 fprintf(stderr, "\n");
2352 fprintf(stderr, "\t-t\tA temporary file to store the source FLV file in if the\n");
2353 fprintf(stderr, "\t\tinput file is read from stdin.\n");
2354 fprintf(stderr, "\n");
2355 fprintf(stderr, "\t-c\tA string that will be written into the creator tag.\n");
2356 fprintf(stderr, "\n");
2357 fprintf(stderr, "\t-s, -l\tAdd the onLastSecond event.\n");
2358 fprintf(stderr, "\t\tThe -l option is deprecated and will be removed in a future\n");
2359 fprintf(stderr, "\t\tversion.\n");
2360 fprintf(stderr, "\n");
2361 fprintf(stderr, "\t-k\tAdd the onLastKeyframe event.\n");
2362 fprintf(stderr, "\n");
2363 /*
2364 fprintf(stderr, "\t-m\tLeave the existing metadata intact.\n");
2365 fprintf(stderr, "\t\tMetadata that yamdi does not add is left untouched, e.g. onCuepoint.\n");
2366 fprintf(stderr, "\n");
2367 */
2368 fprintf(stderr, "\t-M\tStrip all metadata from the FLV. The -s and -k options will\n");
2369 fprintf(stderr, "\t\tbe ignored.\n");
2370 fprintf(stderr, "\n");
2371 fprintf(stderr, "\t-X\tOmit the keyframes tag in the XML output.\n");
2372 fprintf(stderr, "\n");
2373 fprintf(stderr, "\t-a\tTime in milliseconds between keyframes if there is only audio.\n");
2374 fprintf(stderr, "\t\tThis option will be ignored if there is a video stream. No\n");
2375 fprintf(stderr, "\t\tkeyframes will be added if this option is omitted.\n");
2376 fprintf(stderr, "\n");
2377 fprintf(stderr, "\t-w\tReplace the input file with the output file. -i and -o are\n");
2378 fprintf(stderr, "\t\trequired to be different files otherwise this option will be\n");
2379 fprintf(stderr, "\t\tignored.\n");
2380 fprintf(stderr, "\n");
2381 fprintf(stderr, "\t-h\tThis description.\n");
2382 fprintf(stderr, "\n");
2383
2384 fprintf(stderr, "COPYRIGHT\n");
2385 fprintf(stderr, "\t(c) 2007+ Ingo Oppermann\n");
2386 fprintf(stderr, "\n");
2387 return;
2388 }
2389