+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (!_ffmpeg_content->subtitle_stream()) {
+ return;
+ }
+
+ _subtitle_codec_context = _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
+ if (_subtitle_codec_context == 0) {
+ throw DecodeError (N_("could not find subtitle stream"));
+ }
+
+ _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
+
+ if (_subtitle_codec == 0) {
+ throw DecodeError (N_("could not find subtitle decoder"));
+ }
+
+ if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
+ throw DecodeError (N_("could not open subtitle decoder"));
+ }
+}
+
+void
+FFmpegDecoder::decode_subtitle_packet ()
+{
+ int got_subtitle;
+ AVSubtitle sub;
+ if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) {
+ return;
+ }
+
+ /* Sometimes we get an empty AVSubtitle, which is used by some codecs to
+ indicate that the previous subtitle should stop.
+ */
+ if (sub.num_rects <= 0) {
+ image_subtitle (ContentTime (), ContentTime (), shared_ptr<Image> (), dcpomatic::Rect<double> ());
+ return;
+ } else if (sub.num_rects > 1) {
+ throw DecodeError (_("multi-part subtitles not yet supported"));
+ }
+
+ /* Subtitle PTS (within the source, not taking into account any of the
+ source that we may have chopped off for the DCP)
+ */
+ ContentTime packet_time = ContentTime::from_seconds (static_cast<double> (sub.pts) / AV_TIME_BASE) + _pts_offset;
+
+ /* hence start time for this sub */
+ ContentTime const from = packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3);
+ ContentTime const to = packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3);
+
+ AVSubtitleRect const * rect = sub.rects[0];
+
+ if (rect->type != SUBTITLE_BITMAP) {
+ /* XXX */
+ // throw DecodeError (_("non-bitmap subtitles not yet supported"));
+ return;
+ }
+
+ /* Note RGBA is expressed little-endian, so the first byte in the word is R, second
+ G, third B, fourth A.
+ */
+ shared_ptr<Image> image (new Image (PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true));
+
+ /* Start of the first line in the subtitle */
+ uint8_t* sub_p = rect->pict.data[0];
+ /* sub_p looks up into a BGRA palette which is here
+ (i.e. first byte B, second G, third R, fourth A)
+ */
+ uint32_t const * palette = (uint32_t *) rect->pict.data[1];
+ /* Start of the output data */
+ uint32_t* out_p = (uint32_t *) image->data()[0];
+
+ for (int y = 0; y < rect->h; ++y) {
+ uint8_t* sub_line_p = sub_p;
+ uint32_t* out_line_p = out_p;
+ for (int x = 0; x < rect->w; ++x) {
+ uint32_t const p = palette[*sub_line_p++];
+ *out_line_p++ = ((p & 0xff) << 16) | (p & 0xff00) | ((p & 0xff0000) >> 16) | (p & 0xff000000);
+ }
+ sub_p += rect->pict.linesize[0];
+ out_p += image->stride()[0] / sizeof (uint32_t);
+ }
+
+ dcp::Size const vs = _ffmpeg_content->video_size ();
+
+ image_subtitle (
+ from,
+ to,
+ image,
+ dcpomatic::Rect<double> (
+ static_cast<double> (rect->x) / vs.width,
+ static_cast<double> (rect->y) / vs.height,
+ static_cast<double> (rect->w) / vs.width,
+ static_cast<double> (rect->h) / vs.height
+ )
+ );
+
+ avsubtitle_free (&sub);