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