Stop using static initialisation so that dcpomatic::write() can be called more than...
[dcpomatic.git] / src / lib / ffmpeg_decoder.cc
index e5685f6611d60665367b46b97e5f66a06634d113..82eccb576d5df8248926315b72f6748406e9daaf 100644 (file)
@@ -93,8 +93,10 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> film, shared_ptr<const FFmp
        }
 
        if (c->only_text()) {
-               /* XXX: this time here should be the time of the first subtitle, not 0 */
-               text.push_back (make_shared<TextDecoder>(this, c->only_text(), ContentTime()));
+               text.push_back (make_shared<TextDecoder>(this, c->only_text()));
+               /* XXX: we should be calling maybe_set_position() on this TextDecoder, but we can't easily find
+                * the time of the first subtitle at this point.
+                */
        }
 
        for (auto i: c->ffmpeg_audio_streams()) {
@@ -144,7 +146,7 @@ FFmpegDecoder::flush ()
                auto const f = full_length.frames_round (vfr);
                auto v = video->position(film()).get_value_or(ContentTime()).frames_round(vfr) + 1;
                while (v < f) {
-                       video->emit (film(), shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)), v);
+                       video->emit (film(), make_shared<const RawImageProxy>(_black_image), v);
                        ++v;
                }
        }
@@ -533,23 +535,38 @@ FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet)
 
        auto context = video_codec_context();
 
-       int r = avcodec_send_packet (context, packet);
-       if (r < 0) {
-               LOG_WARNING("avcodec_send_packet returned %1 for a video packet", r);
-       }
+       bool pending = false;
+       do {
+               int r = avcodec_send_packet (context, packet);
+               if (r < 0) {
+                       LOG_WARNING("avcodec_send_packet returned %1 for a video packet", r);
+               }
 
-       r = avcodec_receive_frame (context, _video_frame);
-       if (r == AVERROR(EAGAIN) || r == AVERROR_EOF || (r < 0 && !packet)) {
-               /* More input is required, no more frames are coming, or we are flushing and there was
-                * some error which we just want to ignore.
-                */
-               return false;
-       } else if (r < 0) {
-               throw DecodeError (N_("avcodec_receive_frame"), N_("FFmpeg::decode_and_process_video_packet"), r);
-       }
+               /* EAGAIN means we should call avcodec_receive_frame and then re-send the same packet */
+               pending = r == AVERROR(EAGAIN);
+
+               while (true) {
+                       r = avcodec_receive_frame (context, _video_frame);
+                       if (r == AVERROR(EAGAIN) || r == AVERROR_EOF || (r < 0 && !packet)) {
+                               /* More input is required, no more frames are coming, or we are flushing and there was
+                                * some error which we just want to ignore.
+                                */
+                               return false;
+                       } else if (r < 0) {
+                               throw DecodeError (N_("avcodec_receive_frame"), N_("FFmpeg::decode_and_process_video_packet"), r);
+                       }
 
-       /* We assume we'll only get one frame here, which I think is safe */
+                       process_video_frame ();
+               }
+       } while (pending);
 
+       return true;
+}
+
+
+void
+FFmpegDecoder::process_video_frame ()
+{
        boost::mutex::scoped_lock lm (_filter_graphs_mutex);
 
        shared_ptr<VideoFilterGraph> graph;
@@ -587,8 +604,6 @@ FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet)
                        LOG_WARNING_NC ("Dropping frame without PTS");
                }
        }
-
-       return true;
 }
 
 
@@ -631,6 +646,7 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet)
                _have_current_subtitle = true;
        }
 
+       ContentBitmapText bitmap_text(from);
        for (unsigned int i = 0; i < sub.num_rects; ++i) {
                auto const rect = sub.rects[i];
 
@@ -638,7 +654,7 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet)
                case SUBTITLE_NONE:
                        break;
                case SUBTITLE_BITMAP:
-                       process_bitmap_subtitle (rect, from);
+                       bitmap_text.subs.push_back(process_bitmap_subtitle(rect));
                        break;
                case SUBTITLE_TEXT:
                        cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n";
@@ -649,6 +665,10 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet)
                }
        }
 
+       if (!bitmap_text.subs.empty()) {
+               only_text()->emit_bitmap_start(bitmap_text);
+       }
+
        if (_current_subtitle_to) {
                only_text()->emit_stop (*_current_subtitle_to);
        }
@@ -657,8 +677,8 @@ FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet)
 }
 
 
-void
-FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from)
+BitmapText
+FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect)
 {
        /* Note BGRA is expressed little-endian, so the first byte in the word is B, second
           G, third R, fourth A.
@@ -741,7 +761,7 @@ FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime
                static_cast<double>(rect->h) / target_height
                );
 
-       only_text()->emit_bitmap_start (from, image, scaled_rect);
+       return { image, scaled_rect };
 }