Merge.
[dcpomatic.git] / src / lib / ffmpeg_decoder.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /** @file  src/ffmpeg_decoder.cc
21  *  @brief A decoder using FFmpeg to decode content.
22  */
23
24 #include <stdexcept>
25 #include <vector>
26 #include <sstream>
27 #include <iomanip>
28 #include <iostream>
29 #include <stdint.h>
30 #include <boost/lexical_cast.hpp>
31 extern "C" {
32 #include <tiffio.h>
33 #include <libavcodec/avcodec.h>
34 #include <libavformat/avformat.h>
35 #include <libswscale/swscale.h>
36 #include <libpostproc/postprocess.h>
37 }
38 #include <sndfile.h>
39 #include "film.h"
40 #include "format.h"
41 #include "transcoder.h"
42 #include "job.h"
43 #include "filter.h"
44 #include "options.h"
45 #include "exceptions.h"
46 #include "image.h"
47 #include "util.h"
48 #include "log.h"
49 #include "ffmpeg_decoder.h"
50 #include "filter_graph.h"
51 #include "subtitle.h"
52
53 #include "i18n.h"
54
55 using std::cout;
56 using std::string;
57 using std::vector;
58 using std::stringstream;
59 using std::list;
60 using boost::shared_ptr;
61 using boost::optional;
62 using boost::dynamic_pointer_cast;
63 using libdcp::Size;
64
65 FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, DecodeOptions o)
66         : Decoder (f, o)
67         , VideoDecoder (f, o)
68         , AudioDecoder (f, o)
69         , _format_context (0)
70         , _video_stream (-1)
71         , _frame (0)
72         , _video_codec_context (0)
73         , _video_codec (0)
74         , _audio_codec_context (0)
75         , _audio_codec (0)
76         , _subtitle_codec_context (0)
77         , _subtitle_codec (0)
78 {
79         setup_general ();
80         setup_video ();
81         setup_audio ();
82         setup_subtitle ();
83
84         if (!o.video_sync) {
85                 _first_video = 0;
86         }
87 }
88
89 FFmpegDecoder::~FFmpegDecoder ()
90 {
91         if (_audio_codec_context) {
92                 avcodec_close (_audio_codec_context);
93         }
94         
95         if (_video_codec_context) {
96                 avcodec_close (_video_codec_context);
97         }
98
99         if (_subtitle_codec_context) {
100                 avcodec_close (_subtitle_codec_context);
101         }
102
103         av_free (_frame);
104         
105         avformat_close_input (&_format_context);
106 }       
107
108 void
109 FFmpegDecoder::setup_general ()
110 {
111         av_register_all ();
112
113         if (avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0) < 0) {
114                 throw OpenFileError (_film->content_path ());
115         }
116
117         if (avformat_find_stream_info (_format_context, 0) < 0) {
118                 throw DecodeError (_("could not find stream information"));
119         }
120
121         /* Find video, audio and subtitle streams and choose the first of each */
122
123         for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
124                 AVStream* s = _format_context->streams[i];
125                 if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
126                         _video_stream = i;
127                 } else if (s->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
128
129                         /* This is a hack; sometimes it seems that _audio_codec_context->channel_layout isn't set up,
130                            so bodge it here.  No idea why we should have to do this.
131                         */
132
133                         if (s->codec->channel_layout == 0) {
134                                 s->codec->channel_layout = av_get_default_channel_layout (s->codec->channels);
135                         }
136                         
137                         _audio_streams.push_back (
138                                 shared_ptr<AudioStream> (
139                                         new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channel_layout)
140                                         )
141                                 );
142                         
143                 } else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
144                         _subtitle_streams.push_back (
145                                 shared_ptr<SubtitleStream> (
146                                         new SubtitleStream (stream_name (s), i)
147                                         )
148                                 );
149                 }
150         }
151
152         if (_video_stream < 0) {
153                 throw DecodeError (N_("could not find video stream"));
154         }
155
156         _frame = avcodec_alloc_frame ();
157         if (_frame == 0) {
158                 throw DecodeError (N_("could not allocate frame"));
159         }
160 }
161
162 void
163 FFmpegDecoder::setup_video ()
164 {
165         _video_codec_context = _format_context->streams[_video_stream]->codec;
166         _video_codec = avcodec_find_decoder (_video_codec_context->codec_id);
167
168         if (_video_codec == 0) {
169                 throw DecodeError (_("could not find video decoder"));
170         }
171
172         if (avcodec_open2 (_video_codec_context, _video_codec, 0) < 0) {
173                 throw DecodeError (N_("could not open video decoder"));
174         }
175 }
176
177 void
178 FFmpegDecoder::setup_audio ()
179 {
180         if (!_audio_stream) {
181                 return;
182         }
183
184         shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
185         assert (ffa);
186         
187         _audio_codec_context = _format_context->streams[ffa->id()]->codec;
188         _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id);
189
190         if (_audio_codec == 0) {
191                 throw DecodeError (_("could not find audio decoder"));
192         }
193
194         if (avcodec_open2 (_audio_codec_context, _audio_codec, 0) < 0) {
195                 throw DecodeError (N_("could not open audio decoder"));
196         }
197 }
198
199 void
200 FFmpegDecoder::setup_subtitle ()
201 {
202         if (!_subtitle_stream || _subtitle_stream->id() >= int (_format_context->nb_streams)) {
203                 return;
204         }
205
206         _subtitle_codec_context = _format_context->streams[_subtitle_stream->id()]->codec;
207         _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
208
209         if (_subtitle_codec == 0) {
210                 throw DecodeError (_("could not find subtitle decoder"));
211         }
212         
213         if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
214                 throw DecodeError (N_("could not open subtitle decoder"));
215         }
216 }
217
218
219 bool
220 FFmpegDecoder::pass ()
221 {
222         int r = av_read_frame (_format_context, &_packet);
223         
224         if (r < 0) {
225                 if (r != AVERROR_EOF) {
226                         /* Maybe we should fail here, but for now we'll just finish off instead */
227                         char buf[256];
228                         av_strerror (r, buf, sizeof(buf));
229                         _film->log()->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r));
230                 }
231                 
232                 /* Get any remaining frames */
233                 
234                 _packet.data = 0;
235                 _packet.size = 0;
236
237                 /* XXX: should we reset _packet.data and size after each *_decode_* call? */
238
239                 int frame_finished;
240
241                 if (_opt.decode_video) {
242                         while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
243                                 filter_and_emit_video (_frame);
244                         }
245                 }
246
247                 if (_audio_stream && _opt.decode_audio) {
248                         while (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
249                                 int const data_size = av_samples_get_buffer_size (
250                                         0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
251                                         );
252
253                                 assert (_audio_codec_context->channels == _film->audio_channels());
254                                 Audio (deinterleave_audio (_frame->data, data_size));
255                         }
256                 }
257
258                 return true;
259         }
260
261         avcodec_get_frame_defaults (_frame);
262
263         shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
264
265         if (_packet.stream_index == _video_stream && _opt.decode_video) {
266
267                 int frame_finished;
268                 int const r = avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet);
269                 if (r >= 0 && frame_finished) {
270
271                         if (r != _packet.size) {
272                                 _film->log()->log (String::compose (N_("Used only %1 bytes of %2 in packet"), r, _packet.size));
273                         }
274
275                         if (_opt.video_sync) {
276                                 out_with_sync ();
277                         } else {
278                                 filter_and_emit_video (_frame);
279                         }
280                 }
281
282         } else if (ffa && _packet.stream_index == ffa->id() && _opt.decode_audio) {
283
284                 int frame_finished;
285                 if (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
286
287                         /* Where we are in the source, in seconds */
288                         double const source_pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base)
289                                 * av_frame_get_best_effort_timestamp(_frame);
290
291                         /* We only decode audio if we've had our first video packet through, and if it
292                            was before this packet.  Until then audio is thrown away.
293                         */
294                                 
295                         if ((_first_video && _first_video.get() <= source_pts_seconds) || !_opt.decode_video) {
296
297                                 if (!_first_audio && _opt.decode_video) {
298                                         _first_audio = source_pts_seconds;
299                                         
300                                         /* This is our first audio frame, and if we've arrived here we must have had our
301                                            first video frame.  Push some silence to make up any gap between our first
302                                            video frame and our first audio.
303                                         */
304                         
305                                         /* frames of silence that we must push */
306                                         int const s = rint ((_first_audio.get() - _first_video.get()) * ffa->sample_rate ());
307                                         
308                                         _film->log()->log (
309                                                 String::compose (
310                                                         N_("First video at %1, first audio at %2, pushing %3 audio frames of silence for %4 channels (%5 bytes per sample)"),
311                                                         _first_video.get(), _first_audio.get(), s, ffa->channels(), bytes_per_audio_sample()
312                                                         )
313                                                 );
314                                         
315                                         if (s) {
316                                                 shared_ptr<AudioBuffers> audio (new AudioBuffers (ffa->channels(), s));
317                                                 audio->make_silent ();
318                                                 Audio (audio);
319                                         }
320                                 }
321
322                                 int const data_size = av_samples_get_buffer_size (
323                                         0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
324                                         );
325                                 
326                                 assert (_audio_codec_context->channels == _film->audio_channels());
327                                 Audio (deinterleave_audio (_frame->data, data_size));
328                         }
329                 }
330                         
331         } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt.decode_subtitles && _first_video) {
332
333                 int got_subtitle;
334                 AVSubtitle sub;
335                 if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) {
336                         /* Sometimes we get an empty AVSubtitle, which is used by some codecs to
337                            indicate that the previous subtitle should stop.
338                         */
339                         if (sub.num_rects > 0) {
340                                 shared_ptr<TimedSubtitle> ts;
341                                 try {
342                                         emit_subtitle (shared_ptr<TimedSubtitle> (new TimedSubtitle (sub)));
343                                 } catch (...) {
344                                         /* some problem with the subtitle; we probably didn't understand it */
345                                 }
346                         } else {
347                                 emit_subtitle (shared_ptr<TimedSubtitle> ());
348                         }
349                         avsubtitle_free (&sub);
350                 }
351         }
352         
353         av_free_packet (&_packet);
354         return false;
355 }
356
357 /** @param data pointer to array of pointers to buffers.
358  *  Only the first buffer will be used for non-planar data, otherwise there will be one per channel.
359  */
360 shared_ptr<AudioBuffers>
361 FFmpegDecoder::deinterleave_audio (uint8_t** data, int size)
362 {
363         assert (_film->audio_channels());
364         assert (bytes_per_audio_sample());
365
366         shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
367         assert (ffa);
368         
369         /* Deinterleave and convert to float */
370
371         assert ((size % (bytes_per_audio_sample() * ffa->channels())) == 0);
372
373         int const total_samples = size / bytes_per_audio_sample();
374         int const frames = total_samples / _film->audio_channels();
375         shared_ptr<AudioBuffers> audio (new AudioBuffers (ffa->channels(), frames));
376
377         switch (audio_sample_format()) {
378         case AV_SAMPLE_FMT_S16:
379         {
380                 int16_t* p = reinterpret_cast<int16_t *> (data[0]);
381                 int sample = 0;
382                 int channel = 0;
383                 for (int i = 0; i < total_samples; ++i) {
384                         audio->data(channel)[sample] = float(*p++) / (1 << 15);
385
386                         ++channel;
387                         if (channel == _film->audio_channels()) {
388                                 channel = 0;
389                                 ++sample;
390                         }
391                 }
392         }
393         break;
394
395         case AV_SAMPLE_FMT_S16P:
396         {
397                 int16_t** p = reinterpret_cast<int16_t **> (data);
398                 for (int i = 0; i < _film->audio_channels(); ++i) {
399                         for (int j = 0; j < frames; ++j) {
400                                 audio->data(i)[j] = static_cast<float>(p[i][j]) / (1 << 15);
401                         }
402                 }
403         }
404         break;
405         
406         case AV_SAMPLE_FMT_S32:
407         {
408                 int32_t* p = reinterpret_cast<int32_t *> (data[0]);
409                 int sample = 0;
410                 int channel = 0;
411                 for (int i = 0; i < total_samples; ++i) {
412                         audio->data(channel)[sample] = static_cast<float>(*p++) / (1 << 31);
413
414                         ++channel;
415                         if (channel == _film->audio_channels()) {
416                                 channel = 0;
417                                 ++sample;
418                         }
419                 }
420         }
421         break;
422
423         case AV_SAMPLE_FMT_FLT:
424         {
425                 float* p = reinterpret_cast<float*> (data[0]);
426                 int sample = 0;
427                 int channel = 0;
428                 for (int i = 0; i < total_samples; ++i) {
429                         audio->data(channel)[sample] = *p++;
430
431                         ++channel;
432                         if (channel == _film->audio_channels()) {
433                                 channel = 0;
434                                 ++sample;
435                         }
436                 }
437         }
438         break;
439                 
440         case AV_SAMPLE_FMT_FLTP:
441         {
442                 float** p = reinterpret_cast<float**> (data);
443                 for (int i = 0; i < _film->audio_channels(); ++i) {
444                         memcpy (audio->data(i), p[i], frames * sizeof(float));
445                 }
446         }
447         break;
448
449         default:
450                 throw DecodeError (String::compose (_("Unrecognised audio sample format (%1)"), static_cast<int> (audio_sample_format())));
451         }
452
453         return audio;
454 }
455
456 float
457 FFmpegDecoder::frames_per_second () const
458 {
459         AVStream* s = _format_context->streams[_video_stream];
460
461         if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
462                 return av_q2d (s->avg_frame_rate);
463         }
464
465         return av_q2d (s->r_frame_rate);
466 }
467
468 AVSampleFormat
469 FFmpegDecoder::audio_sample_format () const
470 {
471         if (_audio_codec_context == 0) {
472                 return (AVSampleFormat) 0;
473         }
474         
475         return _audio_codec_context->sample_fmt;
476 }
477
478 libdcp::Size
479 FFmpegDecoder::native_size () const
480 {
481         return libdcp::Size (_video_codec_context->width, _video_codec_context->height);
482 }
483
484 PixelFormat
485 FFmpegDecoder::pixel_format () const
486 {
487         return _video_codec_context->pix_fmt;
488 }
489
490 int
491 FFmpegDecoder::time_base_numerator () const
492 {
493         return _video_codec_context->time_base.num;
494 }
495
496 int
497 FFmpegDecoder::time_base_denominator () const
498 {
499         return _video_codec_context->time_base.den;
500 }
501
502 int
503 FFmpegDecoder::sample_aspect_ratio_numerator () const
504 {
505         return _video_codec_context->sample_aspect_ratio.num;
506 }
507
508 int
509 FFmpegDecoder::sample_aspect_ratio_denominator () const
510 {
511         return _video_codec_context->sample_aspect_ratio.den;
512 }
513
514 string
515 FFmpegDecoder::stream_name (AVStream* s) const
516 {
517         stringstream n;
518         
519         AVDictionaryEntry const * lang = av_dict_get (s->metadata, N_("language"), 0, 0);
520         if (lang) {
521                 n << lang->value;
522         }
523         
524         AVDictionaryEntry const * title = av_dict_get (s->metadata, N_("title"), 0, 0);
525         if (title) {
526                 if (!n.str().empty()) {
527                         n << N_(" ");
528                 }
529                 n << title->value;
530         }
531
532         if (n.str().empty()) {
533                 n << N_("unknown");
534         }
535
536         return n.str ();
537 }
538
539 int
540 FFmpegDecoder::bytes_per_audio_sample () const
541 {
542         return av_get_bytes_per_sample (audio_sample_format ());
543 }
544
545 void
546 FFmpegDecoder::set_audio_stream (shared_ptr<AudioStream> s)
547 {
548         AudioDecoder::set_audio_stream (s);
549         setup_audio ();
550 }
551
552 void
553 FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s)
554 {
555         VideoDecoder::set_subtitle_stream (s);
556         setup_subtitle ();
557         OutputChanged ();
558 }
559
560 void
561 FFmpegDecoder::filter_and_emit_video (AVFrame* frame)
562 {
563         boost::mutex::scoped_lock lm (_filter_graphs_mutex);
564         
565         shared_ptr<FilterGraph> graph;
566
567         list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
568         while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format)) {
569                 ++i;
570         }
571
572         if (i == _filter_graphs.end ()) {
573                 graph.reset (new FilterGraph (_film, this, libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format));
574                 _filter_graphs.push_back (graph);
575                 _film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), frame->width, frame->height, frame->format));
576         } else {
577                 graph = *i;
578         }
579
580         list<shared_ptr<Image> > images = graph->process (frame);
581
582         for (list<shared_ptr<Image> >::iterator i = images.begin(); i != images.end(); ++i) {
583                 emit_video (*i, frame_time ());
584         }
585 }
586
587 bool
588 FFmpegDecoder::seek (double p)
589 {
590         return do_seek (p, false);
591 }
592
593 bool
594 FFmpegDecoder::seek_to_last ()
595 {
596         /* This AVSEEK_FLAG_BACKWARD in do_seek is a bit of a hack; without it, if we ask for a seek to the same place as last time
597            (used when we change decoder parameters and want to re-fetch the frame) we end up going forwards rather than
598            staying in the same place.
599         */
600         return do_seek (last_source_time(), true);
601 }
602
603 bool
604 FFmpegDecoder::do_seek (double p, bool backwards)
605 {
606         int64_t const vt = p / av_q2d (_format_context->streams[_video_stream]->time_base);
607
608         int const r = av_seek_frame (_format_context, _video_stream, vt, backwards ? AVSEEK_FLAG_BACKWARD : 0);
609         
610         avcodec_flush_buffers (_video_codec_context);
611         if (_subtitle_codec_context) {
612                 avcodec_flush_buffers (_subtitle_codec_context);
613         }
614         
615         return r < 0;
616 }
617
618 shared_ptr<FFmpegAudioStream>
619 FFmpegAudioStream::create (string t, optional<int> v)
620 {
621         if (!v) {
622                 /* version < 1; no type in the string, and there's only FFmpeg streams anyway */
623                 return shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (t, v));
624         }
625
626         stringstream s (t);
627         string type;
628         s >> type;
629         if (type != N_("ffmpeg")) {
630                 return shared_ptr<FFmpegAudioStream> ();
631         }
632
633         return shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (t, v));
634 }
635
636 FFmpegAudioStream::FFmpegAudioStream (string t, optional<int> version)
637 {
638         stringstream n (t);
639         
640         int name_index = 4;
641         if (!version) {
642                 name_index = 2;
643                 int channels;
644                 n >> _id >> channels;
645                 _channel_layout = av_get_default_channel_layout (channels);
646                 _sample_rate = 0;
647         } else {
648                 string type;
649                 /* Current (marked version 1) */
650                 n >> type >> _id >> _sample_rate >> _channel_layout;
651                 assert (type == N_("ffmpeg"));
652         }
653
654         for (int i = 0; i < name_index; ++i) {
655                 size_t const s = t.find (' ');
656                 if (s != string::npos) {
657                         t = t.substr (s + 1);
658                 }
659         }
660
661         _name = t;
662 }
663
664 string
665 FFmpegAudioStream::to_string () const
666 {
667         return String::compose (N_("ffmpeg %1 %2 %3 %4"), _id, _sample_rate, _channel_layout, _name);
668 }
669
670 void
671 FFmpegDecoder::out_with_sync ()
672 {
673         /* Where we are in the output, in seconds */
674         double const out_pts_seconds = video_frame() / frames_per_second();
675         
676         /* Where we are in the source, in seconds */
677         double const source_pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base)
678                 * av_frame_get_best_effort_timestamp(_frame);
679         
680         _film->log()->log (
681                 String::compose (N_("Source video frame ready; source at %1, output at %2"), source_pts_seconds, out_pts_seconds),
682                 Log::VERBOSE
683                 );
684         
685         if (!_first_video) {
686                 _first_video = source_pts_seconds;
687         }
688         
689         /* Difference between where we are and where we should be */
690         double const delta = source_pts_seconds - _first_video.get() - out_pts_seconds;
691         double const one_frame = 1 / frames_per_second();
692         
693         /* Insert frames if required to get out_pts_seconds up to pts_seconds */
694         if (delta > one_frame) {
695                 int const extra = rint (delta / one_frame);
696                 for (int i = 0; i < extra; ++i) {
697                         repeat_last_video ();
698                         _film->log()->log (
699                                 String::compose (
700                                         N_("Extra video frame inserted at %1s; source frame %2, source PTS %3 (at %4 fps)"),
701                                         out_pts_seconds, video_frame(), source_pts_seconds, frames_per_second()
702                                         )
703                                 );
704                 }
705         }
706         
707         if (delta > -one_frame) {
708                 /* Process this frame */
709                 filter_and_emit_video (_frame);
710         } else {
711                 /* Otherwise we are omitting a frame to keep things right */
712                 _film->log()->log (String::compose (N_("Frame removed at %1s"), out_pts_seconds));
713         }
714 }
715
716 void
717 FFmpegDecoder::film_changed (Film::Property p)
718 {
719         switch (p) {
720         case Film::CROP:
721         case Film::FILTERS:
722         {
723                 boost::mutex::scoped_lock lm (_filter_graphs_mutex);
724                 _filter_graphs.clear ();
725         }
726         OutputChanged ();
727         break;
728
729         default:
730                 break;
731         }
732 }
733
734 /** @return Length (in video frames) according to our content's header */
735 SourceFrame
736 FFmpegDecoder::length () const
737 {
738         return (double(_format_context->duration) / AV_TIME_BASE) * frames_per_second();
739 }
740
741 double
742 FFmpegDecoder::frame_time () const
743 {
744         return av_frame_get_best_effort_timestamp(_frame) * av_q2d (_format_context->streams[_video_stream]->time_base);
745 }
746