Cleanup: move some stuff into process_video_frame().
[dcpomatic.git] / src / lib / ffmpeg_decoder.cc
index ecbd8d00931be7e4505417ce3b607ad87b962dbc..f64ccbd3b6305249c4522ee69d175dee6bb26849 100644 (file)
 
 */
 
+
 /** @file  src/ffmpeg_decoder.cc
  *  @brief A decoder using FFmpeg to decode content.
  */
 
-#include "filter.h"
-#include "exceptions.h"
-#include "image.h"
-#include "util.h"
-#include "log.h"
+
+#include "audio_buffers.h"
+#include "audio_content.h"
+#include "audio_decoder.h"
+#include "compose.hpp"
 #include "dcpomatic_log.h"
-#include "ffmpeg_decoder.h"
-#include "text_decoder.h"
+#include "exceptions.h"
 #include "ffmpeg_audio_stream.h"
-#include "ffmpeg_subtitle_stream.h"
-#include "video_filter_graph.h"
-#include "audio_buffers.h"
 #include "ffmpeg_content.h"
-#include "raw_image_proxy.h"
-#include "video_decoder.h"
+#include "ffmpeg_decoder.h"
+#include "ffmpeg_subtitle_stream.h"
 #include "film.h"
-#include "audio_decoder.h"
-#include "compose.hpp"
-#include "text_content.h"
-#include "audio_content.h"
+#include "filter.h"
 #include "frame_interval_checker.h"
+#include "image.h"
+#include "log.h"
+#include "raw_image_proxy.h"
+#include "text_content.h"
+#include "text_decoder.h"
+#include "util.h"
+#include "video_decoder.h"
+#include "video_filter_graph.h"
 #include <dcp/subtitle_string.h>
 #include <sub/ssa_reader.h>
 #include <sub/subtitle.h>
@@ -52,27 +54,22 @@ extern "C" {
 #include <libavformat/avformat.h>
 }
 #include <boost/algorithm/string.hpp>
-#include <vector>
 #include <iomanip>
 #include <iostream>
+#include <vector>
 #include <stdint.h>
 
 #include "i18n.h"
 
+
 using std::cout;
-using std::string;
-using std::vector;
-using std::list;
+using std::dynamic_pointer_cast;
+using std::make_shared;
 using std::min;
-using std::pair;
-using std::max;
-using std::map;
 using std::shared_ptr;
-using std::make_shared;
-using boost::is_any_of;
-using boost::split;
+using std::string;
+using std::vector;
 using boost::optional;
-using std::dynamic_pointer_cast;
 using dcp::Size;
 using namespace dcpomatic;
 
@@ -80,13 +77,12 @@ using namespace dcpomatic;
 FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> film, shared_ptr<const FFmpegContent> c, bool fast)
        : FFmpeg (c)
        , Decoder (film)
-       , _have_current_subtitle (false)
 {
        if (c->video && c->video->use()) {
                video = make_shared<VideoDecoder>(this, c);
                _pts_offset = pts_offset (c->ffmpeg_audio_streams(), c->first_video(), c->active_video_frame_rate(film));
                /* It doesn't matter what size or pixel format this is, it just needs to be black */
-               _black_image.reset (new Image (AV_PIX_FMT_RGB24, dcp::Size (128, 128), true));
+               _black_image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size (128, 128), Image::Alignment::PADDED);
                _black_image->make_black ();
        } else {
                _pts_offset = {};
@@ -101,24 +97,41 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> film, shared_ptr<const FFmp
                text.push_back (make_shared<TextDecoder>(this, c->only_text(), ContentTime()));
        }
 
-       _next_time.resize (_format_context->nb_streams);
+       for (auto i: c->ffmpeg_audio_streams()) {
+               _next_time[i] = boost::optional<dcpomatic::ContentTime>();
+       }
 }
 
 
