From 5f02ea34590cd6b796a6eaa55211abf5b36fd329 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 8 Oct 2012 15:37:04 +0100 Subject: Hacks. --- src/lib/ffmpeg_decoder.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/lib/ffmpeg_decoder.h') diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 4e5445f67..9b57b5589 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -76,16 +76,22 @@ private: void setup_general (); void setup_video (); void setup_audio (); + void setup_subtitle (); AVFormatContext* _format_context; int _video_stream; int _audio_stream; ///< may be < 0 if there is no audio + int _subtitle_stream; ///< may be < 0 if there is no subtitle AVFrame* _frame; AVCodecContext* _video_codec_context; AVCodec* _video_codec; - AVCodecContext* _audio_codec_context; ///< may be 0 if there is no audio - AVCodec* _audio_codec; ///< may be 0 if there is no audio + AVCodecContext* _audio_codec_context; ///< may be 0 if there is no audio + AVCodec* _audio_codec; ///< may be 0 if there is no audio + AVCodecContext* _subtitle_codec_context; ///< may be 0 if there is no subtitle + AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle AVPacket _packet; + AVSubtitle _subtitle; + bool _have_subtitle; }; -- cgit v1.2.3 From 76740ecc18a5e896c7d05f3d71f865105c3248c7 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 8 Oct 2012 16:22:44 +0100 Subject: Obey subtitle timing. --- src/lib/ffmpeg_decoder.cc | 169 +++++++++++++++++++++++++--------------------- src/lib/ffmpeg_decoder.h | 2 + 2 files changed, 95 insertions(+), 76 deletions(-) (limited to 'src/lib/ffmpeg_decoder.h') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 8dbec87fd..cf17bbfb7 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -209,7 +209,6 @@ FFmpegDecoder::do_pass () if (_opt->decode_video) { while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - process_video (_frame); } } @@ -232,81 +231,7 @@ FFmpegDecoder::do_pass () int frame_finished; if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - - if (_have_subtitle) { - for (unsigned int i = 0; i < _subtitle.num_rects; ++i) { - AVSubtitleRect* rect = _subtitle.rects[i]; - if (rect->type != SUBTITLE_BITMAP) { - throw DecodeError ("non-bitmap subtitles not yet supported"); - } - - /* XXX: all this assumes YUV420 in _frame */ - - assert (rect->nb_colors == 4); - assert (rect->pict.data[0]); - - /* Start of the first line in the target frame */ - uint8_t* frame_y_p = _frame->data[0] + rect->y * _frame->linesize[0]; - uint8_t* frame_u_p = _frame->data[1] + (rect->y / 2) * _frame->linesize[1]; - uint8_t* frame_v_p = _frame->data[2] + (rect->y / 2) * _frame->linesize[2]; - - /* Start of the first line in the subtitle */ - uint8_t* sub_p = rect->pict.data[0]; - /* sub_p looks up into a RGB palette which is here */ - uint32_t const * palette = (uint32_t *) rect->pict.data[1]; - - for (int sub_y = 0; sub_y < rect->h; ++sub_y) { - /* Pointers to the start of this line */ - uint8_t* sub_line_p = sub_p; - uint8_t* frame_line_y_p = frame_y_p + rect->x; - uint8_t* frame_line_u_p = frame_u_p + (rect->x / 2); - uint8_t* frame_line_v_p = frame_v_p + (rect->x / 2); - - /* U and V are subsampled */ - uint8_t current_u = 0; - uint8_t current_v = 0; - int subsample_step = 0; - - for (int sub_x = 0; sub_x < rect->w; ++sub_x) { - - /* RGB value for this subtitle pixel */ - uint32_t const val = palette[*sub_line_p++]; - - int const red = (val & 0xff); - int const green = (val & 0xff00) >> 8; - int const blue = (val & 0xff0000) >> 16; - float const alpha = ((val & 0xff000000) >> 24) / 255.0; - - /* Alpha-blend Y */ - int const cy = *frame_line_y_p; - *frame_line_y_p++ = int (cy * (1 - alpha)) + int (RGB_TO_Y_CCIR (red, green, blue) * alpha); - - /* Store up U and V */ - current_u |= ((RGB_TO_U_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); - current_v |= ((RGB_TO_V_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); - - if (subsample_step == 1 && (sub_y % 2) == 0) { - /* We have complete U and V bytes, so alpha-blend them into the frame */ - int const cu = *frame_line_u_p; - int const cv = *frame_line_v_p; - *frame_line_u_p++ = int (cu * (1 - alpha)) + int (current_u * alpha); - *frame_line_v_p++ = int (cv * (1 - alpha)) + int (current_v * alpha); - current_u = current_v = 0; - } - - subsample_step = (subsample_step + 1) % 2; - } - - sub_p += rect->pict.linesize[0]; - frame_y_p += _frame->linesize[0]; - if ((sub_y % 2) == 0) { - frame_u_p += _frame->linesize[1]; - frame_v_p += _frame->linesize[2]; - } - } - } - } - + maybe_add_subtitle (); process_video (_frame); } @@ -429,3 +354,95 @@ FFmpegDecoder::sample_aspect_ratio_denominator () const return _video_codec_context->sample_aspect_ratio.den; } +void +FFmpegDecoder::maybe_add_subtitle () +{ + if (!_have_subtitle) { + return; + } + + /* subtitle PTS in seconds */ + float const packet_time = (_subtitle.pts / AV_TIME_BASE) + float (_subtitle.pts % AV_TIME_BASE) / 1e6; + /* hence start time for this sub */ + float const from = packet_time + (float (_subtitle.start_display_time) / 1e3); + float const to = packet_time + (float (_subtitle.end_display_time) / 1e3); + + float const video_frame_time = float (last_video_frame ()) / rint (_fs->frames_per_second); + + if (from < video_frame_time || video_frame_time > to) { + return; + } + + for (unsigned int i = 0; i < _subtitle.num_rects; ++i) { + AVSubtitleRect* rect = _subtitle.rects[i]; + if (rect->type != SUBTITLE_BITMAP) { + throw DecodeError ("non-bitmap subtitles not yet supported"); + } + + /* XXX: all this assumes YUV420 in _frame */ + + assert (rect->pict.data[0]); + + /* Start of the first line in the target frame */ + uint8_t* frame_y_p = _frame->data[0] + rect->y * _frame->linesize[0]; + uint8_t* frame_u_p = _frame->data[1] + (rect->y / 2) * _frame->linesize[1]; + uint8_t* frame_v_p = _frame->data[2] + (rect->y / 2) * _frame->linesize[2]; + + /* Start of the first line in the subtitle */ + uint8_t* sub_p = rect->pict.data[0]; + /* sub_p looks up into a RGB palette which is here */ + uint32_t const * palette = (uint32_t *) rect->pict.data[1]; + + for (int sub_y = 0; sub_y < rect->h; ++sub_y) { + /* Pointers to the start of this line */ + uint8_t* sub_line_p = sub_p; + uint8_t* frame_line_y_p = frame_y_p + rect->x; + uint8_t* frame_line_u_p = frame_u_p + (rect->x / 2); + uint8_t* frame_line_v_p = frame_v_p + (rect->x / 2); + + /* U and V are subsampled */ + uint8_t current_u = 0; + uint8_t current_v = 0; + int subsample_step = 0; + + for (int sub_x = 0; sub_x < rect->w; ++sub_x) { + + /* RGB value for this subtitle pixel */ + uint32_t const val = palette[*sub_line_p++]; + + int const red = (val & 0xff); + int const green = (val & 0xff00) >> 8; + int const blue = (val & 0xff0000) >> 16; + float const alpha = ((val & 0xff000000) >> 24) / 255.0; + + /* Alpha-blend Y */ + int const cy = *frame_line_y_p; + *frame_line_y_p++ = int (cy * (1 - alpha)) + int (RGB_TO_Y_CCIR (red, green, blue) * alpha); + + /* Store up U and V */ + current_u |= ((RGB_TO_U_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); + current_v |= ((RGB_TO_V_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); + + if (subsample_step == 1 && (sub_y % 2) == 0) { + /* We have complete U and V bytes, so alpha-blend them into the frame */ + int const cu = *frame_line_u_p; + int const cv = *frame_line_v_p; + *frame_line_u_p++ = int (cu * (1 - alpha)) + int (current_u * alpha); + *frame_line_v_p++ = int (cv * (1 - alpha)) + int (current_v * alpha); + current_u = current_v = 0; + } + + subsample_step = (subsample_step + 1) % 2; + } + + sub_p += rect->pict.linesize[0]; + frame_y_p += _frame->linesize[0]; + if ((sub_y % 2) == 0) { + frame_u_p += _frame->linesize[1]; + frame_v_p += _frame->linesize[2]; + } + } + } +} + + diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 9b57b5589..739502d50 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -78,6 +78,8 @@ private: void setup_audio (); void setup_subtitle (); + void maybe_add_subtitle (); + AVFormatContext* _format_context; int _video_stream; int _audio_stream; ///< may be < 0 if there is no audio -- cgit v1.2.3 From 6fd70950e721181920603d90ad52ed58883f2806 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 9 Oct 2012 02:07:07 +0100 Subject: Various subtitle fixes. --- src/lib/decoder.cc | 2 ++ src/lib/decoder.h | 1 + src/lib/ffmpeg_decoder.cc | 55 ++++++++++++++++++++++++++--------------------- src/lib/ffmpeg_decoder.h | 1 + 4 files changed, 35 insertions(+), 24 deletions(-) (limited to 'src/lib/ffmpeg_decoder.h') diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 8aa5f77c6..44079edf7 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -314,6 +314,8 @@ Decoder::process_video (AVFrame* frame) image->make_black (); } + overlay (image); + TIMING ("Decoder emits %1", _video_frame); Video (image, _video_frame); ++_video_frame; diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 19ef25ede..08d961233 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -99,6 +99,7 @@ protected: virtual int time_base_denominator () const = 0; virtual int sample_aspect_ratio_numerator () const = 0; virtual int sample_aspect_ratio_denominator () const = 0; + virtual void overlay (boost::shared_ptr image) const {} void process_video (AVFrame *); void process_audio (uint8_t *, int); diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index cf17bbfb7..808e5ac9b 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -27,6 +27,7 @@ #include #include #include +#include extern "C" { #include #include @@ -231,7 +232,6 @@ FFmpegDecoder::do_pass () int frame_finished; if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - maybe_add_subtitle (); process_video (_frame); } @@ -355,7 +355,7 @@ FFmpegDecoder::sample_aspect_ratio_denominator () const } void -FFmpegDecoder::maybe_add_subtitle () +FFmpegDecoder::overlay (shared_ptr image) const { if (!_have_subtitle) { return; @@ -368,32 +368,34 @@ FFmpegDecoder::maybe_add_subtitle () float const to = packet_time + (float (_subtitle.end_display_time) / 1e3); float const video_frame_time = float (last_video_frame ()) / rint (_fs->frames_per_second); - - if (from < video_frame_time || video_frame_time > to) { + + if (from > video_frame_time || video_frame_time < to) { return; } - + for (unsigned int i = 0; i < _subtitle.num_rects; ++i) { AVSubtitleRect* rect = _subtitle.rects[i]; if (rect->type != SUBTITLE_BITMAP) { throw DecodeError ("non-bitmap subtitles not yet supported"); } - - /* XXX: all this assumes YUV420 in _frame */ + + /* XXX: all this assumes YUV420 in image */ assert (rect->pict.data[0]); - /* Start of the first line in the target frame */ - uint8_t* frame_y_p = _frame->data[0] + rect->y * _frame->linesize[0]; - uint8_t* frame_u_p = _frame->data[1] + (rect->y / 2) * _frame->linesize[1]; - uint8_t* frame_v_p = _frame->data[2] + (rect->y / 2) * _frame->linesize[2]; + /* Start of the first line in the target image */ + uint8_t* frame_y_p = image->data()[0] + rect->y * image->line_size()[0]; + uint8_t* frame_u_p = image->data()[1] + (rect->y / 2) * image->line_size()[1]; + uint8_t* frame_v_p = image->data()[2] + (rect->y / 2) * image->line_size()[2]; + + int const hlim = min (rect->y + rect->h, image->size().height) - rect->y; /* Start of the first line in the subtitle */ uint8_t* sub_p = rect->pict.data[0]; /* sub_p looks up into a RGB palette which is here */ uint32_t const * palette = (uint32_t *) rect->pict.data[1]; - for (int sub_y = 0; sub_y < rect->h; ++sub_y) { + for (int sub_y = 0; sub_y < hlim; ++sub_y) { /* Pointers to the start of this line */ uint8_t* sub_line_p = sub_p; uint8_t* frame_line_y_p = frame_y_p + rect->x; @@ -401,8 +403,8 @@ FFmpegDecoder::maybe_add_subtitle () uint8_t* frame_line_v_p = frame_v_p + (rect->x / 2); /* U and V are subsampled */ - uint8_t current_u = 0; - uint8_t current_v = 0; + uint8_t next_u = 0; + uint8_t next_v = 0; int subsample_step = 0; for (int sub_x = 0; sub_x < rect->w; ++sub_x) { @@ -420,29 +422,34 @@ FFmpegDecoder::maybe_add_subtitle () *frame_line_y_p++ = int (cy * (1 - alpha)) + int (RGB_TO_Y_CCIR (red, green, blue) * alpha); /* Store up U and V */ - current_u |= ((RGB_TO_U_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); - current_v |= ((RGB_TO_V_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); + next_u |= ((RGB_TO_U_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); + next_v |= ((RGB_TO_V_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); if (subsample_step == 1 && (sub_y % 2) == 0) { - /* We have complete U and V bytes, so alpha-blend them into the frame */ int const cu = *frame_line_u_p; int const cv = *frame_line_v_p; - *frame_line_u_p++ = int (cu * (1 - alpha)) + int (current_u * alpha); - *frame_line_v_p++ = int (cv * (1 - alpha)) + int (current_v * alpha); - current_u = current_v = 0; + + *frame_line_u_p++ = + int (((cu & 0x0f) * (1 - alpha) + (next_u & 0x0f) * alpha)) | + int (((cu & 0xf0) * (1 - alpha) + (next_u & 0xf0) * alpha)); + + *frame_line_v_p++ = + int (((cv & 0x0f) * (1 - alpha) + (next_v & 0x0f) * alpha)) | + int (((cv & 0xf0) * (1 - alpha) + (next_v & 0xf0) * alpha)); + + next_u = next_v = 0; } subsample_step = (subsample_step + 1) % 2; } sub_p += rect->pict.linesize[0]; - frame_y_p += _frame->linesize[0]; + frame_y_p += image->line_size()[0]; if ((sub_y % 2) == 0) { - frame_u_p += _frame->linesize[1]; - frame_v_p += _frame->linesize[2]; + frame_u_p += image->line_size()[1]; + frame_v_p += image->line_size()[2]; } } } } - diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 739502d50..18c2e2aeb 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -72,6 +72,7 @@ private: int time_base_denominator () const; int sample_aspect_ratio_numerator () const; int sample_aspect_ratio_denominator () const; + void overlay (boost::shared_ptr image) const; void setup_general (); void setup_video (); -- cgit v1.2.3 From b5828ccf20a0e0c4365854ac19a05d5a4783e254 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 9 Oct 2012 15:56:55 +0100 Subject: Make subtitle addition optional. --- src/lib/decoder.h | 1 + src/lib/ffmpeg_decoder.cc | 7 ++++++- src/lib/ffmpeg_decoder.h | 1 + src/lib/film.cc | 8 ++++++++ src/lib/film.h | 12 +++++++++++- src/lib/film_state.cc | 6 ++++++ src/lib/film_state.h | 5 +++++ src/lib/imagemagick_decoder.h | 4 ++++ src/lib/tiff_decoder.h | 3 +++ src/wx/film_editor.cc | 32 ++++++++++++++++++++++++++++++++ src/wx/film_editor.h | 3 +++ 11 files changed, 80 insertions(+), 2 deletions(-) (limited to 'src/lib/ffmpeg_decoder.h') diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 7ca9bb1df..9a4c7695e 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -66,6 +66,7 @@ public: /** @return format of audio samples */ virtual AVSampleFormat audio_sample_format () const = 0; virtual int64_t audio_channel_layout () const = 0; + virtual bool has_subtitles () const = 0; void process_begin (); bool pass (); diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 808e5ac9b..7bc579ba6 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -249,7 +249,7 @@ FFmpegDecoder::do_pass () process_audio (_frame->data[0], data_size); } - } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream) { + } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _fs->with_subtitles) { if (_have_subtitle) { avsubtitle_free (&_subtitle); @@ -453,3 +453,8 @@ FFmpegDecoder::overlay (shared_ptr image) const } } +bool +FFmpegDecoder::has_subtitles () const +{ + return (_subtitle_stream != -1); +} diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 18c2e2aeb..59ec7573d 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -63,6 +63,7 @@ public: int audio_sample_rate () const; AVSampleFormat audio_sample_format () const; int64_t audio_channel_layout () const; + bool has_subtitles () const; private: diff --git a/src/lib/film.cc b/src/lib/film.cc index e2b3d4bc3..96dc3d3a4 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -217,6 +217,7 @@ Film::set_content (string c) _state.audio_channels = d->audio_channels (); _state.audio_sample_rate = d->audio_sample_rate (); _state.audio_sample_format = d->audio_sample_format (); + _state.has_subtitles = d->has_subtitles (); _state.content_digest = md5_digest (s->content_path ()); _state.content = c; @@ -658,3 +659,10 @@ Film::encoded_frames () const return N; } + +void +Film::set_with_subtitles (bool w) +{ + _state.with_subtitles = w; + signal_changed (WITH_SUBTITLES); +} diff --git a/src/lib/film.h b/src/lib/film.h index cd3b1b8a8..919cecc22 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -119,6 +119,10 @@ public: int still_duration () const { return _state.still_duration; } + + bool with_subtitles () const { + return _state.with_subtitles; + } void set_filters (std::vector const &); @@ -144,6 +148,7 @@ public: void set_audio_gain (float); void set_audio_delay (int); void set_still_duration (int); + void set_with_subtitles (bool); /** @return size, in pixels, of the source (ignoring cropping) */ Size size () const { @@ -174,6 +179,10 @@ public: AVSampleFormat audio_sample_format () const { return _state.audio_sample_format; } + + bool has_subtitles () const { + return _state.has_subtitles; + } std::string j2k_dir () const; @@ -218,7 +227,8 @@ public: FRAMES_PER_SECOND, AUDIO_CHANNELS, AUDIO_SAMPLE_RATE, - STILL_DURATION + STILL_DURATION, + WITH_SUBTITLES, }; boost::shared_ptr state_copy () const; diff --git a/src/lib/film_state.cc b/src/lib/film_state.cc index 3cd7091ca..1872d9e76 100644 --- a/src/lib/film_state.cc +++ b/src/lib/film_state.cc @@ -80,6 +80,7 @@ FilmState::write_metadata (ofstream& f) const f << "audio_gain " << audio_gain << "\n"; f << "audio_delay " << audio_delay << "\n"; f << "still_duration " << still_duration << "\n"; + f << "with_subtitles " << with_subtitles << "\n"; /* Cached stuff; this is information about our content; we could look it up each time, but that's slow. @@ -94,6 +95,7 @@ FilmState::write_metadata (ofstream& f) const f << "audio_sample_rate " << audio_sample_rate << "\n"; f << "audio_sample_format " << audio_sample_format_to_string (audio_sample_format) << "\n"; f << "content_digest " << content_digest << "\n"; + f << "has_subtitles " << has_subtitles << "\n"; } /** Read state from a key / value pair. @@ -142,6 +144,8 @@ FilmState::read_metadata (string k, string v) audio_delay = atoi (v.c_str ()); } else if (k == "still_duration") { still_duration = atoi (v.c_str ()); + } else if (k == "with_subtitles") { + with_subtitles = (v == "1"); } /* Cached stuff */ @@ -165,6 +169,8 @@ FilmState::read_metadata (string k, string v) audio_sample_format = audio_sample_format_from_string (v); } else if (k == "content_digest") { content_digest = v; + } else if (k == "has_subtitles") { + has_subtitles = (v == "1"); } } diff --git a/src/lib/film_state.h b/src/lib/film_state.h index 16a1b0508..2b792694c 100644 --- a/src/lib/film_state.h +++ b/src/lib/film_state.h @@ -62,10 +62,12 @@ public: , audio_gain (0) , audio_delay (0) , still_duration (10) + , with_subtitles (false) , length (0) , audio_channels (0) , audio_sample_rate (0) , audio_sample_format (AV_SAMPLE_FMT_NONE) + , has_subtitles (false) {} std::string file (std::string f) const; @@ -126,6 +128,7 @@ public: int audio_delay; /** Duration to make still-sourced films (in seconds) */ int still_duration; + bool with_subtitles; /* Data which is cached to speed things up */ @@ -143,6 +146,8 @@ public: AVSampleFormat audio_sample_format; /** MD5 digest of our content file */ std::string content_digest; + /** true if the source has subtitles */ + bool has_subtitles; private: std::string thumb_file_for_frame (int) const; diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index aca91ef55..05dc7f113 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -35,6 +35,10 @@ public: return 0; } + bool has_subtitles () const { + return false; + } + static float static_frames_per_second () { return 24; } diff --git a/src/lib/tiff_decoder.h b/src/lib/tiff_decoder.h index d9b5b3969..b9849a259 100644 --- a/src/lib/tiff_decoder.h +++ b/src/lib/tiff_decoder.h @@ -53,6 +53,9 @@ public: int audio_sample_rate () const; AVSampleFormat audio_sample_format () const; int64_t audio_channel_layout () const; + bool has_subtitles () const { + return false; + } private: bool do_pass (); diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 3b26a5537..bc7414c41 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -130,6 +130,11 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent) _sizer->Add (s); } + _with_subtitles = new wxCheckBox (this, wxID_ANY, wxT("With Subtitles")); + video_control (_with_subtitles); + _sizer->Add (_with_subtitles, 1); + _sizer->AddSpacer (0); + video_control (add_label_to_sizer (_sizer, this, "Frames Per Second")); _frames_per_second = new wxStaticText (this, wxID_ANY, wxT ("")); _sizer->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL); @@ -292,6 +297,7 @@ FilmEditor::content_changed (wxCommandEvent &) setup_visibility (); setup_formats (); + setup_subtitle_button (); } /** Called when the DCP A/B switch has been toggled */ @@ -340,6 +346,7 @@ FilmEditor::film_changed (Film::Property p) _content->SetPath (std_to_wx (_film->content ())); setup_visibility (); setup_formats (); + setup_subtitle_button (); break; case Film::FORMAT: { @@ -437,6 +444,9 @@ FilmEditor::film_changed (Film::Property p) case Film::STILL_DURATION: _still_duration->SetValue (_film->still_duration ()); break; + case Film::WITH_SUBTITLES: + _with_subtitles->SetValue (_film->with_subtitles ()); + break; } } @@ -702,3 +712,25 @@ FilmEditor::setup_formats () _sizer->Layout (); } + +void +FilmEditor::with_subtitles_toggled (wxCommandEvent &) +{ + if (!_film) { + return; + } + + _ignore_changes = Film::WITH_SUBTITLES; + _film->set_with_subtitles (_with_subtitles->GetValue ()); + _ignore_changes = Film::NONE; +} + +void +FilmEditor::setup_subtitle_button () +{ + _with_subtitles->Enable (_film->has_subtitles ()); + if (!_film->has_subtitles ()) { + _with_subtitles->SetValue (false); + } +} + diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index c599cd285..720e71902 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -58,6 +58,7 @@ private: void audio_gain_changed (wxCommandEvent &); void audio_gain_calculate_button_clicked (wxCommandEvent &); void audio_delay_changed (wxCommandEvent &); + void with_subtitles_toggled (wxCommandEvent &); void still_duration_changed (wxCommandEvent &); /* Handle changes to the model */ @@ -69,6 +70,7 @@ private: void set_things_sensitive (bool); void setup_formats (); + void setup_subtitle_button (); wxControl* video_control (wxControl *); wxControl* still_control (wxControl *); @@ -103,6 +105,7 @@ private: wxButton* _audio_gain_calculate_button; /** The Film's audio delay */ wxSpinCtrl* _audio_delay; + wxCheckBox* _with_subtitles; /** The Film's DCP content type */ wxComboBox* _dcp_content_type; /** The Film's frames per second */ -- cgit v1.2.3 From cc4a67b7eb8ecaed076e261960848f70e3e741af Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 10 Oct 2012 15:47:06 +0100 Subject: Subs successfully exported with thumbs. --- src/lib/ab_transcoder.cc | 4 +- src/lib/ab_transcoder.h | 3 +- src/lib/dcp_video_frame.cc | 7 +-- src/lib/dcp_video_frame.h | 4 +- src/lib/decoder.cc | 6 +- src/lib/decoder.h | 7 ++- src/lib/encoder.h | 4 +- src/lib/ffmpeg_decoder.cc | 132 ++++++----------------------------------- src/lib/ffmpeg_decoder.h | 5 +- src/lib/image.cc | 57 ++++++++++++++---- src/lib/image.h | 3 +- src/lib/imagemagick_decoder.cc | 22 ++++++- src/lib/imagemagick_decoder.h | 19 ++++++ src/lib/j2k_still_encoder.cc | 4 +- src/lib/j2k_still_encoder.h | 2 +- src/lib/j2k_wav_encoder.cc | 4 +- src/lib/j2k_wav_encoder.h | 3 +- src/lib/job_manager.cc | 5 ++ src/lib/options.h | 8 ++- src/lib/server.cc | 12 ++-- src/lib/tiff_decoder.cc | 2 +- src/lib/tiff_encoder.cc | 56 ++++++++++++++++- src/lib/tiff_encoder.h | 2 +- src/lib/wscript | 1 + test/test.cc | 2 +- 25 files changed, 204 insertions(+), 170 deletions(-) (limited to 'src/lib/ffmpeg_decoder.h') diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index 1c20ae477..a32d82c54 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -70,7 +70,7 @@ ABTranscoder::~ABTranscoder () } void -ABTranscoder::process_video (shared_ptr yuv, int frame, int index) +ABTranscoder::process_video (shared_ptr yuv, int frame, shared_ptr sub, int index) { if (index == 0) { /* Keep this image around until we get the other half */ @@ -92,7 +92,7 @@ ABTranscoder::process_video (shared_ptr yuv, int frame, int index) } /* And pass it to the encoder */ - _encoder->process_video (_image, frame); + _encoder->process_video (_image, frame, sub); _image.reset (); } diff --git a/src/lib/ab_transcoder.h b/src/lib/ab_transcoder.h index 0310bb923..491205ef7 100644 --- a/src/lib/ab_transcoder.h +++ b/src/lib/ab_transcoder.h @@ -32,6 +32,7 @@ class FilmState; class Options; class Image; class Log; +class Subtitle; /** @class ABTranscoder * @brief A transcoder which uses one FilmState for the left half of the screen, and a different one @@ -54,7 +55,7 @@ public: void go (); private: - void process_video (boost::shared_ptr, int, int); + void process_video (boost::shared_ptr, int, boost::shared_ptr, int); boost::shared_ptr _fs_a; boost::shared_ptr _fs_b; diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index 90826a99f..13d3efcbf 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -72,8 +72,9 @@ using namespace boost; * @param l Log to write to. */ DCPVideoFrame::DCPVideoFrame ( - shared_ptr yuv, Size out, int p, Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l) + shared_ptr yuv, shared_ptr sub, Size out, int p, Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l) : _input (yuv) + , _subtitle (sub) , _out_size (out) , _padding (p) , _scaler (s) @@ -296,10 +297,6 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv) << Config::instance()->colour_lut_index () << " " << Config::instance()->j2k_bandwidth () << " "; - for (int i = 0; i < _input->components(); ++i) { - s << _input->line_size()[i] << " "; - } - socket.write ((uint8_t *) s.str().c_str(), s.str().length() + 1, 30); for (int i = 0; i < _input->components(); ++i) { diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h index 72f885e45..fe2e27966 100644 --- a/src/lib/dcp_video_frame.h +++ b/src/lib/dcp_video_frame.h @@ -31,6 +31,7 @@ class ServerDescription; class Scaler; class Image; class Log; +class Subtitle; /** @class EncodedData * @brief Container for J2K-encoded data. @@ -105,7 +106,7 @@ public: class DCPVideoFrame { public: - DCPVideoFrame (boost::shared_ptr, Size, int, Scaler const *, int, float, std::string, int, int, Log *); + DCPVideoFrame (boost::shared_ptr, boost::shared_ptr, Size, int, Scaler const *, int, float, std::string, int, int, Log *); virtual ~DCPVideoFrame (); boost::shared_ptr encode_locally (); @@ -120,6 +121,7 @@ private: void write_encoded (boost::shared_ptr, uint8_t *, int); boost::shared_ptr _input; ///< the input image + boost::shared_ptr _subtitle; ///< any subtitle that should be on the image Size _out_size; ///< the required size of the output, in pixels int _padding; Scaler const * _scaler; ///< scaler to use diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 15d74022c..e4b892ea4 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -228,7 +228,7 @@ Decoder::process_audio (uint8_t* data, int size) * @param frame to decode; caller manages memory. */ void -Decoder::process_video (AVFrame* frame) +Decoder::process_video (AVFrame* frame, shared_ptr sub) { if (_minimal) { ++_video_frame; @@ -303,10 +303,8 @@ Decoder::process_video (AVFrame* frame) image->make_black (); } - overlay (image); - TIMING ("Decoder emits %1", _video_frame); - Video (image, _video_frame); + Video (image, _video_frame, sub); ++_video_frame; } } diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 9a4c7695e..5cb44b8d9 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -37,6 +37,7 @@ class Options; class Image; class Log; class DelayLine; +class Subtitle; /** @class Decoder. * @brief Parent class for decoders of content. @@ -81,8 +82,9 @@ public: /** Emitted when a video frame is ready. * First parameter is the frame. * Second parameter is its index within the content. + * Third parameter is either 0 or a subtitle that should be on this frame. */ - sigc::signal, int> Video; + sigc::signal, int, boost::shared_ptr > Video; /** Emitted when some audio data is ready. * First parameter is the interleaved sample data, format is given in the FilmState. @@ -98,9 +100,8 @@ protected: virtual int time_base_denominator () const = 0; virtual int sample_aspect_ratio_numerator () const = 0; virtual int sample_aspect_ratio_denominator () const = 0; - virtual void overlay (boost::shared_ptr image) const {} - void process_video (AVFrame *); + void process_video (AVFrame *, boost::shared_ptr); void process_audio (uint8_t *, int); /** our FilmState */ diff --git a/src/lib/encoder.h b/src/lib/encoder.h index ea356cec4..02a2d7723 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -36,6 +36,7 @@ class FilmState; class Options; class Image; class Log; +class Subtitle; /** @class Encoder * @brief Parent class for classes which can encode video and audio frames. @@ -58,8 +59,9 @@ public: /** Called with a frame of video. * @param i Video frame image. * @param f Frame number within the film. + * @param s A subtitle that should be on this frame, or 0. */ - virtual void process_video (boost::shared_ptr i, int f) = 0; + virtual void process_video (boost::shared_ptr i, int f, boost::shared_ptr s) = 0; /** Called with some audio data. * @param d Data. diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index ca35c6e81..c2ee9297b 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -48,6 +48,7 @@ extern "C" { #include "util.h" #include "log.h" #include "ffmpeg_decoder.h" +#include "subtitle.h" using namespace std; using namespace boost; @@ -65,7 +66,6 @@ FFmpegDecoder::FFmpegDecoder (boost::shared_ptr s, boost::share , _audio_codec (0) , _subtitle_codec_context (0) , _subtitle_codec (0) - , _have_subtitle (false) { setup_general (); setup_video (); @@ -83,10 +83,6 @@ FFmpegDecoder::~FFmpegDecoder () avcodec_close (_video_codec_context); } - if (_have_subtitle) { - avsubtitle_free (&_subtitle); - } - if (_subtitle_codec_context) { avcodec_close (_subtitle_codec_context); } @@ -209,7 +205,12 @@ FFmpegDecoder::do_pass () int frame_finished; while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - process_video (_frame); + shared_ptr s; + if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { + s = _subtitle; + } + + process_video (_frame, s); } if (_audio_stream >= 0 && _opt->decode_audio) { @@ -230,7 +231,12 @@ FFmpegDecoder::do_pass () int frame_finished; if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - process_video (_frame); + shared_ptr s; + if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { + s = _subtitle; + } + + process_video (_frame, s); } } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio) { @@ -247,16 +253,13 @@ FFmpegDecoder::do_pass () process_audio (_frame->data[0], data_size); } - } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _fs->with_subtitles) { - - if (_have_subtitle) { - avsubtitle_free (&_subtitle); - _have_subtitle = false; - } + } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream) { int got_subtitle; - if (avcodec_decode_subtitle2 (_subtitle_codec_context, &_subtitle, &got_subtitle, &_packet) && got_subtitle) { - _have_subtitle = true; + AVSubtitle sub; + if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) { + _subtitle.reset (new Subtitle (sub)); + avsubtitle_free (&sub); } } @@ -358,105 +361,6 @@ FFmpegDecoder::sample_aspect_ratio_denominator () const return _video_codec_context->sample_aspect_ratio.den; } -void -FFmpegDecoder::overlay (shared_ptr image) const -{ - if (!_have_subtitle) { - return; - } - - /* subtitle PTS in seconds */ - float const packet_time = (_subtitle.pts / AV_TIME_BASE) + float (_subtitle.pts % AV_TIME_BASE) / 1e6; - /* hence start time for this sub */ - float const from = packet_time + (float (_subtitle.start_display_time) / 1e3); - float const to = packet_time + (float (_subtitle.end_display_time) / 1e3); - - float const video_frame_time = float (last_video_frame ()) / rint (_fs->frames_per_second); - - if (from > video_frame_time || video_frame_time < to) { - return; - } - - for (unsigned int i = 0; i < _subtitle.num_rects; ++i) { - AVSubtitleRect* rect = _subtitle.rects[i]; - if (rect->type != SUBTITLE_BITMAP) { - throw DecodeError ("non-bitmap subtitles not yet supported"); - } - - /* XXX: all this assumes YUV420 in image */ - - assert (rect->pict.data[0]); - - /* Start of the first line in the target image */ - uint8_t* frame_y_p = image->data()[0] + rect->y * image->line_size()[0]; - uint8_t* frame_u_p = image->data()[1] + (rect->y / 2) * image->line_size()[1]; - uint8_t* frame_v_p = image->data()[2] + (rect->y / 2) * image->line_size()[2]; - - int const hlim = min (rect->y + rect->h, image->size().height) - rect->y; - - /* Start of the first line in the subtitle */ - uint8_t* sub_p = rect->pict.data[0]; - /* sub_p looks up into a RGB palette which is here */ - uint32_t const * palette = (uint32_t *) rect->pict.data[1]; - - for (int sub_y = 0; sub_y < hlim; ++sub_y) { - /* Pointers to the start of this line */ - uint8_t* sub_line_p = sub_p; - uint8_t* frame_line_y_p = frame_y_p + rect->x; - uint8_t* frame_line_u_p = frame_u_p + (rect->x / 2); - uint8_t* frame_line_v_p = frame_v_p + (rect->x / 2); - - /* U and V are subsampled */ - uint8_t next_u = 0; - uint8_t next_v = 0; - int subsample_step = 0; - - for (int sub_x = 0; sub_x < rect->w; ++sub_x) { - - /* RGB value for this subtitle pixel */ - uint32_t const val = palette[*sub_line_p++]; - - int const red = (val & 0xff); - int const green = (val & 0xff00) >> 8; - int const blue = (val & 0xff0000) >> 16; - float const alpha = ((val & 0xff000000) >> 24) / 255.0; - - /* Alpha-blend Y */ - int const cy = *frame_line_y_p; - *frame_line_y_p++ = int (cy * (1 - alpha)) + int (RGB_TO_Y_CCIR (red, green, blue) * alpha); - - /* Store up U and V */ - next_u |= ((RGB_TO_U_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); - next_v |= ((RGB_TO_V_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step); - - if (subsample_step == 1 && (sub_y % 2) == 0) { - int const cu = *frame_line_u_p; - int const cv = *frame_line_v_p; - - *frame_line_u_p++ = - int (((cu & 0x0f) * (1 - alpha) + (next_u & 0x0f) * alpha)) | - int (((cu & 0xf0) * (1 - alpha) + (next_u & 0xf0) * alpha)); - - *frame_line_v_p++ = - int (((cv & 0x0f) * (1 - alpha) + (next_v & 0x0f) * alpha)) | - int (((cv & 0xf0) * (1 - alpha) + (next_v & 0xf0) * alpha)); - - next_u = next_v = 0; - } - - subsample_step = (subsample_step + 1) % 2; - } - - sub_p += rect->pict.linesize[0]; - frame_y_p += image->line_size()[0]; - if ((sub_y % 2) == 0) { - frame_u_p += image->line_size()[1]; - frame_v_p += image->line_size()[2]; - } - } - } -} - bool FFmpegDecoder::has_subtitles () const { diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 59ec7573d..e92b326b8 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -44,6 +44,7 @@ class FilmState; class Options; class Image; class Log; +class Subtitle; /** @class FFmpegDecoder * @brief A decoder using FFmpeg to decode content. @@ -73,7 +74,6 @@ private: int time_base_denominator () const; int sample_aspect_ratio_numerator () const; int sample_aspect_ratio_denominator () const; - void overlay (boost::shared_ptr image) const; void setup_general (); void setup_video (); @@ -96,6 +96,5 @@ private: AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle AVPacket _packet; - AVSubtitle _subtitle; - bool _have_subtitle; + boost::shared_ptr _subtitle; }; diff --git a/src/lib/image.cc b/src/lib/image.cc index 2df7636af..8fd43093d 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -57,6 +57,7 @@ Image::lines (int n) const } break; case PIX_FMT_RGB24: + case PIX_FMT_RGBA: return size().height; default: assert (false); @@ -73,6 +74,7 @@ Image::components () const case PIX_FMT_YUV420P: return 3; case PIX_FMT_RGB24: + case PIX_FMT_RGBA: return 1; default: assert (false); @@ -81,6 +83,31 @@ Image::components () const return 0; } +shared_ptr +Image::scale (Size out_size, Scaler const * scaler) const +{ + assert (scaler); + + shared_ptr scaled (new SimpleImage (pixel_format(), out_size)); + + struct SwsContext* scale_context = sws_getContext ( + size().width, size().height, pixel_format(), + out_size.width, out_size.height, pixel_format(), + scaler->ffmpeg_id (), 0, 0, 0 + ); + + sws_scale ( + scale_context, + data(), line_size(), + 0, size().height, + scaled->data (), scaled->line_size () + ); + + sws_freeContext (scale_context); + + return scaled; +} + /** Scale this image to a given size and convert it to RGB. * @param out_size Output image size in pixels. * @param scaler Scaler to use. @@ -192,10 +219,25 @@ SimpleImage::SimpleImage (PixelFormat p, Size s) { _data = (uint8_t **) av_malloc (components() * sizeof (uint8_t *)); _line_size = (int *) av_malloc (components() * sizeof (int)); + + switch (p) { + case PIX_FMT_RGB24: + _line_size[0] = s.width * 3; + break; + case PIX_FMT_RGBA: + _line_size[0] = s.width * 4; + break; + case PIX_FMT_YUV420P: + _line_size[0] = s.width; + _line_size[1] = s.width / 2; + _line_size[2] = s.width / 2; + break; + default: + assert (false); + } for (int i = 0; i < components(); ++i) { - _data[i] = 0; - _line_size[i] = 0; + _data[i] = (uint8_t *) av_malloc (_line_size[i] * lines (i)); } } @@ -210,17 +252,6 @@ SimpleImage::~SimpleImage () av_free (_line_size); } -/** Set the size in bytes of each horizontal line of a given component. - * @param i Component index. - * @param s Size of line in bytes. - */ -void -SimpleImage::set_line_size (int i, int s) -{ - _line_size[i] = s; - _data[i] = (uint8_t *) av_malloc (s * lines (i)); -} - uint8_t ** SimpleImage::data () const { diff --git a/src/lib/image.h b/src/lib/image.h index 0161d2b01..d10bcae9e 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -66,6 +66,7 @@ public: int components () const; int lines (int) const; boost::shared_ptr scale_and_convert_to_rgb (Size, int, Scaler const *) const; + boost::shared_ptr scale (Size, Scaler const *) const; boost::shared_ptr post_process (std::string) const; void make_black (); @@ -108,8 +109,6 @@ public: int * line_size () const; Size size () const; - void set_line_size (int, int); - private: Size _size; ///< size in pixels uint8_t** _data; ///< array of pointers to components diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index 7cee01ec5..df20479c9 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -1,3 +1,22 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + #include #include #include "imagemagick_decoder.h" @@ -5,6 +24,7 @@ #include "image.h" using namespace std; +using namespace boost; ImageMagickDecoder::ImageMagickDecoder ( boost::shared_ptr s, boost::shared_ptr o, Job* j, Log* l, bool minimal, bool ignore_length) @@ -41,7 +61,7 @@ ImageMagickDecoder::do_pass () } - process_video (image.frame ()); + process_video (image.frame (), shared_ptr ()); _done = true; return false; diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index 05dc7f113..809f3aecd 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -1,3 +1,22 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + #include "decoder.h" namespace Magick { diff --git a/src/lib/j2k_still_encoder.cc b/src/lib/j2k_still_encoder.cc index d218d08fe..a241dd6e3 100644 --- a/src/lib/j2k_still_encoder.cc +++ b/src/lib/j2k_still_encoder.cc @@ -48,11 +48,11 @@ J2KStillEncoder::J2KStillEncoder (shared_ptr s, shared_ptr yuv, int frame) +J2KStillEncoder::process_video (shared_ptr yuv, int frame, shared_ptr sub) { pair const s = Filter::ffmpeg_strings (_fs->filters); DCPVideoFrame* f = new DCPVideoFrame ( - yuv, _opt->out_size, _opt->padding, _fs->scaler, 0, _fs->frames_per_second, s.second, + yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, 0, _fs->frames_per_second, s.second, Config::instance()->colour_lut_index(), Config::instance()->j2k_bandwidth(), _log ); diff --git a/src/lib/j2k_still_encoder.h b/src/lib/j2k_still_encoder.h index 7a03e1195..c48b9e69c 100644 --- a/src/lib/j2k_still_encoder.h +++ b/src/lib/j2k_still_encoder.h @@ -37,7 +37,7 @@ public: J2KStillEncoder (boost::shared_ptr, boost::shared_ptr, Log *); void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format) {} - void process_video (boost::shared_ptr, int); + void process_video (boost::shared_ptr, int, boost::shared_ptr); void process_audio (uint8_t *, int) {} void process_end () {} }; diff --git a/src/lib/j2k_wav_encoder.cc b/src/lib/j2k_wav_encoder.cc index a1f70a08a..7bcac483b 100644 --- a/src/lib/j2k_wav_encoder.cc +++ b/src/lib/j2k_wav_encoder.cc @@ -105,7 +105,7 @@ J2KWAVEncoder::close_sound_files () } void -J2KWAVEncoder::process_video (shared_ptr yuv, int frame) +J2KWAVEncoder::process_video (shared_ptr yuv, int frame, shared_ptr sub) { boost::mutex::scoped_lock lock (_worker_mutex); @@ -126,7 +126,7 @@ J2KWAVEncoder::process_video (shared_ptr yuv, int frame) TIMING ("adding to queue of %1", _queue.size ()); _queue.push_back (boost::shared_ptr ( new DCPVideoFrame ( - yuv, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second, + yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second, Config::instance()->colour_lut_index (), Config::instance()->j2k_bandwidth (), _log ) diff --git a/src/lib/j2k_wav_encoder.h b/src/lib/j2k_wav_encoder.h index e11358c2c..87068ad3d 100644 --- a/src/lib/j2k_wav_encoder.h +++ b/src/lib/j2k_wav_encoder.h @@ -38,6 +38,7 @@ class ServerDescription; class DCPVideoFrame; class Image; class Log; +class Subtitle; /** @class J2KWAVEncoder * @brief An encoder which writes JPEG2000 and WAV files. @@ -49,7 +50,7 @@ public: ~J2KWAVEncoder (); void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format); - void process_video (boost::shared_ptr, int); + void process_video (boost::shared_ptr, int, boost::shared_ptr); void process_audio (uint8_t *, int); void process_end (); diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc index 562c887de..2db91a177 100644 --- a/src/lib/job_manager.cc +++ b/src/lib/job_manager.cc @@ -95,6 +95,11 @@ JobManager::scheduler () { boost::mutex::scoped_lock lm (_mutex); for (list >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) { + if ((*i)->running ()) { + /* Something is already happening */ + break; + } + if ((*i)->is_new()) { shared_ptr r = (*i)->required (); if (!r || r->finished_ok ()) { diff --git a/src/lib/options.h b/src/lib/options.h index 39068c24f..2fc5f77ff 100644 --- a/src/lib/options.h +++ b/src/lib/options.h @@ -56,11 +56,15 @@ public: * @param t true to return a temporary file path, otherwise a permanent one. * @return The path to write this video frame to. */ - std::string frame_out_path (int f, bool t) const { + std::string frame_out_path (int f, bool t, std::string e = "") const { + if (e.empty ()) { + e = _frame_out_extension; + } + std::stringstream s; s << _frame_out_path << "/"; s.width (8); - s << std::setfill('0') << f << _frame_out_extension; + s << std::setfill('0') << f << e; if (t) { s << ".tmp"; diff --git a/src/lib/server.cc b/src/lib/server.cc index 28236e3e0..76989d078 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -114,17 +114,15 @@ Server::process (shared_ptr socket) shared_ptr image (new SimpleImage (pixel_format, in_size)); - for (int i = 0; i < image->components(); ++i) { - int line_size; - s >> line_size; - image->set_line_size (i, line_size); - } - for (int i = 0; i < image->components(); ++i) { socket->read_definite_and_consume (image->data()[i], image->line_size()[i] * image->lines(i), 30); } + + /* XXX: subtitle */ + DCPVideoFrame dcp_video_frame ( + image, shared_ptr (), out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log + ); - DCPVideoFrame dcp_video_frame (image, out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log); shared_ptr encoded = dcp_video_frame.encode_locally (); encoded->send (socket); diff --git a/src/lib/tiff_decoder.cc b/src/lib/tiff_decoder.cc index 101e0c047..116c65f7b 100644 --- a/src/lib/tiff_decoder.cc +++ b/src/lib/tiff_decoder.cc @@ -179,7 +179,7 @@ TIFFDecoder::do_pass () _TIFFfree (raster); TIFFClose (t); - process_video (image.frame ()); + process_video (image.frame (), shared_ptr ()); ++_iter; return false; diff --git a/src/lib/tiff_encoder.cc b/src/lib/tiff_encoder.cc index 19e34741d..9aa7b68c3 100644 --- a/src/lib/tiff_encoder.cc +++ b/src/lib/tiff_encoder.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "tiff_encoder.h" @@ -34,6 +35,7 @@ #include "options.h" #include "exceptions.h" #include "image.h" +#include "subtitle.h" using namespace std; using namespace boost; @@ -49,7 +51,7 @@ TIFFEncoder::TIFFEncoder (shared_ptr s, shared_ptr image, int frame) +TIFFEncoder::process_video (shared_ptr image, int frame, shared_ptr sub) { shared_ptr scaled = image->scale_and_convert_to_rgb (_opt->out_size, _opt->padding, _fs->scaler); string tmp_file = _opt->frame_out_path (frame, true); @@ -72,6 +74,56 @@ TIFFEncoder::process_video (shared_ptr image, int frame) TIFFClose (output); - boost::filesystem::rename (tmp_file, _opt->frame_out_path (frame, false)); + filesystem::rename (tmp_file, _opt->frame_out_path (frame, false)); + + if (sub) { + float const x_scale = float (_opt->out_size.width) / _fs->size.width; + float const y_scale = float (_opt->out_size.height) / _fs->size.height; + + string tmp_metadata_file = _opt->frame_out_path (frame, false, ".sub"); + ofstream metadata (tmp_metadata_file.c_str ()); + + list > images = sub->images (); + int n = 0; + for (list >::iterator i = images.begin(); i != images.end(); ++i) { + stringstream ext; + ext << ".sub." << n << ".tiff"; + + string tmp_sub_file = _opt->frame_out_path (frame, true, ext.str ()); + output = TIFFOpen (tmp_sub_file.c_str(), "w"); + if (output == 0) { + throw CreateFileError (tmp_file); + } + + Size new_size = (*i)->image()->size (); + new_size.width *= x_scale; + new_size.height *= y_scale; + shared_ptr scaled = (*i)->image()->scale (new_size, _fs->scaler); + + TIFFSetField (output, TIFFTAG_IMAGEWIDTH, scaled->size().width); + TIFFSetField (output, TIFFTAG_IMAGELENGTH, scaled->size().height); + TIFFSetField (output, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField (output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField (output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField (output, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField (output, TIFFTAG_SAMPLESPERPIXEL, 4); + + if (TIFFWriteEncodedStrip (output, 0, scaled->data()[0], scaled->size().width * scaled->size().height * 4) == 0) { + throw WriteFileError (tmp_file, 0); + } + + TIFFClose (output); + filesystem::rename (tmp_sub_file, _opt->frame_out_path (frame, false, ext.str ())); + + metadata << "image " << n << "\n" + << "x " << (*i)->position().x << "\n" + << "y " << (*i)->position().y << "\n"; + + metadata.close (); + filesystem::rename (tmp_metadata_file, _opt->frame_out_path (frame, false, ".sub")); + } + + } + frame_done (frame); } diff --git a/src/lib/tiff_encoder.h b/src/lib/tiff_encoder.h index ef1ce25d2..1c9f33f4a 100644 --- a/src/lib/tiff_encoder.h +++ b/src/lib/tiff_encoder.h @@ -37,7 +37,7 @@ public: TIFFEncoder (boost::shared_ptr s, boost::shared_ptr o, Log* l); void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format) {} - void process_video (boost::shared_ptr, int); + void process_video (boost::shared_ptr, int, boost::shared_ptr); void process_audio (uint8_t *, int) {} void process_end () {} }; diff --git a/src/lib/wscript b/src/lib/wscript index c809226ce..67292047c 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -42,6 +42,7 @@ def build(bld): screen.cc server.cc sound_processor.cc + subtitle.cc thumbs_job.cc tiff_decoder.cc tiff_encoder.cc diff --git a/test/test.cc b/test/test.cc index c801d538e..b14935da3 100644 --- a/test/test.cc +++ b/test/test.cc @@ -270,7 +270,6 @@ do_remote_encode (shared_ptr frame, ServerDescription* descriptio BOOST_AUTO_TEST_CASE (client_server_test) { shared_ptr image (new SimpleImage (PIX_FMT_RGB24, Size (1998, 1080))); - image->set_line_size (0, 1998 * 3); uint8_t* p = image->data()[0]; @@ -287,6 +286,7 @@ BOOST_AUTO_TEST_CASE (client_server_test) shared_ptr frame ( new DCPVideoFrame ( image, + shared_ptr (), Size (1998, 1080), 0, Scaler::from_id ("bicubic"), -- cgit v1.2.3 From a066feba1b455a72fe10b7baa79f17f69cd24ba9 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 16:50:08 +0100 Subject: Various fixes to subtitling. --- run/dvdomatic | 6 ++++-- src/lib/dcp_video_frame.cc | 30 +++++++++++++++++++++++++----- src/lib/dcp_video_frame.h | 4 +++- src/lib/decoder.cc | 24 +++++++++++++++++------- src/lib/decoder.h | 5 ++++- src/lib/ffmpeg_decoder.cc | 18 ++++-------------- src/lib/ffmpeg_decoder.h | 1 - src/lib/film.cc | 1 + src/lib/image.cc | 42 ++++++++++++++++++++++++++++++++++++++++-- src/lib/image.h | 1 + src/lib/imagemagick_decoder.cc | 2 +- src/lib/j2k_still_encoder.cc | 2 +- src/lib/j2k_wav_encoder.cc | 3 ++- src/lib/options.h | 2 ++ src/lib/server.cc | 7 ++++++- src/lib/subtitle.cc | 25 +++++++++++++++---------- src/lib/subtitle.h | 5 +++-- src/lib/tiff_decoder.cc | 2 +- src/wx/film_viewer.cc | 3 ++- test/test.cc | 2 ++ 20 files changed, 134 insertions(+), 51 deletions(-) (limited to 'src/lib/ffmpeg_decoder.h') diff --git a/run/dvdomatic b/run/dvdomatic index eeb2c7b44..125fd9d27 100755 --- a/run/dvdomatic +++ b/run/dvdomatic @@ -2,9 +2,11 @@ export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH if [ "$1" == "--debug" ]; then - gdb --args build/src/tools/dvdomatic $2 + shift + gdb --args build/src/tools/dvdomatic "$1" elif [ "$1" == "--valgrind" ]; then - valgrind --tool="memcheck" build/src/tools/dvdomatic $2 + shift + valgrind --tool="memcheck" build/src/tools/dvdomatic "$1" else build/src/tools/dvdomatic "$1" fi diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index 13d3efcbf..ce660add5 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -55,6 +55,7 @@ #include "scaler.h" #include "image.h" #include "log.h" +#include "subtitle.h" using namespace std; using namespace boost; @@ -72,11 +73,16 @@ using namespace boost; * @param l Log to write to. */ DCPVideoFrame::DCPVideoFrame ( - shared_ptr yuv, shared_ptr sub, Size out, int p, Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l) + shared_ptr yuv, shared_ptr sub, + Size out, int p, int subtitle_offset, float subtitle_scale, + Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l + ) : _input (yuv) , _subtitle (sub) , _out_size (out) , _padding (p) + , _subtitle_offset (subtitle_offset) + , _subtitle_scale (subtitle_scale) , _scaler (s) , _frame (f) /* we round here; not sure if this is right */ @@ -148,13 +154,25 @@ DCPVideoFrame::~DCPVideoFrame () shared_ptr DCPVideoFrame::encode_locally () { - shared_ptr prepared = _input; - if (!_post_process.empty ()) { - prepared = prepared->post_process (_post_process); + _input = _input->post_process (_post_process); } - prepared = prepared->scale_and_convert_to_rgb (_out_size, _padding, _scaler); + shared_ptr prepared = _input->scale_and_convert_to_rgb (_out_size, _padding, _scaler); + + if (_subtitle) { + list > subs = _subtitle->images (); + for (list >::iterator i = subs.begin(); i != subs.end(); ++i) { + Rectangle tx = transformed_subtitle_area ( + float (_out_size.width) / _input->size().width, + float (_out_size.height) / _input->size().height, + (*i)->area(), _subtitle_offset, _subtitle_scale + ); + + shared_ptr im = (*i)->image()->scale (Size (tx.w, tx.h), _scaler); + prepared->alpha_blend (im, Position (tx.x, tx.y)); + } + } create_openjpeg_container (); @@ -290,6 +308,8 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv) << _input->pixel_format() << " " << _out_size.width << " " << _out_size.height << " " << _padding << " " + << _subtitle_offset << " " + << _subtitle_scale << " " << _scaler->id () << " " << _frame << " " << _frames_per_second << " " diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h index fe2e27966..4e9a777bd 100644 --- a/src/lib/dcp_video_frame.h +++ b/src/lib/dcp_video_frame.h @@ -106,7 +106,7 @@ public: class DCPVideoFrame { public: - DCPVideoFrame (boost::shared_ptr, boost::shared_ptr, Size, int, Scaler const *, int, float, std::string, int, int, Log *); + DCPVideoFrame (boost::shared_ptr, boost::shared_ptr, Size, int, int, float, Scaler const *, int, float, std::string, int, int, Log *); virtual ~DCPVideoFrame (); boost::shared_ptr encode_locally (); @@ -124,6 +124,8 @@ private: boost::shared_ptr _subtitle; ///< any subtitle that should be on the image Size _out_size; ///< the required size of the output, in pixels int _padding; + int _subtitle_offset; + float _subtitle_scale; Scaler const * _scaler; ///< scaler to use int _frame; ///< frame index within the Film int _frames_per_second; ///< Frames per second that we will use for the DCP (rounded) diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index c4759a872..1f771da2d 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -229,7 +229,7 @@ Decoder::process_audio (uint8_t* data, int size) * @param frame to decode; caller manages memory. */ void -Decoder::process_video (AVFrame* frame, shared_ptr sub) +Decoder::process_video (AVFrame* frame) { if (_minimal) { ++_video_frame; @@ -304,12 +304,9 @@ Decoder::process_video (AVFrame* frame, shared_ptr sub) image->make_black (); } - if (sub && _opt->apply_crop) { - list > im = sub->images (); - for (list >::iterator i = im.begin(); i != im.end(); ++i) { - Position p = (*i)->position (); - (*i)->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top)); - } + shared_ptr sub; + if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { + sub = _subtitle; } TIMING ("Decoder emits %1", _video_frame); @@ -414,3 +411,16 @@ Decoder::setup_video_filters () /* XXX: leaking `inputs' / `outputs' ? */ } +void +Decoder::process_subtitle (shared_ptr s) +{ + _subtitle = s; + + if (_opt->apply_crop) { + list > im = _subtitle->images (); + for (list >::iterator i = im.begin(); i != im.end(); ++i) { + Position const p = (*i)->position (); + (*i)->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top)); + } + } +} diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 5cb44b8d9..805955b9d 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -101,8 +101,9 @@ protected: virtual int sample_aspect_ratio_numerator () const = 0; virtual int sample_aspect_ratio_denominator () const = 0; - void process_video (AVFrame *, boost::shared_ptr); + void process_video (AVFrame *); void process_audio (uint8_t *, int); + void process_subtitle (boost::shared_ptr); /** our FilmState */ boost::shared_ptr _fs; @@ -138,6 +139,8 @@ private: (at the DCP sample rate). */ int64_t _audio_frames_processed; + + boost::shared_ptr _subtitle; }; #endif diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index c2ee9297b..e01405191 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -205,12 +205,7 @@ FFmpegDecoder::do_pass () int frame_finished; while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - shared_ptr s; - if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { - s = _subtitle; - } - - process_video (_frame, s); + process_video (_frame); } if (_audio_stream >= 0 && _opt->decode_audio) { @@ -231,12 +226,7 @@ FFmpegDecoder::do_pass () int frame_finished; if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - shared_ptr s; - if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { - s = _subtitle; - } - - process_video (_frame, s); + process_video (_frame); } } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio) { @@ -253,12 +243,12 @@ FFmpegDecoder::do_pass () process_audio (_frame->data[0], data_size); } - } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream) { + } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles) { int got_subtitle; AVSubtitle sub; if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) { - _subtitle.reset (new Subtitle (sub)); + process_subtitle (shared_ptr (new Subtitle (sub))); avsubtitle_free (&sub); } } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index e92b326b8..d34c22785 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -96,5 +96,4 @@ private: AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle AVPacket _packet; - boost::shared_ptr _subtitle; }; diff --git a/src/lib/film.cc b/src/lib/film.cc index e4155c9f6..31af2f1c2 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -535,6 +535,7 @@ Film::make_dcp (bool transcode, int freq) o->decode_video_frequency = freq; o->padding = format()->dcp_padding (this); o->ratio = format()->ratio_as_float (this); + o->decode_subtitles = with_subtitles (); shared_ptr r; diff --git a/src/lib/image.cc b/src/lib/image.cc index 602b20842..ce44ec95b 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -207,6 +207,42 @@ Image::make_black () } } +void +Image::alpha_blend (shared_ptr other, Position position) +{ + /* Only implemented for RGBA onto RGB24 so far */ + assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA); + + int start_tx = position.x; + int start_ox = 0; + + if (start_tx < 0) { + start_ox = -start_tx; + start_tx = 0; + } + + int start_ty = position.y; + int start_oy = 0; + + if (start_ty < 0) { + start_oy = -start_ty; + start_ty = 0; + } + + for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) { + uint8_t* tp = data()[0] + ty * line_size()[0] + position.x * 3; + uint8_t* op = other->data()[0] + oy * other->line_size()[0]; + for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) { + float const alpha = float (op[3]) / 255; + tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha; + tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha; + tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha; + tp += 3; + op += 4; + } + } +} + /** Construct a SimpleImage of a given size and format, allocating memory * as required. * @@ -217,8 +253,10 @@ SimpleImage::SimpleImage (PixelFormat p, Size s) : Image (p) , _size (s) { - _data = (uint8_t **) av_malloc (components() * sizeof (uint8_t *)); - _line_size = (int *) av_malloc (components() * sizeof (int)); + _data = (uint8_t **) av_malloc (4 * sizeof (uint8_t *)); + _data[0] = _data[1] = _data[2] = _data[3] = 0; + _line_size = (int *) av_malloc (4); + _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0; switch (p) { case PIX_FMT_RGB24: diff --git a/src/lib/image.h b/src/lib/image.h index 970750719..ea35fa0b9 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -68,6 +68,7 @@ public: boost::shared_ptr scale_and_convert_to_rgb (Size, int, Scaler const *) const; boost::shared_ptr scale (Size, Scaler const *) const; boost::shared_ptr post_process (std::string) const; + void alpha_blend (boost::shared_ptr image, Position pos); void make_black (); diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index df20479c9..32c433d09 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -61,7 +61,7 @@ ImageMagickDecoder::do_pass () } - process_video (image.frame (), shared_ptr ()); + process_video (image.frame ()); _done = true; return false; diff --git a/src/lib/j2k_still_encoder.cc b/src/lib/j2k_still_encoder.cc index a241dd6e3..2b8aca649 100644 --- a/src/lib/j2k_still_encoder.cc +++ b/src/lib/j2k_still_encoder.cc @@ -52,7 +52,7 @@ J2KStillEncoder::process_video (shared_ptr yuv, int frame, shared_ptr const s = Filter::ffmpeg_strings (_fs->filters); DCPVideoFrame* f = new DCPVideoFrame ( - yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, 0, _fs->frames_per_second, s.second, + yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale, _fs->scaler, 0, _fs->frames_per_second, s.second, Config::instance()->colour_lut_index(), Config::instance()->j2k_bandwidth(), _log ); diff --git a/src/lib/j2k_wav_encoder.cc b/src/lib/j2k_wav_encoder.cc index 7bcac483b..e2a3a5ed7 100644 --- a/src/lib/j2k_wav_encoder.cc +++ b/src/lib/j2k_wav_encoder.cc @@ -126,7 +126,8 @@ J2KWAVEncoder::process_video (shared_ptr yuv, int frame, shared_ptr ( new DCPVideoFrame ( - yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second, + yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale, + _fs->scaler, frame, _fs->frames_per_second, s.second, Config::instance()->colour_lut_index (), Config::instance()->j2k_bandwidth (), _log ) diff --git a/src/lib/options.h b/src/lib/options.h index 2fc5f77ff..86db35210 100644 --- a/src/lib/options.h +++ b/src/lib/options.h @@ -42,6 +42,7 @@ public: , black_after (0) , decode_video_frequency (0) , decode_audio (true) + , decode_subtitles (false) , _frame_out_path (f) , _frame_out_extension (e) , _multichannel_audio_out_path (m) @@ -99,6 +100,7 @@ public: int black_after; ///< first frame for which to output a black frame, rather than the actual video content, or 0 for none int decode_video_frequency; ///< skip frames so that this many are decoded in all (or 0) (for generating thumbnails) bool decode_audio; ///< true to decode audio, otherwise false + bool decode_subtitles; private: /** Path of the directory to write video frames to */ diff --git a/src/lib/server.cc b/src/lib/server.cc index 2fda5952f..26b2be7c7 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -88,6 +88,8 @@ Server::process (shared_ptr socket) int pixel_format_int; Size out_size; int padding; + int subtitle_offset; + int subtitle_scale; string scaler_id; int frame; float frames_per_second; @@ -99,6 +101,8 @@ Server::process (shared_ptr socket) >> pixel_format_int >> out_size.width >> out_size.height >> padding + >> subtitle_offset + >> subtitle_scale >> scaler_id >> frame >> frames_per_second @@ -120,7 +124,8 @@ Server::process (shared_ptr socket) /* XXX: subtitle */ DCPVideoFrame dcp_video_frame ( - image, shared_ptr (), out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log + image, shared_ptr (), out_size, padding, subtitle_offset, subtitle_scale, + scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log ); shared_ptr encoded = dcp_video_frame.encode_locally (); diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc index 0ea5a72d5..f0d77c511 100644 --- a/src/lib/subtitle.cc +++ b/src/lib/subtitle.cc @@ -73,34 +73,39 @@ SubtitleImage::SubtitleImage (AVSubtitleRect const * rect) Rectangle transformed_subtitle_area ( float target_x_scale, float target_y_scale, - Rectangle sub_area, - shared_ptr fs + Rectangle sub_area, int subtitle_offset, float subtitle_scale ) { Rectangle tx; - sub_area.y += fs->subtitle_offset; + sub_area.y += subtitle_offset; /* We will scale the subtitle by the same amount as the video frame, and also by the additional subtitle_scale */ - tx.w = sub_area.w * target_x_scale * fs->subtitle_scale; - tx.h = sub_area.h * target_y_scale * fs->subtitle_scale; + tx.w = sub_area.w * target_x_scale * subtitle_scale; + tx.h = sub_area.h * target_y_scale * subtitle_scale; /* Then we need a corrective translation, consisting of two parts: * * 1. that which is the result of the scaling of the subtitle by target_x_scale and target_y_scale; this will be * sub_area.x * target_x_scale and sub_area.y * target_y_scale. * - * 2. that to shift the origin of the scale by fs->subtitle_scale to the centre of the subtitle; this will be - * (width_before_subtitle_scale * (1 - fs->subtitle_scale) / 2) and - * (height_before_subtitle_scale * (1 - fs->subtitle_scale) / 2). + * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be + * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and + * (height_before_subtitle_scale * (1 - subtitle_scale) / 2). * * Combining these two translations gives these expressions. */ - tx.x = target_x_scale * (sub_area.x + (sub_area.w * (1 - fs->subtitle_scale) / 2)); - tx.y = target_y_scale * (sub_area.y + (sub_area.h * (1 - fs->subtitle_scale) / 2)); + tx.x = target_x_scale * (sub_area.x + (sub_area.w * (1 - subtitle_scale) / 2)); + tx.y = target_y_scale * (sub_area.y + (sub_area.h * (1 - subtitle_scale) / 2)); return tx; } + +Rectangle +SubtitleImage::area () const +{ + return Rectangle (_position.x, _position.y, _image->size().width, _image->size().height); +} diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h index d9717564e..6fd0d8772 100644 --- a/src/lib/subtitle.h +++ b/src/lib/subtitle.h @@ -47,8 +47,7 @@ private: extern Rectangle transformed_subtitle_area ( float target_x_scale, float target_y_scale, - Rectangle sub_area, - boost::shared_ptr fs + Rectangle sub_area, int subtitle_offset, float subtitle_scale ); class SubtitleImage @@ -68,6 +67,8 @@ public: return _image; } + Rectangle area () const; + private: Position _position; boost::shared_ptr _image; diff --git a/src/lib/tiff_decoder.cc b/src/lib/tiff_decoder.cc index 116c65f7b..101e0c047 100644 --- a/src/lib/tiff_decoder.cc +++ b/src/lib/tiff_decoder.cc @@ -179,7 +179,7 @@ TIFFDecoder::do_pass () _TIFFfree (raster); TIFFClose (t); - process_video (image.frame (), shared_ptr ()); + process_video (image.frame ()); ++_iter; return false; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index bfeb44cb9..bf082adc2 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -179,7 +179,7 @@ private: for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { i->transformed_area = transformed_subtitle_area ( - x_scale, y_scale, i->base_area, _film->state_copy() + x_scale, y_scale, i->base_area, _film->subtitle_offset(), _film->subtitle_scale() ); i->transformed_image = i->base_image; @@ -328,6 +328,7 @@ FilmViewer::update_thumbs () o->apply_crop = false; o->decode_audio = false; o->decode_video_frequency = 128; + o->decode_subtitles = true; shared_ptr j (new ThumbsJob (s, o, _film->log(), shared_ptr ())); j->Finished.connect (sigc::mem_fun (_film, &Film::update_thumbs_post_gui)); diff --git a/test/test.cc b/test/test.cc index 789724b53..aaa911b32 100644 --- a/test/test.cc +++ b/test/test.cc @@ -290,6 +290,8 @@ BOOST_AUTO_TEST_CASE (client_server_test) shared_ptr (), Size (1998, 1080), 0, + 0, + 0, Scaler::from_id ("bicubic"), 0, 24, -- cgit v1.2.3