1 /***************************************************************************
2 \file ADM_flv.cpp
3 \author (C) 2007 by mean email : fixounet@free.fr
4
5
6 Not sure if the timestamp is PTS or DTS (...)
7
8 See lavformat/flv[dec/env].c for detail
9 ***************************************************************************/
10
11 /***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20 #include "ADM_default.h"
21 #include "fourcc.h"
22 #include "DIA_coreToolkit.h"
23 #include "ADM_aacinfo.h"
24 #include "ADM_flv.h"
25
26 #include <math.h>
27 #include <algorithm>
28 #include <vector>
29 #if 0
30 #define aprintf printf
31 #else
32 #define aprintf(...) {}
33 #endif
34 // Borrowed from lavformt/flv.h
35 #include "libavformat/flv.h"
36 #include "libavutil/intfloat.h"
37 // Borrowed from lavformt/flv.h
38 uint32_t ADM_UsecFromFps1000(uint32_t fps1000);
39
40 #ifdef USE_BUFFERED_IO
Skip(uint32_t len)41 uint8_t flvHeader::Skip(uint32_t len)
42 {
43 return parser->forward(len);
44 }
read(uint32_t len,uint8_t * where)45 uint8_t flvHeader::read(uint32_t len, uint8_t *where)
46 {
47 if(len==parser->read32(len,where))
48 return 1;
49 return 0;
50 }
read8(void)51 uint8_t flvHeader::read8(void)
52 {
53 return parser->read8i();
54 }
read16(void)55 uint32_t flvHeader::read16(void)
56 {
57 uint32_t r=parser->read16i();
58 return r;
59 }
read24(void)60 uint32_t flvHeader::read24(void)
61 {
62 uint32_t r=parser->read16i();
63 return (r<<8)+(uint32_t)parser->read8i();
64 }
read32(void)65 uint32_t flvHeader::read32(void)
66 {
67 return parser->read32i();
68 }
69 #else
70 /**
71 \fn Skip
72 \brief Skip some bytes from the file
73 */
Skip(uint32_t len)74 uint8_t flvHeader::Skip(uint32_t len)
75 {
76 fseeko(_fd,len,SEEK_CUR);
77 return 1;
78 }
79 /**
80 \fn read
81 \brief read the given size from file
82 */
83
read(uint32_t len,uint8_t * where)84 uint8_t flvHeader::read(uint32_t len, uint8_t *where)
85 {
86
87 uint32_t got=fread(where,1,len,_fd);
88 if(len!=got)
89 {
90 printf("[FLV] Read error : asked %u, got %u\n",len,got);
91 return 0;
92 }
93 return 1;
94 }
95 //__________________________________________________________
read8(void)96 uint8_t flvHeader::read8(void)
97 {
98 uint8_t r;
99 aprintf("[Read]At 0x%x ",ftello(_fd));
100 fread(&r,1,1,_fd);
101 aprintf("uint8_t =%d ",r);
102 return r;
103 }
read16(void)104 uint32_t flvHeader::read16(void)
105 {
106 uint8_t r[2];
107 aprintf("[Read]At 0x%x ",ftello(_fd));
108 fread(r,2,1,_fd);
109 aprintf("uint16_t =%d ",(r[0]<<8)+r[1]);
110 return (r[0]<<8)+r[1];
111 }
read24(void)112 uint32_t flvHeader::read24(void)
113 {
114 uint8_t r[3];
115 aprintf("[Read]At 0x%x ",ftello(_fd));
116 fread(r,3,1,_fd);
117 aprintf("uint24_t =%d ",(r[0]<<16)+(r[1]<<8)+r[2]);
118 return (r[0]<<16)+(r[1]<<8)+r[2];
119 }
read32(void)120 uint32_t flvHeader::read32(void)
121 {
122 uint8_t r[4];
123 aprintf("[Read]At 0x%x ",ftello(_fd));
124 fread(r,4,1,_fd);
125 aprintf("uint32_t =%d ",(r[0]<<24)+(r[1]<<16)+(r[2]<<8)+r[3]);
126 return (r[0]<<24)+(r[1]<<16)+(r[2]<<8)+r[3];
127 }
128 #endif
129 /**
130 \fn readFlvString
131 \brief read pascal like string
132 */
133 #define FLV_MAX_STRING 255
readFlvString(void)134 char *flvHeader::readFlvString(void)
135 {
136 static uint8_t stringz[FLV_MAX_STRING+1];
137 int size=read16();
138 if(size>FLV_MAX_STRING)
139 {
140 int pre=FLV_MAX_STRING;
141 read(pre,stringz);
142 ADM_warning("String way too large :%d\n",size);
143 mixDump(stringz,pre);
144 stringz[0]='X';
145 stringz[1]='X';
146 stringz[2]=0;
147 stringz[FLV_MAX_STRING]=0;
148 Skip(size-FLV_MAX_STRING);
149 return (char *)stringz;
150 }
151 read(size,stringz);
152 stringz[size]=0;
153 return (char *)stringz;
154 }
155
timeBaseFromFps1000(uint32_t v,uint32_t * num,uint32_t * den)156 static bool timeBaseFromFps1000(uint32_t v, uint32_t *num, uint32_t *den)
157 {
158 if(!v || !num || !den)
159 return false;
160 switch(v)
161 {
162 case 23976:
163 *num=1001;
164 *den=24000;
165 break;
166 case 29970:
167 *num=1001;
168 *den=30000;
169 break;
170 case 59940:
171 *num=1001;
172 *den=60000;
173 break;
174 default:
175 *num=1000;
176 *den=v;
177 break;
178 }
179 return true;
180 }
181
182 /**
183 \fn setProperties
184 \brief get a couple key/value and use it if needed...
185 */
setProperties(const char * name,float value)186 void flvHeader::setProperties(const char *name,float value)
187 {
188 if(!strcmp(name,"framerate"))
189 {
190 value*=1000.;
191 value+=0.49;
192 uint32_t v=(uint32_t)value;
193 uint32_t num,den;
194 if(timeBaseFromFps1000(v,&num,&den))
195 {
196 _videostream.dwScale=num;
197 _videostream.dwRate=den;
198 _mainaviheader.dwMicroSecPerFrame=0; // conformance with constant fps will be probed later
199 }
200 return;
201 }
202 if(!strcmp(name,"width"))
203 {
204 metaWidth=(uint32_t)value;
205 }
206 if(!strcmp(name,"height"))
207 {
208 metaHeight=(uint32_t)value;
209 }
210 if(!strcmp(name,"frameWidth"))
211 {
212 metaFrameWidth=(uint32_t)value;
213 }
214 if(!strcmp(name,"frameHeight"))
215 {
216 metaFrameHeight=(uint32_t)value;
217 }
218
219 }
220 #define Nest() {for(int xxx=0;xxx<nesting;xxx++) printf("\t");}
221 /**
222 \fn parseOneMeta
223 */
parseOneMeta(const char * stri,uint64_t endPos,bool & end)224 bool flvHeader::parseOneMeta(const char *stri,uint64_t endPos,bool &end)
225 {
226 static int nesting=0;
227 nesting++;
228 int type=read8();
229 Nest();
230 printf("\n>> type :%d ",type);
231 #ifdef USE_BUFFERED_IO
232 uint64_t pos=0;
233 parser->getpos(&pos);
234 aprintf("nesting = %d, at %d, : ,end=%d",nesting,pos,endPos);
235 switch(type)
236 {
237 case AMF_DATA_TYPE_NULL:
238 parser->setpos(endPos);
239 break;
240 case AMF_DATA_TYPE_OBJECT_END:
241 Nest(); printf("** Object end**.\n");
242 parser->getpos(&pos);
243 if(pos>=endPos-4)
244 parser->setpos(endPos);
245 end=true;
246 nesting--;
247 break;
248 case AMF_DATA_TYPE_OBJECT:
249 {
250 printf("\n");
251 bool myEnd=false;
252 parser->getpos(&pos);
253 while(pos<endPos-4 && myEnd==false)
254 {
255 Nest();
256 parser->getpos(&pos);
257 aprintf("Pos = %d, end=%d (object)\n",pos,endPos-4);
258 char *o=readFlvString();
259 Nest(); printf("\t ** Object**:%s",o);
260 if(false==parseOneMeta(o,endPos,myEnd)) return false;
261 /*
262 char objType=read8();
263 printf("-->%d",objType);
264 if(objType!=10)
265 {
266 ADM_warning("type is not strict array\n");
267 goto xxer;
268 }
269 int count=read32();
270 printf(", count=%d\n",count);
271 for(int i=0;i<count;i++)
272 {
273 if(false==parseOneMeta(endPos)) return false;
274 }
275 break;
276 */
277 parser->getpos(&pos);
278 }
279 break;
280 }
281 case AMF_DATA_TYPE_ARRAY:
282 {
283 uint32_t len=read32();
284 Nest();printf("\n**[FLV] Array : %" PRIu32" entries**\n",len);
285 bool theend;
286 parser->getpos(&pos);
287 for(int i=0;i<len && pos<endPos-4;i++)
288 if(false==parseOneMeta("",endPos,theend)) return false;
289 Nest();printf("\n");
290 break;
291 }
292 case AMF_DATA_TYPE_DATE: Skip(8+2);break;
293 case AMF_DATA_TYPE_NUMBER:
294 {
295 float val;
296 uint64_t hi,lo;
297 hi=read32();lo=read32();
298 hi=(hi<<32)+lo;
299 val=(float)av_int2double(hi);
300 printf("->%f",val);
301 setProperties(stri,val);
302 break;
303 }
304 case AMF_DATA_TYPE_STRING:
305 {
306 int r=read16();
307 #if 1
308 Nest();printf("<");
309 for(int i=0;i<r;i++)
310 {
311 printf("%c",read8());
312 }
313 printf(">");
314 #else
315 Skip(r);
316 #endif
317 }
318 break;
319 case AMF_DATA_TYPE_BOOL: read8();break;
320 case AMF_DATA_TYPE_MIXEDARRAY:
321 {
322 read32();
323 parser->getpos(&pos);
324 while(pos<endPos-4)
325 {
326 char *o=readFlvString();
327 bool theend;
328 if(!o) break;
329 Nest();printf("** MixedArray:%s **",o);
330 if(false==parseOneMeta(o,endPos,theend)) return false;
331 parser->getpos(&pos);
332 }
333 if(read8()!=AMF_END_OF_OBJECT) return false;
334 }
335 break;
336 default: printf("Unknown type=%d\n",type);ADM_assert(0);
337 }
338 printf("\n");
339
340 nesting--;
341 return true;
342 #else
343 aprintf("nesting = %d, at %d, : ,end=%d",nesting,ftello(_fd),endPos);
344 switch(type)
345 {
346 case AMF_DATA_TYPE_NULL:
347 fseek(_fd,endPos,SEEK_SET);
348 break;
349 case AMF_DATA_TYPE_OBJECT_END:
350 Nest(); printf("** Object end**.\n");
351 if(ftello(_fd)>=endPos-4)
352 {
353 fseek(_fd,endPos,SEEK_SET);
354 }
355 end=true;
356 nesting--;
357 break;
358 case AMF_DATA_TYPE_OBJECT:
359 {
360 printf("\n");
361 bool myEnd=false;
362 while(ftello(_fd)<endPos-4 && myEnd==false)
363 {
364 Nest();aprintf("Pos = %d, end=%d (object)\n",ftello(_fd),endPos-4);
365 char *o=readFlvString();
366 Nest(); printf("\t ** Object**:%s",o);
367 if(false==parseOneMeta(o,endPos,myEnd)) return false;
368 /*
369 char objType=read8();
370 printf("-->%d",objType);
371 if(objType!=10)
372 {
373 ADM_warning("type is not strict array\n");
374 goto xxer;
375 }
376 int count=read32();
377 printf(", count=%d\n",count);
378 for(int i=0;i<count;i++)
379 {
380 if(false==parseOneMeta(endPos)) return false;
381 }
382 break;
383 */
384
385 }
386 break;
387
388 }
389 case AMF_DATA_TYPE_ARRAY:
390 {
391 uint32_t len=read32();
392 Nest();printf("\n**[FLV] Array : %" PRIu32" entries**\n",len);
393 bool theend;
394 for(int i=0;i<len && ftello(_fd)<endPos-4;i++)
395 if(false==parseOneMeta("",endPos,theend)) return false;
396 Nest();printf("\n");
397 break;
398 }
399 case AMF_DATA_TYPE_DATE: Skip(8+2);break;
400 case AMF_DATA_TYPE_NUMBER:
401 {
402 float val;
403 uint64_t hi,lo;
404 hi=read32();lo=read32();
405 hi=(hi<<32)+lo;
406 val=(float)av_int2double(hi);
407 printf("->%f",val);
408 setProperties(stri,val);
409 }
410 ;break;
411 case AMF_DATA_TYPE_STRING:
412 {
413 int r=read16();
414
415
416 #if 1
417 Nest();printf("<");
418 for(int i=0;i<r;i++)
419 {
420 printf("%c",read8());
421 }
422 printf(">");
423 #else
424 Skip(r);
425 #endif
426 }
427
428 break;
429 case AMF_DATA_TYPE_BOOL: read8();break;
430 case AMF_DATA_TYPE_MIXEDARRAY:
431 {
432 read32();
433 while(ftello(_fd)<endPos-4)
434 {
435 char *o=readFlvString();
436 bool theend;
437 if(!o) break;
438 Nest();printf("** MixedArray:%s **",o);
439 if(false==parseOneMeta(o,endPos,theend)) return false;
440
441 }
442 if(read8()!=AMF_END_OF_OBJECT) return false;
443
444 }
445 break;
446 default : printf("Unknown type=%d\n",type);ADM_assert(0);
447 }
448 printf("\n");
449
450 nesting--;
451 return true;
452 #endif
453 xxer:
454 nesting --;
455 return false;
456 }
457 /**
458 \fn parseMetaData
459 \brief
460 */
parseMetaData(uint32_t remaining)461 uint8_t flvHeader::parseMetaData(uint32_t remaining)
462 {
463 #ifdef USE_BUFFERED_IO
464 uint64_t pos=0;
465 parser->getpos(&pos);
466 pos+=remaining;
467 ADM_assert(!(pos&0xffffffff00000000));
468 uint32_t endPos=pos;
469 #else
470 uint32_t endPos=ftello(_fd)+remaining;
471 #endif
472 // Check the first one is onMetaData...
473 uint8_t type=read8();
474 char *z;
475 if(type!=AMF_DATA_TYPE_STRING) // String!
476 goto endit;
477 z=readFlvString();
478 printf("[FlashString] %s\n",z);
479 if(z && strncmp(z,"onMetaData",10)) goto endit;
480 // Normally the next one is mixed array
481 #ifdef USE_BUFFERED_IO
482 parser->getpos(&pos);
483 while(pos<endPos-4)
484 {
485 bool theend;
486 printf("\n----------------------- Parse---------------------\n");
487 if(false==parseOneMeta("meta",endPos,theend)) goto endit;
488 parser->getpos(&pos);
489 }
490
491 #else
492 while(ftello(_fd)<endPos-4)
493 {
494 bool theend;
495 printf("\n----------------------- Parse---------------------\n");
496 if(false==parseOneMeta("meta",endPos,theend)) goto endit;
497 }
498 #endif
499 endit:
500 #ifdef USE_BUFFERED_IO
501 parser->setpos(endPos);
502 #else
503 fseeko(_fd,endPos,SEEK_SET);
504 #endif
505 updateDimensionWithMeta(videoCodec);
506 return 1;
507 }
508 /**
509 \fn searchMinimum
510 \brief returns minimum time in us between 2 video frames
511
512 */
searchMinimum(void)513 uint32_t flvHeader::searchMinimum(void)
514 {
515 uint32_t delta=0xF000000;
516 for(int i=0;i<videoTrack->_nbIndex-1;i++)
517 {
518 flvIndex *x=&(videoTrack->_index[i]);
519 if((x[1].dtsUs-x[0].dtsUs)<delta) delta=x[1].dtsUs-x[0].dtsUs;
520
521 }
522 return delta;
523 }
524 /**
525 \fn extraHeader
526 \brief if returns true means we must skip the remainder
527 */
extraHeader(flvTrak * trk,uint32_t * remain,bool have_cts,int32_t * cts)528 bool flvHeader::extraHeader(flvTrak *trk,uint32_t *remain,bool have_cts,int32_t *cts)
529 {
530 int type=read8();
531 int r=*remain;
532 r--;
533 if(have_cts)
534 {
535 uint32_t c=read24();
536 *cts=(c+0xff800000)^0xff800000;
537 //printf("Type :%d\n",type);
538 r-=3;
539 }
540 if(!type)
541 { // Grab extra data
542 if(trk->extraData)
543 {
544 Skip(r);
545 r=0;
546 }
547 else
548 {
549 ADM_info("[FLV] found some extradata %" PRIu32"\n",r);
550 trk->extraData=new uint8_t[r];
551 trk->extraDataLen=r;
552 read(r,trk->extraData);
553 mixDump(trk->extraData,r);
554 r=0;
555 }
556 *remain=r;
557 return true;
558 }
559 *remain=r;
560 return false;
561 }
562 /**
563 \fn open
564 \brief open the flv file, gather infos and build index(es).
565 */
566
open(const char * name)567 uint8_t flvHeader::open(const char *name)
568 {
569 uint32_t prevLen, type, size, dts;
570 uint64_t pos=0;
571 bool firstVideo=true;
572 bool tryProbedAvgFps=false;
573 int32_t firstCts=0;
574 _isvideopresent=0;
575 _isaudiopresent=0;
576 audioTrack=NULL;
577 videoTrack=NULL;
578 _videostream.dwRate=0;
579 _videostream.dwScale=1000;
580 _filename=ADM_strdup(name);
581 #ifdef USE_BUFFERED_IO
582 parser=new fileParser(CACHE_SIZE);
583 ADM_assert(parser);
584 int append=0;
585 if(!parser->open(name,&append))
586 {
587 ADM_error("[flv] Cannot open %s\n",name);
588 return 0;
589 }
590 uint64_t fileSize=parser->getSize();
591 #else
592 _fd=ADM_fopen(name,"rb");
593 if(!_fd)
594 {
595 printf("[FLV] Cannot open %s\n",name);
596 return 0;
597 }
598 // Get size
599 uint64_t fileSize=0;
600 fseeko(_fd,0,SEEK_END);
601 fileSize=ftello(_fd);
602 fseeko(_fd,0,SEEK_SET);
603 printf("[FLV] file size :%" PRIu64 " bytes\n",fileSize);
604 #endif
605 // It must begin by F L V 01
606 uint8_t four[4];
607
608 read(4,four);
609 if(four[0]!='F' || four[1]!='L' || four[2]!='V')
610 {
611 printf("[FLV] Not a flv file %s\n",name);
612 return 0;
613 }
614 // Next one is flags
615 uint32_t flags=read8();
616 if(flags & 1) // VIDEO
617 {
618 _isvideopresent=1;
619 printf("[FLV] Video flag\n");
620 }else
621 {
622 GUI_Info_HIG(ADM_LOG_INFO,QT_TRANSLATE_NOOP("flvdemuxer","Warning"),QT_TRANSLATE_NOOP("flvdemuxer","This FLV file says it has no video.\nI will assume it has and try to continue"));
623 _isvideopresent=1;
624 }
625 if(flags & 4) // Audio
626 {
627 _isaudiopresent=1;
628 printf("[FLV] Audio flag\n");
629 }
630
631
632 // Skip header
633 uint32_t skip=read32();
634 #ifdef USE_BUFFERED_IO
635 parser->setpos(skip);
636 printf("[flv] Skipping %u header bytes\n",skip);
637 parser->getpos(&pos);
638 #else
639 fseeko(_fd,skip,SEEK_SET);
640 printf("[FLV] Skipping %u header bytes\n",skip);
641 pos=ftello(_fd);
642 #endif
643 printf("pos:%" PRIu64 "/%" PRIu64 "\n",pos,fileSize);
644 // Create our video index
645 videoTrack=new flvTrak(50);
646 if(_isaudiopresent)
647 audioTrack=new flvTrak(50);
648 else
649 audioTrack=NULL;
650 // Loop
651 while(pos<fileSize-14)
652 {
653 int32_t cts=0;
654 uint32_t pts=0xffffffff;
655 #ifdef USE_BUFFERED_IO
656 parser->getpos(&pos);
657 #else
658 pos=ftello(_fd);
659 #endif
660 prevLen=read32();
661 type=read8();
662 size=read24();
663 dts=read24();
664 dts|=((uint32_t)read8())<<24;
665 read24(); // StreamID, always 0
666 aprintf("--------\n");
667 aprintf("prevLen=%d\n",(int)prevLen);
668 aprintf("type =%d\n",(int)type);
669 aprintf("size =%d\n",(int)size);
670 aprintf("dts =%d\n",(int)dts);
671 if(!size)
672 {
673 #ifdef USE_BUFFERED_IO
674 parser->getpos(&pos);
675 #endif
676 continue;
677 }
678 uint32_t remaining=size;
679 //printf("[FLV] At %08" PRIu64 " found type %x size %u pts%u\n",pos,type,size,dts);
680 switch(type)
681 {
682 case FLV_TAG_TYPE_AUDIO:
683 {
684 aprintf("** Audio **\n");
685 if(!_isaudiopresent)
686 {
687 audioTrack=new flvTrak(50);
688 _isaudiopresent=1; /* Damn lying headers...*/
689 };
690 uint8_t flags=read8();
691 int of=1+4+3+3+1+4;
692 remaining--;
693 int format=flags>>4;
694 int fq=(flags>>2)&3;
695 int bps=(flags>>1) & 1;
696 int channel=(flags) & 1;
697 if(!audioTrack->_nbIndex) // first frame..
698 {
699 setAudioHeader(format,fq,bps,channel);
700 }
701 if(format==10)
702 {
703 if(extraHeader(audioTrack,&remaining,false,&cts)) continue;
704 }
705 #ifdef USE_BUFFERED_IO
706 if(remaining)
707 {
708 parser->getpos(&pos);
709 insertAudio(pos,remaining,dts);
710 }
711 #else
712 if(remaining)
713 insertAudio(ftello(_fd),remaining,dts);
714 #endif
715 }
716 break;
717 case FLV_TAG_TYPE_META:
718 aprintf("** Meta **\n");
719 parseMetaData(remaining);
720 remaining=0;
721 break;
722 case FLV_TAG_TYPE_VIDEO:
723 {
724 int of=1+4+3+3+1+4;
725 aprintf("** Video **\n");
726 uint8_t flags=read8();
727 remaining--;
728 int frameType=flags>>4;
729
730 videoCodec=(flags)&0xf;
731
732 if(videoCodec==FLV_CODECID_VP6 || videoCodec==FLV_CODECID_VP6A)
733 {
734 read8(); // 1 byte of extraData
735 remaining--;
736 of++;
737 }
738
739 if(firstVideo==true) // first frame..
740 {
741 if(!setVideoHeader(videoCodec,&remaining)) return 0;
742 firstVideo=false;
743 }
744 if(videoCodec==FLV_CODECID_H264)
745 {
746 if(true==extraHeader(videoTrack,&remaining,true,&cts))
747 {
748 ADM_info("Retrieving H.264 info...\n");
749 nalsize=ADM_getNalSizeH264(videoTrack->extraData,videoTrack->extraDataLen);
750 spsinfo=new ADM_SPSInfo;
751 if(false==extractSPSInfo_mp4Header(videoTrack->extraData,videoTrack->extraDataLen,spsinfo))
752 {
753 ADM_warning("Cannot decode SPS\n");
754 delete spsinfo;
755 spsinfo=NULL;
756 }
757 continue;
758 }
759 if(!videoTrack->_nbIndex) firstCts=cts;
760 if(!bFramesPresent && firstCts!=cts)
761 bFramesPresent=true;
762 int64_t sum=cts+dts;
763 if(sum<0) pts=0xffffffff;
764 else pts=dts+(int32_t)cts;
765
766 }
767 #ifdef USE_BUFFERED_IO
768 if(remaining)
769 {
770 bool check=bFramesPresent;
771 parser->getpos(&pos);
772 insertVideo(pos,remaining,frameType,dts,pts);
773 if(!ptsInvalid && bFramesPresent!=check)
774 {
775 ADM_warning("Invalid PTS detected at frame %d\n",(int)videoTrack->_nbIndex-1);
776 ptsInvalid=true;
777 }
778 pos+=remaining;
779 remaining=0;
780 parser->setpos(pos);
781 }
782 #else
783 if(remaining)
784 {
785 bool check=bFramesPresent;
786 pos=ftello(_fd);
787 insertVideo(pos,remaining,frameType,dts,pts);
788 if(!ptsInvalid && bFramesPresent!=check)
789 {
790 ADM_warning("Invalid PTS detected at frame %d\n",(int)videoTrack->_nbIndex-1);
791 ptsInvalid=true;
792 }
793 pos+=remaining;
794 remaining=0;
795 fseeko(_fd,pos,SEEK_SET);
796 }
797 #endif
798 }
799 break;
800 default: printf("[FLV]At 0x%" PRIx64 ", unhandled type %u\n",pos,type);
801 }
802 Skip(remaining);
803 } // while
804
805 // Udpate frame count etc..
806 ADM_info("[FLV] Found %u frames\n",videoTrack->_nbIndex);
807
808 if(videoCodec==FLV_CODECID_H264 && spsinfo && spsinfo->width && spsinfo->height)
809 {
810 ADM_info("Setting width and height to values obtained from codec\n");
811 ADM_info("W %d\n",spsinfo->width);
812 ADM_info("H %d\n",spsinfo->height);
813 metaWidth=spsinfo->width;
814 metaHeight=spsinfo->height;
815 updateDimensionWithMeta(FLV_CODECID_H264);
816 }
817
818 _videostream.dwLength= _mainaviheader.dwTotalFrames=videoTrack->_nbIndex;
819
820 // Can we force a constant frame rate based on metadata?
821 uint64_t delay=(firstCts>0)? 1000LL*firstCts : 0;
822 if(false==enforceConstantFps(_videostream.dwScale,_videostream.dwRate,delay,bFramesPresent))
823 {
824 ADM_info("Cannot force constant frame rate for timebase %u / %u\n",_videostream.dwScale,_videostream.dwRate);
825 tryProbedAvgFps=true;
826 }
827 // Compute average fps
828 float f=_videostream.dwLength;
829 uint64_t duration=videoTrack->_index[videoTrack->_nbIndex-1].dtsUs;
830 duration+=(uint64_t)((double)duration/(double)f+0.49);
831
832 if(duration)
833 f=1000.*1000.*1000.*f/duration;
834 else f=25000;
835 // If it was available from the metadata, use the one from metadata
836 if(tryProbedAvgFps)
837 {
838 float d=searchMinimum();
839 printf("[FLV] minimum delta :%d\n",(uint32_t)d);
840 d=1/d;
841 d*=1000*1000*1000;
842
843 uint32_t avg=(uint32_t)floor(f);
844 uint32_t max=(uint32_t)floor(d);
845 if(max<2) max=2; // 500 fps max
846 printf("[FLV] Avg fps :%d, max fps :%d\n",avg,max);
847 // Can we use the probed average fps for timebase?
848 bool skip=false;
849 uint32_t num,den;
850 if(timeBaseFromFps1000(avg,&num,&den))
851 {
852 skip = (num==_videostream.dwScale) && (den==_videostream.dwRate);
853 if(!skip)
854 {
855 _videostream.dwScale=num;
856 _videostream.dwRate=den;
857 _mainaviheader.dwMicroSecPerFrame=0; // assume a perfectly regular stream
858 }
859 }
860 if(skip || false==enforceConstantFps(_videostream.dwScale,_videostream.dwRate,delay,bFramesPresent))
861 {
862 if(skip)
863 ADM_info("Average fps matches metadata, skipping the check.\n");
864 else
865 ADM_info("Cannot force CFR based on avg fps for timebase %u / %u\n",_videostream.dwScale,_videostream.dwRate);
866 // Can we use at least a timebase derived from max fps?
867 if(timeBaseFromFps1000(max,&num,&den))
868 {
869 _videostream.dwScale=num;
870 _videostream.dwRate=den;
871 }
872 _mainaviheader.dwMicroSecPerFrame=ADM_UsecFromFps1000(avg); // should be safe now
873 if(false==checkTimeBase(_videostream.dwScale,_videostream.dwRate))
874 {
875 ADM_info("Cannot use timebase %u / %u from max fps, falling back to 1 / 1000 equivalent.\n",_videostream.dwScale,_videostream.dwRate);
876 {
877 _videostream.dwRate=16000; // lavf doesn't like low clocks like e.g. 1000 // FIXME
878 _videostream.dwScale=16;
879 }
880 }
881 }
882 }
883
884 ADM_info("[FLV] Duration: %" PRIu64" ms, time base: %u / %u\n",duration/1000,
885 _videostream.dwScale,_videostream.dwRate);
886
887 //
888 _videostream.fccType=fourCC::get((uint8_t *)"vids");
889 _video_bih.biBitCount=24;
890 _videostream.dwInitialFrames= 0;
891 _videostream.dwStart= 0;
892 videoTrack->_index[0].flags &= ~AVI_B_FRAME;
893 videoTrack->_index[0].flags |= AVI_KEY_FRAME;
894
895 // if it is AAC and we have extradata...
896 if(_isaudiopresent && wavHeader.encoding && audioTrack->extraDataLen>=2)
897 {
898 AacAudioInfo info;
899 // Check frequency..
900 if(ADM_getAacInfoFromConfig(audioTrack->extraDataLen,audioTrack->extraData,info))
901 {
902 ADM_info("AAC detected with fq=%d, sbr=%d\n",info.frequency,info.sbr);
903 wavHeader.frequency=info.frequency;
904 }
905 }
906
907 // audio track
908 if(_isaudiopresent)
909 {
910 _access=new ADM_flvAccess(name,audioTrack);
911 _audioStream=ADM_audioCreateStream(&wavHeader,_access);
912 }
913 else
914 {
915 _audioStream = NULL;
916 _access=NULL;
917 }
918 #ifdef USE_BUFFERED_IO
919 // Too big cache may be detrimental for keyframe-based navigation, reduce cache size
920 parser->setBufferSize(DMX_BUFFER);
921 #endif
922 printf("[FLV]FLV successfully read\n");
923
924 return 1;
925 }
926 /**
927 \fn getVideoDuration
928 \brief Returns duration of video in us
929 */
getVideoDuration(void)930 uint64_t flvHeader::getVideoDuration(void)
931 {
932 uint64_t pts=videoTrack->_index[videoTrack->_nbIndex-1].ptsUs;
933 if(pts==ADM_NO_PTS) pts=videoTrack->_index[videoTrack->_nbIndex-1].dtsUs;
934 pts+=frameToUs(1);
935 return pts;
936 }
937
938 /**
939 \fn setVideoHeader
940 \brief Set codec and stuff
941 */
updateDimensionWithMeta(uint32_t codec)942 bool flvHeader::updateDimensionWithMeta(uint32_t codec)
943 {
944 if(codec==0xFFFF) return false;
945 ADM_info("We got metadata : %d x %d\n",(int)metaWidth,(int)metaHeight,(int)codec);
946 if(metaFrameWidth) metaWidth=metaFrameWidth;
947 if(metaFrameHeight) metaHeight=metaFrameHeight;
948 if( metaWidth && metaHeight )
949 {
950 switch(codec)
951 {
952 case FLV_CODECID_VP6A:
953 case FLV_CODECID_H264:
954 case FLV_CODECID_VP6:
955
956 _video_bih.biHeight=_mainaviheader.dwHeight=metaHeight ;
957 _video_bih.biWidth=_mainaviheader.dwWidth=metaWidth;
958 break;
959 default:break;
960 }
961 }
962 return true;
963 }
964 /**
965 \fn setVideoHeader
966 */
setVideoHeader(uint8_t codec,uint32_t * remaining)967 uint8_t flvHeader::setVideoHeader(uint8_t codec,uint32_t *remaining)
968 {
969 printf("[FLV] Video Codec:%u\n",codec);
970
971 _video_bih.biWidth=_mainaviheader.dwWidth=320;
972 _video_bih.biHeight=_mainaviheader.dwHeight=240;
973 #define MKFLV(x,t) case FLV_CODECID_##x : _videostream.fccHandler=_video_bih.biCompression=\
974 fourCC::get((uint8_t *)#t);break;
975 switch(codec)
976 {
977 MKFLV(H264,H264);
978 MKFLV(H263,FLV1);
979 MKFLV(VP6,VP6F);
980 MKFLV(VP6A,VP6A);
981 default : _videostream.fccHandler=_video_bih.biCompression=fourCC::get((uint8_t *)"XXX");break;
982 }
983 updateDimensionWithMeta(codec);
984
985 if(codec==FLV_CODECID_H263 && *remaining)
986 {
987 #ifdef USE_BUFFERED_IO
988 uint64_t pos=0;
989 parser->getpos(&pos);
990 #else
991 uint32_t pos=ftello(_fd);
992 #endif
993 uint32_t len=*remaining,width,height;
994 uint8_t *buffer=new uint8_t[len];
995 read(len,buffer);
996 #ifdef USE_BUFFERED_IO
997 parser->setpos(pos);
998 #else
999 fseeko(_fd,pos,SEEK_SET);
1000 #endif
1001 /* Decode header, from h263dec.c / lavcodec*/
1002 if(extractH263FLVInfo(buffer,len,&width,&height))
1003 {
1004 _video_bih.biHeight=_mainaviheader.dwHeight=height;
1005 _video_bih.biWidth=_mainaviheader.dwWidth=width;
1006 }
1007 delete [] buffer;
1008 }
1009 return 1;
1010 }
1011 /**
1012 \fn setAudioHeader
1013 \brief Build WavHeader from info
1014
1015 */
setAudioHeader(uint32_t format,uint32_t fq,uint32_t bps,uint32_t channels)1016 uint8_t flvHeader::setAudioHeader(uint32_t format,uint32_t fq,uint32_t bps,uint32_t channels)
1017 {
1018 switch(fq)
1019 {
1020 case 3: wavHeader.frequency=44100;break;
1021 case 2: wavHeader.frequency=22050;break;
1022 case 1: wavHeader.frequency=11025;break;
1023 case 0:
1024 if(format==5) wavHeader.frequency=8000;
1025 else wavHeader.frequency=5512;
1026 break;
1027 default: printf("[FLV]Unknown frequency:%u\n",fq);
1028 }
1029 switch(format)
1030 {
1031 case 6: wavHeader.encoding=WAV_NELLYMOSER;break;
1032 case 2: wavHeader.encoding=WAV_MP3;break;
1033 case 3: wavHeader.encoding=WAV_PCM;break;
1034 case 0: wavHeader.encoding=WAV_LPCM;break;
1035 case 1: wavHeader.encoding=WAV_MSADPCM;break;
1036 case 10:wavHeader.encoding=WAV_AAC;break;
1037 default:
1038 printf("[FLV]Unsupported audio codec:%u\n",format);
1039 }
1040 switch(channels)
1041 {
1042 case 1: wavHeader.channels=2;break;
1043 case 0: wavHeader.channels=1;break;
1044 default:
1045 printf("[FLV]Unsupported channel mode :%u\n",channels);
1046 }
1047 switch(bps)
1048 {
1049 case 1: wavHeader.bitspersample=16;break;
1050 case 0: wavHeader.bitspersample=8;break;
1051 default:
1052 printf("[FLV]Unsupported bps mode :%u\n",bps);
1053 }
1054 wavHeader.byterate=(64000)/8; // 64 kbits default
1055 return 1;
1056 }
1057 /**
1058 \fn insertVideo
1059 \brief add a frame to the index, grow the index if needed
1060 */
insertVideo(uint64_t pos,uint32_t size,uint32_t frameType,uint32_t dts,uint32_t pts)1061 uint8_t flvHeader::insertVideo(uint64_t pos,uint32_t size,uint32_t frameType,uint32_t dts,uint32_t pts)
1062 {
1063 videoTrack->grow();
1064 flvIndex *x=&(videoTrack->_index[videoTrack->_nbIndex]);
1065 x->size=size;
1066 x->pos=pos;
1067 x->dtsUs=dts*1000LL;
1068 if(pts==0xffffffff) x->ptsUs=ADM_NO_PTS;
1069 else
1070 x->ptsUs=pts*1000LL;
1071 if(videoCodec==FLV_CODECID_H264 && nalsize && spsinfo)
1072 {
1073 uint8_t *buffer=new uint8_t[size];
1074 if(read(size,buffer))
1075 { // Keep fingers crossed that we don't encounter inband SPS changing midstream.
1076 uint32_t flags=0;
1077 if(extractH264FrameType(buffer,size,nalsize,&flags,NULL,spsinfo))
1078 {
1079 if(!!(flags & AVI_KEY_FRAME) != (frameType==1))
1080 ADM_warning("Container and codec disagree about frame %u: %s says keyframe.\n",
1081 videoTrack->_nbIndex,(flags & AVI_KEY_FRAME)? "codec" : "container");
1082 if(flags & AVI_B_FRAME)
1083 bFramesPresent=true;
1084 x->flags=flags;
1085 videoTrack->_nbIndex++;
1086 return 1;
1087 }
1088 }
1089 }
1090 if(frameType==1)
1091 {
1092 x->flags=AVI_KEY_FRAME;
1093 }
1094 else
1095 {
1096 x->flags=0;
1097 }
1098 videoTrack->_nbIndex++;
1099 return 1;
1100 }
1101 /**
1102 \fn enforceConstantFps
1103 \brief Check whether we can force CFR and update video index if necessary
1104 */
enforceConstantFps(uint32_t scale,uint32_t rate,uint64_t delay,bool reorder)1105 bool flvHeader::enforceConstantFps(uint32_t scale, uint32_t rate, uint64_t delay, bool reorder)
1106 {
1107 if(!_videostream.dwRate)
1108 return false;
1109
1110 double d=_videostream.dwScale;
1111 d*=1000.*1000;
1112 d/=_videostream.dwRate*2;
1113 d+=0.49;
1114 const int64_t threshold=(int64_t)d; // half of frame increment, as generous as possible
1115 const uint32_t nbFrames=videoTrack->_nbIndex;
1116 uint32_t i;
1117
1118 for(i=0; i<nbFrames; i++)
1119 {
1120 d=i;
1121 d*=_videostream.dwScale;
1122 d*=1000.;
1123 d/=_videostream.dwRate;
1124 d*=1000.;
1125 d+=0.49;
1126 uint64_t expected=(uint64_t)d;
1127 flvIndex *x=&(videoTrack->_index[i]);
1128 if(x->dtsUs!=ADM_NO_PTS)
1129 {
1130 int64_t delta=x->dtsUs-expected;
1131 //printf("Frame %u dts delta: %" PRId64" us\n",i,delta);
1132 if(delta>threshold || -delta>threshold)
1133 {
1134 ADM_warning("Delta %" PRId64" for frame %u exceeds threshold.\n",delta,i);
1135 return false;
1136 }
1137 }
1138 }
1139 ADM_info("Forcing constant frame rate...\n");
1140 // Force constant frame rate
1141 for(i=0; i<nbFrames; i++)
1142 {
1143 d=i;
1144 d*=_videostream.dwScale;
1145 d*=1000.*1000;
1146 d/=_videostream.dwRate;
1147 d+=0.49;
1148 videoTrack->_index[i].dtsUs=(uint64_t)d;
1149 }
1150 // round up delay to a multiple of timebase
1151 if(delay)
1152 {
1153 uint64_t num=_videostream.dwScale;
1154 delay+=num-1;
1155 delay/=num;
1156 delay*=num;
1157 }
1158 if(reorder)
1159 {
1160 uint32_t i;
1161 std::vector <uint32_t> DisplayOrder;
1162 std::vector <uint64_t> sortedPts;
1163 for(i=0; i<nbFrames; i++)
1164 {
1165 flvIndex *x=&(videoTrack->_index[i]);
1166 if(x->ptsUs==ADM_NO_PTS) continue;
1167 sortedPts.push_back(x->ptsUs);
1168 }
1169 std::sort(sortedPts.begin(),sortedPts.end());
1170 for(i=0; i<nbFrames; i++)
1171 {
1172 uint32_t nbInvalid=0;
1173 flvIndex *x=&(videoTrack->_index[i]);
1174 if(x->ptsUs==ADM_NO_PTS)
1175 {
1176 DisplayOrder.push_back(i);
1177 nbInvalid++;
1178 continue;
1179 }
1180 uint32_t base=0;
1181 if(i>32) base=i-32; // max 16 ref * 2 fields
1182 for(uint32_t j=base;j<sortedPts.size();j++)
1183 {
1184 if(x->ptsUs!=sortedPts.at(j))
1185 continue;
1186 uint32_t k=nbInvalid+j;
1187 ADM_assert(k<nbFrames);
1188 DisplayOrder.push_back(k);
1189 //printf("%u --> %u\n",i,k);
1190 break;
1191 }
1192 }
1193 for(i=0; i<nbFrames; i++)
1194 {
1195 if(i+1>=DisplayOrder.size())
1196 break;
1197 uint32_t j=DisplayOrder.at(i);
1198 flvIndex *x=&(videoTrack->_index[i]);
1199 if(x->ptsUs==ADM_NO_PTS) continue;
1200 x->ptsUs=videoTrack->_index[j].dtsUs+delay;
1201 }
1202 }else
1203 {
1204 for(i=0; i<nbFrames; i++)
1205 {
1206 flvIndex *x=&(videoTrack->_index[i]);
1207 x->ptsUs=x->dtsUs+delay;
1208 }
1209 }
1210 // verify that the delay is sufficient
1211 uint64_t delay2=0;
1212 for(i=0; i<nbFrames; i++)
1213 {
1214 flvIndex *x=&(videoTrack->_index[i]);
1215 if(x->ptsUs==ADM_NO_PTS || x->dtsUs==ADM_NO_PTS)
1216 continue;
1217 if(x->ptsUs+delay2<x->dtsUs)
1218 delay2+=x->dtsUs-x->ptsUs;
1219 }
1220 if(delay2)
1221 {
1222 ADM_warning("Original PTS delay is insufficient, adding %" PRIu64" us.\n",delay2);
1223 for(i=0; i<nbFrames; i++)
1224 videoTrack->_index[i].ptsUs+=delay2;
1225 }
1226 return true;
1227 }
1228
1229 /**
1230 \fn checkTimeBase
1231 \brief Check whether all dts and pts can be expressed in the given timebase
1232 */
checkTimeBase(uint32_t scale,uint32_t rate)1233 bool flvHeader::checkTimeBase(uint32_t scale, uint32_t rate)
1234 {
1235 if(!scale || rate<1000)
1236 return false;
1237
1238 uint32_t i;
1239 const uint32_t nbFrames=videoTrack->_nbIndex;
1240 scale*=1000;
1241 // check dts first
1242 for(i=0; i<nbFrames; i++)
1243 {
1244 flvIndex *x=&(videoTrack->_index[i]);
1245 if(x->dtsUs==ADM_NO_PTS || x->dtsUs<1000)
1246 continue;
1247 uint64_t low=x->dtsUs-1000; // -1 ms
1248 uint64_t high=x->dtsUs+1000; // +1 ms
1249 double f=low;
1250 f*=rate;
1251 f/=scale;
1252 f+=0.49;
1253 low=(uint64_t)f; // 1000 * scaled time
1254 f=high;
1255 f*=rate;
1256 f/=scale;
1257 f+=0.49;
1258 high=(uint64_t)f;
1259 if(high%1000>100 || low%1000<900)
1260 {
1261 ADM_warning("Frame %d dts is not a multiple of timebase.\n",i);
1262 return false;
1263 }
1264 }
1265 // now get pts delay
1266 #define HIGH_POINT 0xFFFFFFF0 // an arbitrary high value
1267 uint64_t delay=HIGH_POINT;
1268 for(i=0; i<nbFrames; i++)
1269 {
1270 flvIndex *x=&(videoTrack->_index[i]);
1271 if(x->ptsUs==ADM_NO_PTS)
1272 continue;
1273 if(x->ptsUs<delay) delay=x->ptsUs;
1274 if(!delay) break;
1275 }
1276 if(delay==HIGH_POINT)
1277 return true; // no valid pts
1278 ADM_info("Probed PTS delay: %" PRIu64" us.\n",delay);
1279 // check pts
1280 for(i=0; i<nbFrames; i++)
1281 {
1282 flvIndex *x=&(videoTrack->_index[i]);
1283 if(x->ptsUs==ADM_NO_PTS || x->ptsUs-delay<1000)
1284 continue;
1285 uint64_t low=x->ptsUs-delay-1000; // -1 ms
1286 uint64_t high=x->ptsUs-delay+1000; // +1 ms
1287 double f=low;
1288 f*=rate;
1289 f/=scale;
1290 f+=0.49;
1291 low=(uint64_t)f; // 1000 * scaled time
1292 f=high;
1293 f*=rate;
1294 f/=scale;
1295 f+=0.49;
1296 high=(uint64_t)f;
1297 if(high%1000>100 || low%1000<900)
1298 {
1299 ADM_warning("Frame %d pts is not a multiple of timebase.\n",i);
1300 return false;
1301 }
1302 }
1303 return true;
1304 }
1305
1306 /**
1307 \fn insertAudio
1308 \brief add a frame to the index, grow the index if needed
1309 */
insertAudio(uint64_t pos,uint32_t size,uint32_t pts)1310 uint8_t flvHeader::insertAudio(uint64_t pos,uint32_t size,uint32_t pts)
1311 {
1312 audioTrack->grow();
1313 flvIndex *x=&(audioTrack->_index[audioTrack->_nbIndex]);
1314 x->size=size;
1315 x->pos=pos;
1316 x->dtsUs=pts*1000LL;
1317 x->flags=0;
1318 audioTrack->_nbIndex++;
1319 return 1;
1320 }
1321
1322
1323
1324 /**
1325 \fn getAudioInfo
1326 \brief returns wav header for stream i (=0)
1327 */
getAudioInfo(uint32_t i)1328 WAVHeader *flvHeader::getAudioInfo(uint32_t i )
1329 {
1330 if(_isaudiopresent)
1331 return &wavHeader;
1332 else
1333 return NULL;
1334 }
1335 /**
1336 \fn getAudioStream
1337 */
1338
getAudioStream(uint32_t i,ADM_audioStream ** audio)1339 uint8_t flvHeader::getAudioStream(uint32_t i,ADM_audioStream **audio)
1340 {
1341 if(_isaudiopresent)
1342 {
1343 *audio=_audioStream;
1344 return 1;
1345 }
1346 *audio=NULL;
1347 return 0;
1348 }
1349 /**
1350 \fn getNbAudioStreams
1351
1352 */
getNbAudioStreams(void)1353 uint8_t flvHeader::getNbAudioStreams(void)
1354 {
1355 if(_isaudiopresent)
1356 {
1357
1358 return 1;
1359 }
1360 return 0;
1361 }
1362 /*
1363 __________________________________________________________
1364 */
1365
Dump(void)1366 void flvHeader::Dump(void)
1367 {
1368
1369 }
1370 /**
1371 \fn close
1372 \brief cleanup
1373 */
1374
close(void)1375 uint8_t flvHeader::close(void)
1376 {
1377 if(_filename) ADM_dealloc(_filename);
1378 if(videoTrack)
1379 {
1380 if(videoTrack->extraData) delete [] videoTrack->extraData;
1381 delete videoTrack;
1382 }
1383 if(audioTrack)
1384 {
1385 if(audioTrack->extraData) delete [] audioTrack->extraData;
1386 delete audioTrack;
1387 }
1388 #ifdef USE_BUFFERED_IO
1389 if(parser)
1390 {
1391 delete parser;
1392 parser=NULL;
1393 }
1394 #else
1395 if(_fd) fclose(_fd);
1396 _fd=NULL;
1397 #endif
1398 if(_audioStream) delete _audioStream;
1399 if(_access) delete _access;
1400 if(spsinfo) delete spsinfo;
1401
1402 _filename=NULL;
1403 videoTrack=NULL;
1404 audioTrack=NULL;
1405 _audioStream=NULL;
1406 _access=NULL;
1407 spsinfo=NULL;
1408 return 1;
1409 }
1410 /**
1411 \fn flvHeader
1412 \brief constructor
1413 */
1414
flvHeader(void)1415 flvHeader::flvHeader( void ) : vidHeader()
1416 {
1417 videoCodec=0xFFFF;
1418 #ifdef USE_BUFFERED_IO
1419 parser=NULL;
1420 #else
1421 _fd=NULL;
1422 #endif
1423 _filename=NULL;
1424 videoTrack=NULL;
1425 audioTrack=NULL;
1426 _audioStream=NULL;
1427 _access=NULL;
1428 memset(&wavHeader,0,sizeof(wavHeader));
1429 metaWidth=0;
1430 metaHeight=0;
1431 metaFps1000=0;
1432 metaFrameWidth=0;
1433 metaFrameHeight=0;
1434 ptsInvalid=false;
1435 bFramesPresent=false;
1436 nalsize=0;
1437 spsinfo=NULL;
1438 }
1439 /**
1440 \fn flvHeader
1441 \brief destructor
1442 */
1443
~flvHeader()1444 flvHeader::~flvHeader( )
1445 {
1446 close();
1447 }
1448
1449
1450 /**
1451 \fn setFlag
1452 \brief Returns timestamp in us of frame "frame" (PTS)
1453 */
1454
setFlag(uint32_t frame,uint32_t flags)1455 uint8_t flvHeader::setFlag(uint32_t frame,uint32_t flags)
1456 {
1457 if(frame>=videoTrack->_nbIndex)
1458 {
1459 printf("[FLV] Setflags out of boud %u/%u\n",frame,videoTrack->_nbIndex);
1460 return 0;
1461 }
1462 videoTrack->_index[frame].flags=flags;
1463 return 1;
1464 }
1465 /**
1466 \fn getFlags
1467 \brief Returns timestamp in us of frame "frame" (PTS)
1468 */
1469
getFlags(uint32_t frame,uint32_t * flags)1470 uint32_t flvHeader::getFlags(uint32_t frame,uint32_t *flags)
1471 {
1472 if(frame>=videoTrack->_nbIndex)
1473 {
1474 printf("[FLV] Getflags out of boud %u/%u\n",frame,videoTrack->_nbIndex);
1475 return 0;
1476 }
1477
1478 *flags=videoTrack->_index[frame].flags;
1479 return 1;
1480 }
1481
1482 /**
1483 \fn getTime
1484 \brief Returns timestamp in us of frame "frame" (PTS)
1485 */
getTime(uint32_t frame)1486 uint64_t flvHeader::getTime(uint32_t frame)
1487 {
1488 if(frame>=videoTrack->_nbIndex) return ADM_NO_PTS;
1489 flvIndex *idx=&(videoTrack->_index[frame]);
1490 return idx->ptsUs;
1491 }
1492 /**
1493 \fn getFrame
1494 */
1495
getFrame(uint32_t frame,ADMCompressedImage * img)1496 uint8_t flvHeader::getFrame(uint32_t frame,ADMCompressedImage *img)
1497 {
1498 if(frame>=videoTrack->_nbIndex) return 0;
1499 flvIndex *idx=&(videoTrack->_index[frame]);
1500 #ifdef USE_BUFFERED_IO
1501 parser->setpos(idx->pos);
1502 if(!read(idx->size,img->data))
1503 return 0;
1504 #else
1505 fseeko(_fd,idx->pos,SEEK_SET);
1506 fread(img->data,idx->size,1,_fd);
1507 #endif
1508 img->dataLength=idx->size;
1509 img->flags=idx->flags;
1510 //img->demuxerDts=ADM_COMPRESSED_NO_PTS;
1511 // For flash assume PTS=DTS (???)
1512 img->demuxerDts=idx->dtsUs;;
1513 img->demuxerPts=idx->ptsUs;;
1514 return 1;
1515 }
1516 /**
1517 \fn getExtraHeaderData
1518 */
getExtraHeaderData(uint32_t * len,uint8_t ** data)1519 uint8_t flvHeader::getExtraHeaderData(uint32_t *len, uint8_t **data)
1520 {
1521 if(videoTrack)
1522 {
1523 *len=videoTrack->extraDataLen;
1524 *data=videoTrack->extraData;
1525 return 1;
1526 }
1527 *len=0;
1528 *data=NULL;
1529 return 1;
1530 }
1531
1532 /**
1533 \fn getFrameSize
1534 \brief return the size of frame frame
1535 */
getFrameSize(uint32_t frame,uint32_t * size)1536 uint8_t flvHeader::getFrameSize (uint32_t frame, uint32_t * size)
1537 {
1538 if(frame>=videoTrack->_nbIndex)
1539 {
1540 printf("[FLV] getFrameSize out of boud %u/%u\n",frame,videoTrack->_nbIndex);
1541 return 0;
1542 }
1543 *size = videoTrack->_index[frame].size;
1544 return 1;
1545 }
1546 //!!
1547
1548 /**
1549 \fn getPtsDts
1550 */
getPtsDts(uint32_t frame,uint64_t * pts,uint64_t * dts)1551 bool flvHeader::getPtsDts(uint32_t frame,uint64_t *pts,uint64_t *dts)
1552 {
1553
1554 if(frame>=videoTrack->_nbIndex)
1555 {
1556 printf("[MKV] Frame %" PRIu32" exceeds # of frames %" PRIu32"\n",frame,videoTrack->_nbIndex);
1557 return 0;
1558 }
1559
1560 flvIndex *idx=&(videoTrack->_index[frame]);
1561
1562 *dts=idx->dtsUs; // FIXME
1563 *pts=idx->ptsUs;
1564 return true;
1565 }
1566 /**
1567 \fn setPtsDts
1568 */
setPtsDts(uint32_t frame,uint64_t pts,uint64_t dts)1569 bool flvHeader::setPtsDts(uint32_t frame,uint64_t pts,uint64_t dts)
1570 {
1571 if(frame>=videoTrack->_nbIndex)
1572 {
1573 printf("[MKV] Frame %" PRIu32" exceeds # of frames %" PRIu32"\n",frame,videoTrack->_nbIndex);
1574 return 0;
1575 }
1576
1577 flvIndex *idx=&(videoTrack->_index[frame]);
1578
1579 idx->dtsUs=dts; // FIXME
1580 idx->ptsUs=pts;
1581 //*pts=idx->timeCodeUs; // FIXME PTS=DTS ??
1582 return true;
1583 }
1584
1585 //EOF
1586