-void
+bool
 FFmpegDecoder::flush ()
 {
-       /* Get any remaining frames */
+       /* Flush video and audio once */
 
-       _packet.data = 0;
-       _packet.size = 0;
-
-       /* XXX: should we reset _packet.data and size after each *_decode_* call? */
+       bool did_something = false;
+       if (video) {
+               if (decode_and_process_video_packet(nullptr)) {
+                       did_something = true;
+               }
+       }
 
-       while (video && decode_video_packet()) {}
+       for (auto i: ffmpeg_content()->ffmpeg_audio_streams()) {
+               auto context = _codec_context[i->index(_format_context)];
+               int r = avcodec_send_packet (context, nullptr);
+               if (r < 0 && r != AVERROR_EOF) {
+                       /* EOF can happen if we've already sent a flush packet */
+                       throw DecodeError (N_("avcodec_send_packet"), N_("FFmpegDecoder::flush"), r);
+               }
+               r = avcodec_receive_frame (context, audio_frame(i));
+               if (r >= 0) {
+                       process_audio_frame (i);
+                       did_something = true;
+               }
+       }
 
-       if (audio) {
-               decode_audio_packet ();
+       if (did_something) {
+               /* We want to be called again */
+               return false;
        }
 
        /* Make sure all streams are the same length and round up to the next video frame */
@@ -156,13 +169,18 @@ FFmpegDecoder::flush ()
        if (audio) {
                audio->flush ();
        }
+
+       return true;
 }
 
 
 bool
 FFmpegDecoder::pass ()
 {
-       int r = av_read_frame (_format_context, &_packet);
+       auto packet = av_packet_alloc();
+       DCPOMATIC_ASSERT (packet);
+
+       int r = av_read_frame (_format_context, packet);
 
        /* AVERROR_INVALIDDATA can apparently be returned sometimes even when av_read_frame
           has pretty-much succeeded (and hence generated data which should be processed).
@@ -176,22 +194,22 @@ FFmpegDecoder::pass ()
                        LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), &buf[0], r);
                }
 
-               flush ();
-               return true;
+               av_packet_free (&packet);
+               return flush ();
        }
 
-       int const si = _packet.stream_index;
+       int const si = packet->stream_index;
        auto fc = _ffmpeg_content;
 
        if (_video_stream && si == _video_stream.get() && video && !video->ignore()) {
-               decode_video_packet ();
+               decode_and_process_video_packet (packet);
        } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index(_format_context, si) && !only_text()->ignore()) {
-               decode_subtitle_packet ();
+               decode_and_process_subtitle_packet (packet);
        } else {
-               decode_audio_packet ();
+               decode_and_process_audio_packet (packet);
        }
 
-       av_packet_unref (&_packet);
+       av_packet_free (&packet);
        return false;
 }
 
@@ -200,34 +218,22 @@ FFmpegDecoder::pass ()
  *  Only the first buffer will be used for non-planar data, otherwise there will be one per channel.
  */
 shared_ptr<AudioBuffers>
-FFmpegDecoder::deinterleave_audio (shared_ptr<FFmpegAudioStream> stream) const
+FFmpegDecoder::deinterleave_audio (AVFrame* frame)
 {
-       DCPOMATIC_ASSERT (bytes_per_audio_sample (stream));
+       auto format = static_cast<AVSampleFormat>(frame->format);
 
-DCPOMATIC_DISABLE_WARNINGS
-       int const size = av_samples_get_buffer_size (
-               0, stream->stream(_format_context)->codec->channels, _frame->nb_samples, audio_sample_format (stream), 1
-               );
-DCPOMATIC_ENABLE_WARNINGS
-
-       /* XXX: can't we just use _frame->nb_samples directly here? */
        /* XXX: can't we use swr_convert() to do the format conversion? */
 
-       /* Deinterleave and convert to float */
-
-       /* total_samples and frames will be rounded down here, so if there are stray samples at the end
-          of the block that do not form a complete sample or frame they will be dropped.
-       */
-       int const total_samples = size / bytes_per_audio_sample (stream);
-       int const channels = stream->channels();
-       int const frames = total_samples / channels;
+       int const channels = frame->channels;
+       int const frames = frame->nb_samples;
+       int const total_samples = frames * channels;
        auto audio = make_shared<AudioBuffers>(channels, frames);
        auto data = audio->data();
 
-       switch (audio_sample_format (stream)) {
+       switch (format) {
        case AV_SAMPLE_FMT_U8:
        {
-               uint8_t* p = reinterpret_cast<uint8_t *> (_frame->data[0]);
+               auto p = reinterpret_cast<uint8_t *> (frame->data[0]);
                int sample = 0;
                int channel = 0;
                for (int i = 0; i < total_samples; ++i) {
@@ -244,7 +250,7 @@ DCPOMATIC_ENABLE_WARNINGS
 
        case AV_SAMPLE_FMT_S16:
        {
-               int16_t* p = reinterpret_cast<int16_t *> (_frame->data[0]);
+               auto p = reinterpret_cast<int16_t *> (frame->data[0]);
                int sample = 0;
                int channel = 0;
                for (int i = 0; i < total_samples; ++i) {
@@ -261,7 +267,7 @@ DCPOMATIC_ENABLE_WARNINGS
 
        case AV_SAMPLE_FMT_S16P:
        {
-               int16_t** p = reinterpret_cast<int16_t **> (_frame->data);
+               auto p = reinterpret_cast<int16_t **> (frame->data);
                for (int i = 0; i < channels; ++i) {
                        for (int j = 0; j < frames; ++j) {
                                data[i][j] = static_cast<float>(p[i][j]) / (1 << 15);
@@ -272,7 +278,7 @@ DCPOMATIC_ENABLE_WARNINGS
 
        case AV_SAMPLE_FMT_S32:
        {
-               int32_t* p = reinterpret_cast<int32_t *> (_frame->data[0]);
+               auto p = reinterpret_cast<int32_t *> (frame->data[0]);
                int sample = 0;
                int channel = 0;
                for (int i = 0; i < total_samples; ++i) {
@@ -289,7 +295,7 @@ DCPOMATIC_ENABLE_WARNINGS
 
        case AV_SAMPLE_FMT_S32P:
        {
-               int32_t** p = reinterpret_cast<int32_t **> (_frame->data);
+               auto p = reinterpret_cast<int32_t **> (frame->data);
                for (int i = 0; i < channels; ++i) {
                        for (int j = 0; j < frames; ++j) {
                                data[i][j] = static_cast<float>(p[i][j]) / 2147483648;
@@ -300,7 +306,7 @@ DCPOMATIC_ENABLE_WARNINGS
 
        case AV_SAMPLE_FMT_FLT:
        {
-               float* p = reinterpret_cast<float*> (_frame->data[0]);
+               auto p = reinterpret_cast<float*> (frame->data[0]);
                int sample = 0;
                int channel = 0;
                for (int i = 0; i < total_samples; ++i) {
@@ -317,20 +323,20 @@ DCPOMATIC_ENABLE_WARNINGS
 
        case AV_SAMPLE_FMT_FLTP:
        {
-               float** p = reinterpret_cast<float**> (_frame->data);
-               DCPOMATIC_ASSERT (_frame->channels <= channels);
-               /* Sometimes there aren't as many channels in the _frame as in the stream */
-               for (int i = 0; i < _frame->channels; ++i) {
+               auto p = reinterpret_cast<float**> (frame->data);
+               DCPOMATIC_ASSERT (frame->channels <= channels);
+               /* Sometimes there aren't as many channels in the frame as in the stream */
+               for (int i = 0; i < frame->channels; ++i) {
                        memcpy (data[i], p[i], frames * sizeof(float));
                }
-               for (int i = _frame->channels; i < channels; ++i) {
+               for (int i = frame->channels; i < channels; ++i) {
                        audio->make_silent (i);
                }
        }
        break;
 
        default:
-               throw DecodeError (String::compose (_("Unrecognised audio sample format (%1)"), static_cast<int> (audio_sample_format (stream))));
+               throw DecodeError (String::compose(_("Unrecognised audio sample format (%1)"), static_cast<int>(format)));
        }
 
        return audio;
@@ -340,9 +346,7 @@ DCPOMATIC_ENABLE_WARNINGS
 AVSampleFormat
 FFmpegDecoder::audio_sample_format (shared_ptr<FFmpegAudioStream> stream) const
 {
-DCPOMATIC_DISABLE_WARNINGS
-       return stream->stream (_format_context)->codec->sample_fmt;
-DCPOMATIC_ENABLE_WARNINGS
+       return static_cast<AVSampleFormat>(stream->stream(_format_context)->codecpar->format);
 }
 
 
@@ -406,11 +410,9 @@ FFmpegDecoder::seek (ContentTime time, bool accurate)
                avcodec_flush_buffers (video_codec_context());
        }
 
-DCPOMATIC_DISABLE_WARNINGS
        for (auto i: ffmpeg_content()->ffmpeg_audio_streams()) {
-               avcodec_flush_buffers (i->stream(_format_context)->codec);
+               avcodec_flush_buffers (_codec_context[i->index(_format_context)]);
        }
-DCPOMATIC_ENABLE_WARNINGS
 
        if (subtitle_codec_context ()) {
                avcodec_flush_buffers (subtitle_codec_context ());
@@ -419,141 +421,164 @@ DCPOMATIC_ENABLE_WARNINGS
        _have_current_subtitle = false;
 
        for (auto& i: _next_time) {
-               i = optional<ContentTime>();
+               i.second = boost::optional<dcpomatic::ContentTime>();
        }
 }
 
 
-void
-FFmpegDecoder::decode_audio_packet ()
+shared_ptr<FFmpegAudioStream>
+FFmpegDecoder::audio_stream_from_index (int index) const
 {
-       /* Audio packets can contain multiple frames, so we may have to call avcodec_decode_audio4
-          several times.
-       */
-
-       AVPacket copy_packet = _packet;
-       int const stream_index = copy_packet.stream_index;
-
        /* XXX: inefficient */
        auto streams = ffmpeg_content()->ffmpeg_audio_streams();
-       auto stream = streams.begin ();
-       while (stream != streams.end () && !(*stream)->uses_index (_format_context, stream_index)) {
+       auto stream = streams.begin();
+       while (stream != streams.end() && !(*stream)->uses_index(_format_context, index)) {
                ++stream;
        }
 
        if (stream == streams.end ()) {
-               /* The packet's stream may not be an audio one; just ignore it in this method if so */
-               return;
+               return {};
        }
 
-DCPOMATIC_DISABLE_WARNINGS
-       while (copy_packet.size > 0) {
+       return *stream;
+}
 
-               int frame_finished;
-               int decode_result = avcodec_decode_audio4 ((*stream)->stream (_format_context)->codec, _frame, &frame_finished, &copy_packet);
-               if (decode_result < 0) {
-                       /* avcodec_decode_audio4 can sometimes return an error even though it has decoded
-                          some valid data; for example dca_subframe_footer can return AVERROR_INVALIDDATA
-                          if it overreads the auxiliary data.  ffplay carries on if frame_finished is true,
-                          even in the face of such an error, so I think we should too.
 
-                          Returning from the method here caused mantis #352.
-                       */
-                       LOG_WARNING ("avcodec_decode_audio4 failed (%1)", decode_result);
-
-                       /* Fudge decode_result so that we come out of the while loop when
-                          we've processed this data.
-                       */
-                       decode_result = copy_packet.size;
+void
+FFmpegDecoder::process_audio_frame (shared_ptr<FFmpegAudioStream> stream)
+{
+       auto frame = audio_frame (stream);
+       auto data = deinterleave_audio (frame);
+
+       ContentTime ct;
+       if (frame->pts == AV_NOPTS_VALUE) {
+               /* In some streams we see not every frame coming through with a timestamp; for those
+                  that have AV_NOPTS_VALUE we need to work out the timestamp ourselves.  This is
+                  particularly noticeable with TrueHD streams (see #1111).
+                  */
+               if (_next_time[stream]) {
+                       ct = *_next_time[stream];
                }
+       } else {
+               ct = ContentTime::from_seconds (
+                       frame->best_effort_timestamp *
+                       av_q2d (stream->stream(_format_context)->time_base))
+                       + _pts_offset;
+       }
 
-               if (frame_finished) {
-                       shared_ptr<AudioBuffers> data = deinterleave_audio (*stream);
-
-                       ContentTime ct;
-                       if (_frame->pts == AV_NOPTS_VALUE) {
-                               /* In some streams we see not every frame coming through with a timestamp; for those
-                                  that have AV_NOPTS_VALUE we need to work out the timestamp ourselves.  This is
-                                  particularly noticeable with TrueHD streams (see #1111).
-                               */
-                               if (_next_time[stream_index]) {
-                                       ct = *_next_time[stream_index];
-                               }
-                       } else {
-                               ct = ContentTime::from_seconds (
-                                       av_frame_get_best_effort_timestamp (_frame) *
-                                       av_q2d ((*stream)->stream (_format_context)->time_base))
-                                       + _pts_offset;
-                       }
+       _next_time[stream] = ct + ContentTime::from_frames(data->frames(), stream->frame_rate());
 
-                       _next_time[stream_index] = ct + ContentTime::from_frames(data->frames(), (*stream)->frame_rate());
+       if (ct < ContentTime()) {
+               /* Discard audio data that comes before time 0 */
+               auto const remove = min (int64_t(data->frames()), (-ct).frames_ceil(double(stream->frame_rate())));
+               data->move (data->frames() - remove, remove, 0);
+               data->set_frames (data->frames() - remove);
+               ct += ContentTime::from_frames (remove, stream->frame_rate());
+       }
 
-                       if (ct < ContentTime()) {
-                               /* Discard audio data that comes before time 0 */
-                               auto const remove = min (int64_t (data->frames()), (-ct).frames_ceil(double((*stream)->frame_rate ())));
-                               data->move (data->frames() - remove, remove, 0);
-                               data->set_frames (data->frames() - remove);
-                               ct += ContentTime::from_frames (remove, (*stream)->frame_rate ());
-                       }
+       if (ct < ContentTime()) {
+               LOG_WARNING (
+                       "Crazy timestamp %1 for %2 samples in stream %3 (ts=%4 tb=%5, off=%6)",
+                       to_string(ct),
+                       data->frames(),
+                       stream->id(),
+                       frame->best_effort_timestamp,
+                       av_q2d(stream->stream(_format_context)->time_base),
+                       to_string(_pts_offset)
+                       );
+       }
 
-                       if (ct < ContentTime()) {
-                               LOG_WARNING (
-                                       "Crazy timestamp %1 for %2 samples in stream %3 packet pts %4 (ts=%5 tb=%6, off=%7)",
-                                       to_string(ct),
-                                       data->frames(),
-                                       copy_packet.stream_index,
-                                       copy_packet.pts,
-                                       av_frame_get_best_effort_timestamp(_frame),
-                                       av_q2d((*stream)->stream(_format_context)->time_base),
-                                       to_string(_pts_offset)
-                                       );
-                       }
-DCPOMATIC_ENABLE_WARNINGS
+       /* Give this data provided there is some, and its time is sane */
+       if (ct >= ContentTime() && data->frames() > 0) {
+               audio->emit (film(), stream, data, ct);
+       }
+}
 
-                       /* Give this data provided there is some, and its time is sane */
-                       if (ct >= ContentTime() && data->frames() > 0) {
-                               audio->emit (film(), *stream, data, ct);
-                       }
+
+void
+FFmpegDecoder::decode_and_process_audio_packet (AVPacket* packet)
+{
+       auto stream = audio_stream_from_index (packet->stream_index);
+       if (!stream) {
+               return;
+       }
+
+       auto context = _codec_context[stream->index(_format_context)];
+       auto frame = audio_frame (stream);
+
+       int r = avcodec_send_packet (context, packet);
+       if (r < 0) {
+               LOG_WARNING("avcodec_send_packet returned %1 for an audio packet", r);
+       }
+       while (r >= 0) {
+               r = avcodec_receive_frame (context, frame);
+               if (r == AVERROR(EAGAIN)) {
+                       /* More input is required */
+                       return;
                }
 
-               copy_packet.data += decode_result;
-               copy_packet.size -= decode_result;
+               /* We choose to be relaxed here about other errors; it seems that there may be valid
+                * data to decode even if an error occurred.  #352 may be related (though this was
+                * when we were using an old version of the FFmpeg API).
+                */
+               process_audio_frame (stream);
        }
 }
 
 
 bool
-FFmpegDecoder::decode_video_packet ()
+FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet)
 {
        DCPOMATIC_ASSERT (_video_stream);
 
-       int frame_finished;
-DCPOMATIC_DISABLE_WARNINGS
-       if (avcodec_decode_video2 (video_codec_context(), _frame, &frame_finished, &_packet) < 0 || !frame_finished) {
+       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);
+       }
+
+       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);
        }
-DCPOMATIC_ENABLE_WARNINGS
+
+       process_video_frame ();
+
+       return true;
+}
+
+
+void
+FFmpegDecoder::process_video_frame ()
+{
+       /* We assume we'll only get one frame here, which I think is safe */
 
        boost::mutex::scoped_lock lm (_filter_graphs_mutex);
 
        shared_ptr<VideoFilterGraph> graph;
 
        auto i = _filter_graphs.begin();
-       while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
+       while (i != _filter_graphs.end() && !(*i)->can_process(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format)) {
                ++i;
        }
 
        if (i == _filter_graphs.end ()) {
                dcp::Fraction vfr (lrint(_ffmpeg_content->video_frame_rate().get() * 1000), 1000);
-               graph = make_shared<VideoFilterGraph>(dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format, vfr);
+               graph = make_shared<VideoFilterGraph>(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format, vfr);
                graph->setup (_ffmpeg_content->filters ());
                _filter_graphs.push_back (graph);
-               LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
+               LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _video_frame->width, _video_frame->height, _video_frame->format);
        } else {
                graph = *i;
        }
 
-       auto images = graph->process (_frame);
+       auto images = graph->process (_video_frame);
 
        for (auto const& i: images) {
 
@@ -571,39 +596,39 @@ DCPOMATIC_ENABLE_WARNINGS
                        LOG_WARNING_NC ("Dropping frame without PTS");
                }
        }
-
-       return true;
 }
 
 
 void
-FFmpegDecoder::decode_subtitle_packet ()
+FFmpegDecoder::decode_and_process_subtitle_packet (AVPacket* packet)
 {
        int got_subtitle;
        AVSubtitle sub;
-       if (avcodec_decode_subtitle2 (subtitle_codec_context(), &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) {
+       if (avcodec_decode_subtitle2 (subtitle_codec_context(), &sub, &got_subtitle, packet) < 0 || !got_subtitle) {
                return;
        }
 
+       auto sub_period = subtitle_period (packet, ffmpeg_content()->subtitle_stream()->stream(_format_context), sub);
+
        /* Stop any current subtitle, either at the time it was supposed to stop, or now if now is sooner */
        if (_have_current_subtitle) {
                if (_current_subtitle_to) {
-                       only_text()->emit_stop (min(*_current_subtitle_to, subtitle_period(sub).from + _pts_offset));
+                       only_text()->emit_stop (min(*_current_subtitle_to, sub_period.from + _pts_offset));
                } else {
-                       only_text()->emit_stop (subtitle_period(sub).from + _pts_offset);
+                       only_text()->emit_stop (sub_period.from + _pts_offset);
                }
                _have_current_subtitle = false;
        }
 
        if (sub.num_rects <= 0) {
                /* Nothing new in this subtitle */
+               avsubtitle_free (&sub);
                return;
        }
 
        /* Subtitle PTS (within the source, not taking into account any of the
           source that we may have chopped off for the DCP).
        */
-       auto sub_period = subtitle_period (sub);
        ContentTime from;
        from = sub_period.from + _pts_offset;
        if (sub_period.to) {
@@ -620,13 +645,13 @@ FFmpegDecoder::decode_subtitle_packet ()
                case SUBTITLE_NONE:
                        break;
                case SUBTITLE_BITMAP:
-                       decode_bitmap_subtitle (rect, from);
+                       process_bitmap_subtitle (rect, from);
                        break;
                case SUBTITLE_TEXT:
                        cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n";
                        break;
                case SUBTITLE_ASS:
-                       decode_ass_subtitle (rect->ass, from);
+                       process_ass_subtitle (rect->ass, from);
                        break;
                }
        }
@@ -640,12 +665,12 @@ FFmpegDecoder::decode_subtitle_packet ()
 
 
 void
-FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from)
+FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from)
 {
        /* Note BGRA is expressed little-endian, so the first byte in the word is B, second
           G, third R, fourth A.
        */
-       auto image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (rect->w, rect->h), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (rect->w, rect->h), Image::Alignment::PADDED);
 
 #ifdef DCPOMATIC_HAVE_AVSUBTITLERECT_PICT
        /* Start of the first line in the subtitle */
@@ -728,7 +753,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime
 
 
 void
-FFmpegDecoder::decode_ass_subtitle (string ass, ContentTime from)
+FFmpegDecoder::process_ass_subtitle (string ass, ContentTime from)
 {
        /* We have no styles and no Format: line, so I'm assuming that FFmpeg
           produces a single format of Dialogue: lines...