1 /***************************************************************************
2             \file            muxerFFmpeg
3             \brief           Base class for ffmpeg based muxer
4                              -------------------
5 
6     copyright            : (C) 2009 by mean
7     email                : fixounet@free.fr
8 
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 "ADM_vidMisc.h"
22 #include "fourcc.h"
23 #include "ADM_coreMuxerFfmpeg.h"
24 #include "ADM_muxerUtils.h"
25 #include "ADM_coreCodecMapping.h"
26 #include "ADM_audioXiphUtils.h"
27 
28 extern "C" {
29 #include "libavformat/url.h"
30 #include "ADM_audioClock.h"
31 }
32 
33 #if 1
34 #define aprintf(...) {}
35 #else
36 #define aprintf printf
37 #endif
38 /**
39     \fn ffmpuxerSetExtradata
40     \brief dupe the extradata if needed
41 */
ffmpuxerSetExtradata(AVCodecParameters * params,int size,const uint8_t * data)42 bool ffmpuxerSetExtradata(AVCodecParameters *params, int size, const uint8_t *data)
43 {
44     if(!size)
45     {
46         params->extradata=NULL;
47         params->extradata_size=0;
48         return true;
49     }
50     uint8_t *copy=(uint8_t *)av_malloc( (1+(size>>4))<<4);;
51     memcpy(copy,data,size);
52     params->extradata=copy;
53     params->extradata_size=size;
54     return true;
55 }
56 
57 /**
58     \fn writePacket
59 */
writePacket(AVPacket * pkt)60 bool muxerFFmpeg::writePacket(AVPacket *pkt)
61 {
62 #if 0
63         printf("Track :%d size :%d PTS:%"PRId64" DTS:%"PRId64"\n",
64                     pkt->stream_index,pkt->size,pkt->pts,pkt->dts);
65 #endif
66     int ret =av_write_frame(oc, pkt);
67     if(ret)
68         return false;
69     return true;
70 }
71 
72 /**
73     \fn muxerFFmpeg
74 */
muxerFFmpeg()75 muxerFFmpeg::muxerFFmpeg()
76 {
77     fmt=NULL;
78     oc=NULL;
79     for(int i=0;i<ADM_MAX_AUDIO_STREAM;i++)
80         audio_st[i]=NULL;
81     video_st=NULL;
82     audioDelay=0;
83     initialized=false;
84     roundup=0;
85 }
86 /**
87     \fn closeMuxer
88 */
closeMuxer()89 bool muxerFFmpeg::closeMuxer()
90 {
91     int res=0;
92     if(oc)
93     {
94         if(initialized==true)
95         {
96             res=av_write_trailer(oc);
97             if(res<0)
98                 ADM_warning("Error %d writing trailer.\n",res);
99             avio_close(oc->pb);
100         }
101         avformat_free_context(oc);
102         oc=NULL;
103     }
104 
105     for(int i=0;i<ADM_MAX_AUDIO_STREAM;i++)
106     {
107         audio_st[i]=NULL;
108     }
109     video_st=NULL;
110     return !res;
111 }
112 /**
113     \fn muxerFFmpeg
114 */
~muxerFFmpeg()115 muxerFFmpeg::~muxerFFmpeg()
116 {
117         closeMuxer();
118 }
119 /**
120     \fn setupMuxer
121     \brief open and do the base muxer setup
122 */
setupMuxer(const char * format,const char * filename)123 bool muxerFFmpeg::setupMuxer(const char *format,const char *filename)
124 {
125     fmt=av_guess_format(format, NULL, NULL);
126     if(!fmt)
127     {
128             printf("[FF] guess format failed\n");
129             return false;
130     }
131     oc = avformat_alloc_context();
132     if (!oc)
133     {
134         printf("[FF] alloc format context failed\n");
135         return false;
136     }
137     oc->oformat = fmt;
138 #if defined(__APPLE__)
139  #define MAX_LEN 1024
140 #else
141  #define MAX_LEN 4096
142 #endif
143     uint32_t len=strlen(filename);
144     if(len>MAX_LEN)
145     {
146         ADM_error("Filename length %u exceeds limit %u\n",len,MAX_LEN);
147         return false;
148     }
149     char *url=(char *)ADM_alloc(len+8);
150     snprintf(url,len+8,"file://%s",filename);
151     url[len+7]=0;
152     oc->url=url;
153 //#warning use AV METADATA
154     printf("[FF] Muxer opened\n");
155     return true;
156 }
157 /**
158         \fn setAvCodec
159 */
setAvCodec(AVCodecContext * c,enum AVCodecID id)160 static bool setAvCodec(AVCodecContext *c,enum AVCodecID id)
161 {
162         AVCodec *d=avcodec_find_decoder(id);
163         ADM_assert(d);
164         c->codec=d;
165         return true;
166 }
167 /**
168     \fn initVideo
169     \brief setup video part of muxer
170 */
initVideo(ADM_videoStream * stream)171 bool muxerFFmpeg::initVideo(ADM_videoStream *stream)
172 {
173     audioDelay=stream->getVideoDelay();
174     printf("[muxerFFmpeg::initVideo] Initial audio delay: %" PRIu64" ms\n",audioDelay/1000);
175     video_st = avformat_new_stream(oc, NULL);
176 	if (!video_st)
177 	{
178 		printf("[FF] new stream failed\n");
179 		return false;
180 	}
181     AVCodecParameters *par;
182         par = video_st->codecpar;
183         par->sample_aspect_ratio.num=1;
184         par->sample_aspect_ratio.den=1;
185         video_st->sample_aspect_ratio=par->sample_aspect_ratio;
186         par->bit_rate=9000*1000;
187         par->codec_type = AVMEDIA_TYPE_VIDEO;
188         par->width = stream->getWidth();
189         par->height =stream->getHeight();
190 
191         uint32_t videoExtraDataSize=0;
192         uint8_t  *videoExtraData;
193         stream->getExtraData(&videoExtraDataSize,&videoExtraData);
194         printf("[FF] Using %d bytes for video extradata\n",(int)videoExtraDataSize);
195         ffmpuxerSetExtradata(par,videoExtraDataSize,videoExtraData);
196 
197     AVCodecContext *c;
198         c = video_st->codec;
199         c->rc_buffer_size=8*1024*224;
200         c->rc_max_rate=9500*1000;
201         c->rc_min_rate=0;
202         c->flags=AV_CODEC_FLAG_QSCALE;
203 
204         uint32_t fcc=stream->getFCC();
205 
206         if(isMpeg4Compatible(fcc))
207         {
208                 par->codec_id = AV_CODEC_ID_MPEG4;
209                 if(stream->providePts()==true)
210                 {
211                     c->has_b_frames=1; // in doubt...
212                     c->max_b_frames=2;
213                 }else
214                 {
215                     ADM_warning("Incoming stream does not provide PTS \n");
216                     c->has_b_frames=0; // No PTS=cannot handle CTS...
217                     c->max_b_frames=0;
218                 }
219         }else
220         {
221                 if(isH264Compatible(fcc) || isH265Compatible(fcc))
222                 {
223                         if(stream->providePts()==true)
224                         {
225                             c->has_b_frames=1; // in doubt...
226                             c->max_b_frames=2;
227                         }else
228                         {
229                             printf("[MP4] Source video has no PTS information, assuming no b frames\n");
230                             c->has_b_frames=0; // No PTS=cannot handle CTS...
231                             c->max_b_frames=0;
232                         }
233 
234                         if(isH265Compatible(fcc)) {
235                             par->codec_id = AV_CODEC_ID_HEVC;
236                              setAvCodec(c,AV_CODEC_ID_HEVC);
237                         } else {
238                             par->codec_id = AV_CODEC_ID_H264;
239                              setAvCodec(c,AV_CODEC_ID_H264);
240                         }
241                 }
242                 else
243                 {
244                         if(isDVCompatible(fcc))
245                         {
246                           par->codec_id = AV_CODEC_ID_DVVIDEO;
247                         }else
248                         {
249                           if(fourCC::check(fcc,(uint8_t *)"H263"))
250                           {
251                                     par->codec_id= AV_CODEC_ID_H263;
252                             }else
253 
254                            if(isVP6Compatible(stream->getFCC()))
255                                 {
256                                          par->codec_id= AV_CODEC_ID_VP6F;
257                                          setAvCodec(c,AV_CODEC_ID_VP6F);
258                                          c->has_b_frames=0; // No PTS=cannot handle CTS...
259                                          c->max_b_frames=0;
260                                 }else
261                                         if(fourCC::check(stream->getFCC(),(uint8_t *)"FLV1"))
262                                         {
263                                                 c->has_b_frames=0; // No PTS=cannot handle CTS...
264                                                 c->max_b_frames=0;
265                                                 par->codec_id= AV_CODEC_ID_FLV1;
266                                                 setAvCodec(c,AV_CODEC_ID_FLV1);
267 
268                                         }else
269                                         {
270                                             if(fourCC::check(stream->getFCC(),(uint8_t *)"mpg1"))
271                                             {
272                                                 c->has_b_frames=1; // No PTS=cannot handle CTS...
273                                                 c->max_b_frames=2;
274                                                 par->codec_id= AV_CODEC_ID_MPEG1VIDEO;
275                                             }
276                                             else if(fourCC::check(stream->getFCC(),(uint8_t *)"MPEG"))
277                                             {
278                                                 c->has_b_frames=1; // No PTS=cannot handle CTS...
279                                                 c->max_b_frames=2;
280                                                 par->codec_id= AV_CODEC_ID_MPEG2VIDEO;
281                                             }else
282                                             {
283                                                 uint32_t id=stream->getFCC();
284 
285                                                 AVCodecID cid=ADM_codecIdFindByFourcc(fourCC::tostring(id));
286                                                 if(cid== AV_CODEC_ID_NONE)
287                                                 {
288                                                     printf("[FF] Unknown video codec\n");
289                                                     return false;
290                                                 }
291                                                 par->codec_id=cid;
292                                             }
293                                         }
294                         }
295                 }
296         }
297         if(useGlobalHeader()==true)
298         {
299             if(videoExtraDataSize)
300             {
301                 ADM_info("Video has extradata and muxer requires globalHeader, assuming it is done so.\n");
302                 c->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
303             }else
304             {
305                 ADM_warning("Video has no extradata but muxer requires globalHeader.\n");
306             }
307         }
308 
309         printf("[FF] Video initialized\n");
310 
311     return true;
312 }
313 /**
314     \fn initAudio
315     \brief setup the audio parts if present
316 */
initAudio(uint32_t nbAudioTrack,ADM_audioStream ** audio)317 bool muxerFFmpeg::initAudio(uint32_t nbAudioTrack,ADM_audioStream **audio)
318 {
319     if(!nbAudioTrack)
320     {
321         printf("[FF] No audio\n");
322         return true;
323     }
324 
325     for(int i=0;i<nbAudioTrack;i++)
326     {
327           uint32_t audioextraSize;
328           uint8_t  *audioextraData;
329 
330           audio[i]->getExtraData(&audioextraSize,&audioextraData);
331 
332           audio_st[i] = avformat_new_stream(oc, NULL);
333           if (!audio_st[i])
334           {
335                   printf("[FF]: new stream failed (audio)\n");
336                   return false;
337           }
338           WAVHeader *audioheader=audio[i]->getInfo();;
339           AVCodecContext *c;
340           AVCodecParameters *par;
341           c = audio_st[i]->codec;
342           par = audio_st[i]->codecpar;
343           par->frame_size=1024; //For AAC mainly, sample per frame
344           printf("[FF] Bitrate %u\n",(audioheader->byterate*8)/1000);
345           par->sample_rate = audioheader->frequency;
346           switch(audioheader->encoding)
347           {
348                   case WAV_OGG_VORBIS:
349                                 par->codec_id = AV_CODEC_ID_VORBIS;par->frame_size=6*256;
350                                 if(!strcmp(fmt->name,"mp4") || !strcmp(fmt->name,"psp")) // Need to translate from adm to xiph
351                                 {
352                                     int xiphLen=(int)audioextraSize+(audioextraSize/255)+4+5;
353                                     uint8_t *xiph=new uint8_t[xiphLen+AV_INPUT_BUFFER_PADDING_SIZE];
354                                     memset(xiph,0,xiphLen+AV_INPUT_BUFFER_PADDING_SIZE);
355                                     xiphLen=ADMXiph::admExtraData2xiph(audioextraSize,audioextraData,xiph);
356                                     ffmpuxerSetExtradata(par,xiphLen,xiph);
357                                     delete [] xiph;
358                                     xiph=NULL;
359                                 }else
360                                 {
361                                     ffmpuxerSetExtradata(par,audioextraSize,audioextraData);
362                                 }
363                                 break;
364                   case WAV_FLAC:
365                                 par->codec_id = AV_CODEC_ID_FLAC;
366                                 // Do we already have the flac header ? FFmpeg will add it..
367                                 // If we have it, skip it
368                                 if(audioextraSize>=8 && audioextraData[0]==0x66 && audioextraData[1]==0x4c &&audioextraData[2]==0x61 && audioextraData[3]==0x43 )
369                                     ffmpuxerSetExtradata(par,audioextraSize-8,audioextraData+8);
370                                 else
371                                     ffmpuxerSetExtradata(par,audioextraSize,audioextraData);
372                                 break;
373                   case WAV_DTS: par->codec_id = AV_CODEC_ID_DTS;par->frame_size=1024;break;
374                   case WAV_OPUS:    par->codec_id = AV_CODEC_ID_OPUS;
375                                     par->frame_size=1024;
376                                     ffmpuxerSetExtradata(par,audioextraSize,audioextraData);
377                                     break;
378                   case WAV_EAC3: par->codec_id = AV_CODEC_ID_EAC3;par->frame_size=6*256;break;
379                   case WAV_AC3: par->codec_id = AV_CODEC_ID_AC3;par->frame_size=6*256;break;
380                   case WAV_MP2: par->codec_id = AV_CODEC_ID_MP2;par->frame_size=1152;break;
381                   case WAV_MP3:
382 //  #warning FIXME : Probe deeper
383                               par->frame_size=1152;
384                               par->codec_id = AV_CODEC_ID_MP3;
385                               break;
386                   case WAV_LPCM:
387                   case WAV_PCM:
388                                 if(audioheader->encoding==WAV_LPCM)
389                                 {
390                                   // One chunk is 10 ms (1/100 of fq)
391                                   par->frame_size=4;
392                                   par->codec_id = AV_CODEC_ID_PCM_S16BE;break;
393                                 }
394                                 else
395                                 {
396                                   // One chunk is 10 ms (1/100 of fq)
397                                   par->frame_size=4;
398                                   par->codec_id = AV_CODEC_ID_PCM_S16LE;break;
399                                 }
400                   case WAV_AAC:
401                   case WAV_AAC_HE:
402                                   ffmpuxerSetExtradata(par,audioextraSize,audioextraData);
403                                   par->codec_id = AV_CODEC_ID_AAC;
404                                   par->frame_size=audio[i]->getSamplesPerPacket();
405                                   break;
406                   default:
407                                  printf("[FF]: Unsupported audio\n");
408                                  return false;
409                           break;
410           }
411           par->codec_type = AVMEDIA_TYPE_AUDIO;
412           par->bit_rate = audioheader->byterate*8;
413           c->rc_buffer_size=(par->bit_rate/(2*8)); // 500 ms worth
414           par->channels = audioheader->channels;
415           if(useGlobalHeader()==true)
416           {
417             if(audioextraSize)
418             {
419                 ADM_info("Audio has extradata and muxer requires globalHeader, assuming it is done so.\n");
420                 c->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
421             }else
422             {
423                 ADM_warning("Audio has no extradata but muxer requires globalHeader.\n");
424             }
425           }
426 
427             //set language
428             const std::string lang=audio[i]->getLanguage();
429             if(lang.size())
430             {
431                   AVDictionary *dict = NULL;
432                   av_dict_set(&dict, "language", lang.c_str(), 0);
433                   audio_st[i]->metadata=dict;
434                   ADM_info("Language for track %d is %s\n",i,lang.c_str());
435             }
436         }
437         printf("[FF] Audio initialized\n");
438         return true;
439 }
440 #define AUDIO_BUFFER_SIZE 48000*6*sizeof(float)
441 /**
442  * \class MuxAudioPacket
443  */
444 class MuxAudioPacket
445 {
446 public:
MuxAudioPacket()447     MuxAudioPacket() {eof=false;dts=ADM_NO_PTS;present=false;size=0;clock=NULL;}
~MuxAudioPacket()448     ~MuxAudioPacket()
449     {
450         if(clock)
451         {
452             delete clock;
453             clock=NULL;
454         }
455     }
456     uint8_t     buffer[AUDIO_BUFFER_SIZE];
457     uint32_t    size;
458     bool        eof;
459     bool        present;
460     uint64_t    dts;
461     uint32_t    samples;
462     audioClock  *clock;
463 };
464 
465 /**
466     \fn saveLoop
467 */
saveLoop(const char * title)468 bool muxerFFmpeg::saveLoop(const char *title)
469 {
470 
471 
472     printf("[FF] Saving\n");
473     uint32_t bufSize=vStream->getWidth()*vStream->getHeight()*3;
474     uint8_t *buffer=new uint8_t[bufSize];
475     uint64_t rawDts;
476     uint64_t lastVideoDts=0;
477     uint64_t lastVideoDtsLav=0;
478     uint64_t videoIncrement;
479     bool ret=true;
480     uint32_t written=0;
481     bool result=true;
482     bool gotVideoPacket=true;
483     int missingPts=0;
484 
485     float f=(float)vStream->getAvgFps1000();
486     f=1000./f;
487     f*=1000000;
488     videoIncrement=(uint64_t)f;
489 
490 
491 
492     ADM_info("avg fps=%u\n",vStream->getAvgFps1000());
493     uint64_t videoDuration=vStream->getVideoDuration();
494 
495     initUI(QT_TRANSLATE_NOOP("adm","Saving"));
496     encoding->setContainer(getContainerName());
497     MuxAudioPacket *audioPackets=new MuxAudioPacket[nbAStreams];
498     for(int i=0;i<nbAStreams;i++) // ugly...
499         audioPackets[i].clock=new audioClock(aStreams[i]->getInfo()->frequency);
500     ADMBitstream out(bufSize);
501     out.data=buffer;
502 
503     while(gotVideoPacket)
504     {
505         gotVideoPacket=vStream->getPacket(&out);
506         uint64_t maxAudioDts;
507         if(gotVideoPacket)
508         {
509             encoding->refresh();
510             if(!encoding->isAlive())
511             {
512                 result=false;
513                 break;
514             }
515             int64_t xpts=(int64_t)out.pts;
516             int64_t xdts=(int64_t)out.dts;
517             if(out.pts==ADM_NO_PTS) xpts=-1;
518             if(out.dts==ADM_NO_PTS) xdts=-1;
519             aprintf("[FF:V] Pts: %" PRId64" DTS:%" PRId64" ms\n",xpts/1000,xdts/1000);
520 
521             aprintf("[FF:V] LastDts:%08" PRIu64" Dts:%08" PRIu64" (%04" PRIu64") Delta : %" PRIu64"\n",
522                 lastVideoDts,out.dts,out.dts/1000000,out.dts-lastVideoDts);
523             rawDts=out.dts;
524             if(rawDts==ADM_NO_PTS)
525             {
526                 ADM_warning("No DTS information for frame %" PRIu32"\n",written);
527                 lastVideoDts+=videoIncrement;
528             }else if(lastVideoDts && rawDts<lastVideoDts)
529             {
530                 ADM_warning("Duplicated or going back DTS for frame %" PRIu32"\n",written);
531                 out.dts=lastVideoDts;
532             }else
533             {
534                 lastVideoDts=out.dts;
535             }
536             maxAudioDts=lastVideoDts+audioDelay;
537         }else
538         {
539             maxAudioDts=videoDuration;
540         }
541 
542         if(written)
543         {
544             // Now send audio until they all have DTS > lastVideoDts
545             for(int audio=0;audio<nbAStreams;audio++)
546             {
547                 MuxAudioPacket *audioTrack=&(audioPackets[audio]);
548                 ADM_audioStream*a=aStreams[audio];
549                 WAVHeader *info=a->getInfo();
550                 if(!info) // no more track
551                     continue;
552 
553                 while(1)
554                 {
555                     if(audioTrack->eof==true) break; // no more packet for this track
556                     if(audioTrack->present==false)
557                     {
558                         if(false==a->getPacket(audioTrack->buffer,
559                                             &(audioTrack->size),
560                                             AUDIO_BUFFER_SIZE,
561                                             &(audioTrack->samples),
562                                             &(audioTrack->dts)))
563                         {
564                             audioTrack->eof=true;
565                             ADM_info("No more audio packets for audio track %d\n",audio);
566                             break;
567                         }
568                        // printf("Track %d , new audio packet DTS=%"PRId64" size=%"PRIu32"\n",audioTrack->dts,audioTrack->size);
569                         audioTrack->present=true;
570                         // Delay audio by the delay induce by encoder
571                         if(audioTrack->dts!=ADM_NO_PTS) audioTrack->dts+=audioDelay;
572                     }
573                     if(audioTrack->dts!=ADM_NO_PTS)
574                     {
575                         //printf("Audio PTS:%"PRId64", limit=%"PRId64"\n",audioTrack->dts,lastVideoDts+videoIncrement);
576                         if(audioTrack->dts>maxAudioDts) break; // This packet is in the future
577                     }
578                     // Write...
579                     AVPacket pkt;
580                     uint64_t rescaledDts;
581                     rescaledDts=audioTrack->dts;
582                     if(rescaledDts==ADM_NO_PTS)
583                         rescaledDts=audioTrack->clock->getTimeUs(); // we assume the 1st one has a PTS/DTS..., can we ?
584                     else
585                         audioTrack->clock->setTimeUs(rescaledDts);
586                     audioTrack->clock->advanceBySample(audioTrack->samples);
587                     encoding->pushAudioFrame(audioTrack->size);
588                     muxerRescaleAudioTime(audio,&rescaledDts,a->getInfo()->frequency);
589                    //printf("[FF] A: Video frame  %d, audio Dts :%"PRIu64" size :%"PRIu32" nbSample : %"PRIu32" rescaled:%"PRIu64"\n",
590                      //               written,audioTrack->dts,audioTrack->size,audioTrack->samples,rescaledDts);
591                     av_init_packet(&pkt);
592 
593                     pkt.dts=rescaledDts;
594                     pkt.pts=rescaledDts;
595                     pkt.stream_index=1+audio;
596                     pkt.data= audioTrack->buffer;
597                     pkt.size= audioTrack->size;
598                     pkt.flags |= AV_PKT_FLAG_KEY; // Assume all audio are keyframe, which is slightly wrong
599                     ret =writePacket( &pkt);
600                     audioTrack->present=false; // consumed
601                     if(false==ret)
602                     {
603                         ADM_warning("[FF]Error writing audio packet\n");
604                         break;
605                     }
606                    // printf("[FF] A:%"PRIu32" ms vs V: %"PRIu32" ms\n",(uint32_t)audioTrack->dts/1000,(uint32_t)(lastVideoDts+videoIncrement)/1000);
607                 }
608             }
609         }
610 
611         if(!gotVideoPacket) break;
612 
613         if(out.pts==ADM_NO_PTS)
614         {
615             ADM_warning("No PTS information for frame %" PRIu32"\n",written);
616             missingPts++;
617             out.pts=lastVideoDts;
618         }
619         if(out.pts<lastVideoDts)
620         {
621             ADM_warning("Bumping PTS to keep PTS >= DTS for frame %" PRIu32"\n",written);
622             out.pts=lastVideoDts;
623         }
624 
625         encoding->pushVideoFrame(out.len,out.out_quantizer,lastVideoDts);
626         muxerRescaleVideoTimeDts(&(out.dts),lastVideoDts);
627         if(!roundup && lastVideoDtsLav && out.dts==lastVideoDtsLav)
628         {
629             ADM_warning("Bumping lav DTS to avoid collision for frame %" PRIu32"\n",written);
630             out.dts++;
631         }
632         lastVideoDtsLav=out.dts;
633         muxerRescaleVideoTime(&(out.pts));
634         if(out.dts>out.pts)
635         {
636             ADM_warning("Bumping lav PTS to keep PTS >= DTS for frame %" PRIu32"\n",written);
637             out.pts=out.dts;
638         }
639         aprintf("[FF:V] RawDts:%lu Scaled Dts:%lu\n",rawDts,out.dts);
640         aprintf("[FF:V] Rescaled: Len : %d flags:%x Pts:%" PRIu64" Dts:%" PRIu64"\n",out.len,out.flags,out.pts,out.dts);
641 
642         AVPacket pkt;
643 
644         av_init_packet(&pkt);
645         pkt.dts=out.dts;
646         if(vStream->providePts()==true)
647         {
648             pkt.pts=out.pts;
649         }else
650         {
651             pkt.pts=pkt.dts;
652         }
653         pkt.stream_index=0;
654         pkt.data = buffer;
655         pkt.size = out.len;
656         if(out.flags & 0x10) // FIXME AVI_KEY_FRAME
657             pkt.flags |= AV_PKT_FLAG_KEY;
658         ret = writePacket( &pkt);
659         aprintf("[FF]Frame:%u, DTS=%08lu PTS=%08lu\n",written,out.dts,out.pts);
660         if(false==ret)
661         {
662             printf("[FF]Error writing video packet\n");
663             break;
664         }
665         if(!written)
666         {
667             audioDelay=vStream->getVideoDelay();
668             printf("[muxerFFmpeg::initVideo] Final audio delay: %" PRIu64" ms\n",audioDelay/1000);
669         }
670         written++;
671     }
672     delete [] buffer;
673     if(false==ret)
674     {
675         char msg[512+1];
676         snprintf(msg,512,QT_TRANSLATE_NOOP("adm",
677             "The saved video is incomplete. "
678             "The error occured at %s (%d\%). "
679             "This may happen as result of invalid time stamps in the video."),
680             ADM_us2plain(lastVideoDts),
681             (int)(lastVideoDts*100/videoDuration));
682         GUI_Error_HIG(QT_TRANSLATE_NOOP("adm","Too short"),msg);
683         result=false;
684     }
685     ADM_info("[FF] Wrote %d frames, nb audio streams %d\n",written,nbAStreams);
686     ADM_info("[FF] Found %d missing PTS / %d total frames\n",missingPts,written);
687     delete [] audioPackets;
688     audioPackets=NULL;
689     return result;
690 }
691 // EOF
692