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.cc | 128 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib/ffmpeg_decoder.h | 10 +++- src/lib/util.h | 16 ++++++ src/wx/film_viewer.cc | 2 +- 4 files changed, 153 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 1096bb253..5167ce488 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -56,15 +56,20 @@ FFmpegDecoder::FFmpegDecoder (boost::shared_ptr s, boost::share , _format_context (0) , _video_stream (-1) , _audio_stream (-1) + , _subtitle_stream (-1) , _frame (0) , _video_codec_context (0) , _video_codec (0) , _audio_codec_context (0) , _audio_codec (0) + , _subtitle_codec_context (0) + , _subtitle_codec (0) + , _have_subtitle (false) { setup_general (); setup_video (); setup_audio (); + setup_subtitle (); } FFmpegDecoder::~FFmpegDecoder () @@ -76,6 +81,14 @@ FFmpegDecoder::~FFmpegDecoder () if (_video_codec_context) { avcodec_close (_video_codec_context); } + + if (_have_subtitle) { + avsubtitle_free (&_subtitle); + } + + if (_subtitle_codec_context) { + avcodec_close (_subtitle_codec_context); + } av_free (_frame); avformat_close_input (&_format_context); @@ -101,6 +114,8 @@ FFmpegDecoder::setup_general () _video_stream = i; } else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { _audio_stream = i; + } else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { + _subtitle_stream = i; } } @@ -156,6 +171,26 @@ FFmpegDecoder::setup_audio () } } +void +FFmpegDecoder::setup_subtitle () +{ + if (_subtitle_stream < 0) { + return; + } + + _subtitle_codec_context = _format_context->streams[_subtitle_stream]->codec; + _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id); + + if (_subtitle_codec == 0) { + throw DecodeError ("could not find subtitle decoder"); + } + + if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) { + throw DecodeError ("could not open subtitle decoder"); + } +} + + bool FFmpegDecoder::do_pass () { @@ -174,6 +209,7 @@ FFmpegDecoder::do_pass () if (_opt->decode_video) { while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { + process_video (_frame); } } @@ -196,6 +232,85 @@ FFmpegDecoder::do_pass () int frame_finished; if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { + + cout << "decoded some video.\n"; + if (_have_subtitle) { + cout << "have a subtitle; " << _subtitle.num_rects << "\n"; + for (unsigned int i = 0; i < _subtitle.num_rects; ++i) { + AVSubtitleRect* rect = _subtitle.rects[i]; + if (rect->type != SUBTITLE_BITMAP) { + cout << "not a bitmap\n"; + 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]; + + cout << "frame ls 0 is " << _frame->linesize[0] << "\n"; + cout << "frame ls 1 is " << _frame->linesize[1] << "\n"; + cout << "frame ls 2 is " << _frame->linesize[2] << "\n"; + + uint32_t* palette = (uint32_t *) rect->pict.data[1]; + + for (int sub_y = 0; sub_y < rect->h; ++sub_y) { + 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); + + 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) { + + 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; + int const alpha = (val & 0xff000000) >> 24; + + if (alpha) { + *frame_line_y_p = RGB_TO_Y_CCIR (red, green, blue); + } + frame_line_y_p++; + + 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) { + if (alpha) { + *frame_line_u_p = current_u; + *frame_line_v_p = current_v; + } + frame_line_u_p++; + frame_line_v_p++; + 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]; + } + } + } + } + process_video (_frame); } @@ -212,6 +327,19 @@ FFmpegDecoder::do_pass () assert (_audio_codec_context->channels == _fs->audio_channels); process_audio (_frame->data[0], data_size); } + + } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream) { + + if (_have_subtitle) { + avsubtitle_free (&_subtitle); + _have_subtitle = false; + } + + int got_subtitle; + if (avcodec_decode_subtitle2 (_subtitle_codec_context, &_subtitle, &got_subtitle, &_packet) && got_subtitle) { + cout << "got a subtitle.\n"; + _have_subtitle = true; + } } av_free_packet (&_packet); 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; }; diff --git a/src/lib/util.h b/src/lib/util.h index 3eac06e97..ed13cd43c 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -165,4 +165,20 @@ private: int _buffer_data; }; +#define SCALEBITS 10 +#define ONE_HALF (1 << (SCALEBITS - 1)) +#define FIX(x) ((int) ((x) * (1<> SCALEBITS) + +#define RGB_TO_U_CCIR(r1, g1, b1, shift)\ +(((- FIX(0.16874*224.0/255.0) * r1 - FIX(0.33126*224.0/255.0) * g1 + \ + FIX(0.50000*224.0/255.0) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) + +#define RGB_TO_V_CCIR(r1, g1, b1, shift)\ +(((FIX(0.50000*224.0/255.0) * r1 - FIX(0.41869*224.0/255.0) * g1 - \ + FIX(0.08131*224.0/255.0) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) + #endif diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 3c7d76bce..817657abb 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -224,7 +224,7 @@ FilmViewer::film_changed (Film::Property p) _thumb_panel->refresh (); } else if (p == Film::CONTENT) { setup_visibility (); - _film->examine_content (); +// _film->examine_content (); update_thumbs (); } } -- cgit v1.2.3 From e9c88f5652a79b25adafc65fce6282a1d824ed16 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 8 Oct 2012 15:41:39 +0100 Subject: Do alpha blending maybe right. --- src/lib/ffmpeg_decoder.cc | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 5167ce488..8feb36623 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -275,26 +275,24 @@ FFmpegDecoder::do_pass () for (int sub_x = 0; sub_x < rect->w; ++sub_x) { 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; - int const alpha = (val & 0xff000000) >> 24; + + 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; - if (alpha) { - *frame_line_y_p = RGB_TO_Y_CCIR (red, green, blue); - } - frame_line_y_p++; + int const cy = *frame_line_y_p; + + *frame_line_y_p++ = int (cy * (1 - alpha)) + int (RGB_TO_Y_CCIR (red, green, blue) * alpha); 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) { - if (alpha) { - *frame_line_u_p = current_u; - *frame_line_v_p = current_v; - } - frame_line_u_p++; - frame_line_v_p++; + 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; } -- cgit v1.2.3 From 04dbff6ab81741748d81721656e565788c4afc6b Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 8 Oct 2012 15:43:56 +0100 Subject: Tidy up a bit. --- src/lib/ffmpeg_decoder.cc | 21 +++++++++------------ src/wx/film_viewer.cc | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 8feb36623..8dbec87fd 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -233,13 +233,10 @@ FFmpegDecoder::do_pass () int frame_finished; if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - cout << "decoded some video.\n"; if (_have_subtitle) { - cout << "have a subtitle; " << _subtitle.num_rects << "\n"; for (unsigned int i = 0; i < _subtitle.num_rects; ++i) { AVSubtitleRect* rect = _subtitle.rects[i]; if (rect->type != SUBTITLE_BITMAP) { - cout << "not a bitmap\n"; throw DecodeError ("non-bitmap subtitles not yet supported"); } @@ -255,25 +252,24 @@ FFmpegDecoder::do_pass () /* Start of the first line in the subtitle */ uint8_t* sub_p = rect->pict.data[0]; - - cout << "frame ls 0 is " << _frame->linesize[0] << "\n"; - cout << "frame ls 1 is " << _frame->linesize[1] << "\n"; - cout << "frame ls 2 is " << _frame->linesize[2] << "\n"; - - uint32_t* palette = (uint32_t *) rect->pict.data[1]; + /* 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); @@ -281,14 +277,16 @@ FFmpegDecoder::do_pass () 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); @@ -335,7 +333,6 @@ FFmpegDecoder::do_pass () int got_subtitle; if (avcodec_decode_subtitle2 (_subtitle_codec_context, &_subtitle, &got_subtitle, &_packet) && got_subtitle) { - cout << "got a subtitle.\n"; _have_subtitle = true; } } diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 817657abb..3c7d76bce 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -224,7 +224,7 @@ FilmViewer::film_changed (Film::Property p) _thumb_panel->refresh (); } else if (p == Film::CONTENT) { setup_visibility (); -// _film->examine_content (); + _film->examine_content (); update_thumbs (); } } -- 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') 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') 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') 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') 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 ed70b4faf0f53b106aebd4b9195ccc81da97880e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 10 Oct 2012 20:35:05 +0100 Subject: Thumbs sort of have subs. --- src/lib/film.cc | 45 +++++++++++++++++++ src/lib/film.h | 1 + src/lib/film_state.cc | 14 +++++- src/lib/film_state.h | 2 + src/lib/util.cc | 13 ++++++ src/lib/util.h | 25 +++++++++++ src/wx/film_viewer.cc | 122 ++++++++++++++++++++++++++++++++++++++------------ 7 files changed, 193 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/lib/film.cc b/src/lib/film.cc index 95dc7b825..1e23c4d6b 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "film.h" #include "format.h" #include "tiff_encoder.h" @@ -668,3 +669,47 @@ Film::set_with_subtitles (bool w) _state.with_subtitles = w; signal_changed (WITH_SUBTITLES); } + +list > +Film::thumb_subtitles (int n) const +{ + string sub_file = _state.thumb_base(n) + ".sub"; + if (!filesystem::exists (sub_file)) { + return list > (); + } + + ifstream f (sub_file.c_str ()); + string line; + + int sub_number; + int sub_x; + list > subs; + + while (getline (f, line)) { + if (line.empty ()) { + continue; + } + + if (line[line.size() - 1] == '\r') { + line = line.substr (0, line.size() - 1); + } + + size_t const s = line.find (' '); + if (s == string::npos) { + continue; + } + + string const k = line.substr (0, s); + int const v = lexical_cast (line.substr(s + 1)); + + if (k == "image") { + sub_number = v; + } else if (k == "x") { + sub_x = v; + } else if (k == "y") { + subs.push_back (make_pair (Position (sub_x, v), String::compose ("%1.sub.%2.tiff", _state.thumb_base(n), sub_number))); + } + } + + return subs; +} diff --git a/src/lib/film.h b/src/lib/film.h index 919cecc22..1e01747a2 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -193,6 +193,7 @@ public: int num_thumbs () const; int thumb_frame (int) const; std::string thumb_file (int) const; + std::list > thumb_subtitles (int) const; void copy_from_dvd_post_gui (); void examine_content (); diff --git a/src/lib/film_state.cc b/src/lib/film_state.cc index 03d4cae83..2847ea513 100644 --- a/src/lib/film_state.cc +++ b/src/lib/film_state.cc @@ -190,10 +190,22 @@ FilmState::thumb_file (int n) const */ string FilmState::thumb_file_for_frame (int n) const +{ + return thumb_base_for_frame(n) + ".tiff"; +} + +string +FilmState::thumb_base (int n) const +{ + return thumb_base_for_frame (thumb_frame (n)); +} + +string +FilmState::thumb_base_for_frame (int n) const { stringstream s; s.width (8); - s << setfill('0') << n << ".tiff"; + s << setfill('0') << n; filesystem::path p; p /= dir ("thumbs"); diff --git a/src/lib/film_state.h b/src/lib/film_state.h index 2b792694c..5b3ef8367 100644 --- a/src/lib/film_state.h +++ b/src/lib/film_state.h @@ -79,6 +79,7 @@ public: bool content_is_dvd () const; std::string thumb_file (int) const; + std::string thumb_base (int) const; int thumb_frame (int) const; int bytes_per_sample () const; @@ -151,6 +152,7 @@ public: private: std::string thumb_file_for_frame (int) const; + std::string thumb_base_for_frame (int) const; }; #endif diff --git a/src/lib/util.cc b/src/lib/util.cc index 935566440..82aaf8ff5 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -600,3 +600,16 @@ Socket::read_indefinite (uint8_t* data, int size, int timeout) assert (size >= _buffer_data); memcpy (data, _buffer, size); } + +Rectangle +Rectangle::intersection (Rectangle const & other) const +{ + int const tx = max (x, other.x); + int const ty = max (y, other.y); + + return Rectangle ( + tx, ty, + min (x + w, other.x + other.w) - tx, + min (y + h, other.y + other.h) - ty + ); +} diff --git a/src/lib/util.h b/src/lib/util.h index ed13cd43c..e1ad7fd64 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -121,6 +121,31 @@ struct Position int y; }; +/** A rectangle */ +struct Rectangle +{ + Rectangle () + : x (0) + , y (0) + , w (0) + , h (0) + {} + + Rectangle (int x_, int y_, int w_, int h_) + : x (x_) + , y (y_) + , w (w_) + , h (h_) + {} + + int x; + int y; + int w; + int h; + + Rectangle intersection (Rectangle const & other) const; +}; + extern std::string crop_string (Position, Size); extern int dcp_audio_sample_rate (int); extern std::string colour_lut_index_to_name (int index); diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 6ffe4e66a..8c298ea57 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -42,18 +42,24 @@ public: ThumbPanel (wxPanel* parent, Film* film) : wxPanel (parent) , _film (film) - , _image (0) - , _bitmap (0) { } /** Handle a paint event */ void paint_event (wxPaintEvent& ev) { - if (_current_image != _pending_image) { - delete _image; - _image = new wxImage (std_to_wx (_pending_image)); - _current_image = _pending_image; + if (_current_index != _pending_index) { + _image.reset (new wxImage (std_to_wx (_film->thumb_file (_pending_index)))); + _current_index = _pending_index; + + _subtitles.clear (); + + cout << "=== SUBS for " << _film->thumb_frame (_pending_index) << "\n"; + list > s = _film->thumb_subtitles (_pending_index); + for (list >::iterator i = s.begin(); i != s.end(); ++i) { + _subtitles.push_back (SubtitleView (i->first, std_to_wx (i->second))); + } + setup (); } @@ -64,8 +70,14 @@ public: wxPaintDC dc (this); if (_bitmap) { + cout << "frame bm " << _bitmap->GetWidth() << " " << _bitmap->GetHeight() << "\n"; dc.DrawBitmap (*_bitmap, 0, 0, false); } + + for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + dc.DrawBitmap (*i->bitmap, i->position.x, i->position.y, true); + cout << "\tsub bm at " << i->position.x << " " << i->position.y << " size " << i->bitmap->GetWidth() << " by " << i->bitmap->GetHeight() << "\n"; + } } /** Handle a size event */ @@ -79,9 +91,10 @@ public: Refresh (); } - void set (string f) + /** @param n Thumbnail index */ + void set (int n) { - _pending_image = f; + _pending_index = n; Refresh (); } @@ -106,10 +119,9 @@ public: /** Clear our thumbnail image */ void clear () { - delete _bitmap; - _bitmap = 0; - delete _image; - _image = 0; + _bitmap.reset (); + _image.reset (); + _subtitles.clear (); } void refresh () @@ -127,41 +139,95 @@ private: if (!_film || !_image) { return; } - + + /* Size of the view */ int vw, vh; GetSize (&vw, &vh); + /* Cropped rectangle */ + Rectangle cropped ( + _current_crop.left, + _current_crop.top, + _image->GetWidth() - (_current_crop.left + _current_crop.right), + _image->GetHeight() - (_current_crop.top + _current_crop.bottom) + ); + + /* Target ratio */ float const target = _film->format() ? _film->format()->ratio_as_float (_film) : 1.78; - _cropped_image = _image->GetSubImage ( - wxRect ( - _current_crop.left, - _current_crop.top, - _image->GetWidth() - (_current_crop.left + _current_crop.right), - _image->GetHeight() - (_current_crop.top + _current_crop.bottom) - ) - ); + _cropped_image = _image->GetSubImage (wxRect (cropped.x, cropped.y, cropped.w, cropped.h)); + + float x_scale = 1; + float y_scale = 1; if ((float (vw) / vh) > target) { /* view is longer (horizontally) than the ratio; fit height */ _cropped_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH); + x_scale = vh * target / _image->GetWidth (); + y_scale = float (vh) / _image->GetHeight (); } else { /* view is shorter (horizontally) than the ratio; fit width */ _cropped_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH); + x_scale = float (vw) / _image->GetWidth (); + y_scale = (vw / target) / _image->GetHeight (); } - delete _bitmap; - _bitmap = new wxBitmap (_cropped_image); + _bitmap.reset (new wxBitmap (_cropped_image)); + + for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + Rectangle sub_rect (i->position.x, i->position.y, i->image.GetWidth(), i->image.GetHeight()); + Rectangle cropped_sub_rect = sub_rect.intersection (cropped); + + cout << "sub " << sub_rect.x << " " << sub_rect.y << " " << sub_rect.w << " " << sub_rect.h << "\n"; + cout << "cropped " << cropped_sub_rect.x << " " << cropped_sub_rect.y << " " << cropped_sub_rect.w << " " << cropped_sub_rect.h << "\n"; + + i->cropped_image = i->image.GetSubImage ( + wxRect ( + cropped_sub_rect.x - sub_rect.x, + cropped_sub_rect.y - sub_rect.y, + cropped_sub_rect.w, + cropped_sub_rect.h + ) + ); + + i->cropped_image.Rescale (cropped_sub_rect.w * x_scale, cropped_sub_rect.h * y_scale, wxIMAGE_QUALITY_HIGH); + + i->position = Position ( + cropped_sub_rect.x * x_scale, + cropped_sub_rect.y * y_scale + ); + + cout << "scales are " << x_scale << " " << y_scale << "\n"; + cout << "scaled to " << (cropped_sub_rect.w * x_scale) << " " << (cropped_sub_rect.h * y_scale) << "\n"; + + i->bitmap.reset (new wxBitmap (i->cropped_image)); + } } Film* _film; - wxImage* _image; - std::string _current_image; - std::string _pending_image; + shared_ptr _image; wxImage _cropped_image; - wxBitmap* _bitmap; + /** currently-displayed thumbnail index */ + int _current_index; + int _pending_index; + shared_ptr _bitmap; Crop _current_crop; Crop _pending_crop; + + struct SubtitleView + { + SubtitleView (Position p, wxString const & i) + : position (p) + , image (i) + {} + + Position position; + wxImage image; + wxImage cropped_image; + shared_ptr bitmap; + }; + + list _subtitles; }; BEGIN_EVENT_TABLE (ThumbPanel, wxPanel) @@ -196,7 +262,7 @@ FilmViewer::set_thumbnail (int n) return; } - _thumb_panel->set (_film->thumb_file(n)); + _thumb_panel->set (n); } void -- cgit v1.2.3 From b96ad1327c25d5de8d61ed73a1c0e207e67f1226 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 11 Oct 2012 01:18:24 +0100 Subject: Use PNG for thumbs so that we get alpha blending in wxwidgets. --- src/lib/film.cc | 6 +- src/lib/film_state.cc | 2 +- src/lib/imagemagick_encoder.cc | 99 +++++++++++++++++++++++++++++++ src/lib/imagemagick_encoder.h | 43 ++++++++++++++ src/lib/subtitle.cc | 70 ++++++++++++++++++++++ src/lib/subtitle.h | 63 ++++++++++++++++++++ src/lib/thumbs_job.cc | 4 +- src/lib/tiff_encoder.cc | 129 ----------------------------------------- src/lib/tiff_encoder.h | 43 -------------- src/lib/wscript | 2 +- src/wx/film_viewer.cc | 2 +- 11 files changed, 283 insertions(+), 180 deletions(-) create mode 100644 src/lib/imagemagick_encoder.cc create mode 100644 src/lib/imagemagick_encoder.h create mode 100644 src/lib/subtitle.cc create mode 100644 src/lib/subtitle.h delete mode 100644 src/lib/tiff_encoder.cc delete mode 100644 src/lib/tiff_encoder.h (limited to 'src') diff --git a/src/lib/film.cc b/src/lib/film.cc index 1e23c4d6b..08ef938d1 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -30,7 +30,7 @@ #include #include "film.h" #include "format.h" -#include "tiff_encoder.h" +#include "imagemagick_encoder.h" #include "job.h" #include "filter.h" #include "transcoder.h" @@ -397,7 +397,7 @@ Film::update_thumbs_post_gui () string const l = i->leaf (); #endif - size_t const d = l.find (".tiff"); + size_t const d = l.find (".png"); if (d != string::npos) { _state.thumbs.push_back (atoi (l.substr (0, d).c_str())); } @@ -707,7 +707,7 @@ Film::thumb_subtitles (int n) const } else if (k == "x") { sub_x = v; } else if (k == "y") { - subs.push_back (make_pair (Position (sub_x, v), String::compose ("%1.sub.%2.tiff", _state.thumb_base(n), sub_number))); + subs.push_back (make_pair (Position (sub_x, v), String::compose ("%1.sub.%2.png", _state.thumb_base(n), sub_number))); } } diff --git a/src/lib/film_state.cc b/src/lib/film_state.cc index 2847ea513..610362dcd 100644 --- a/src/lib/film_state.cc +++ b/src/lib/film_state.cc @@ -191,7 +191,7 @@ FilmState::thumb_file (int n) const string FilmState::thumb_file_for_frame (int n) const { - return thumb_base_for_frame(n) + ".tiff"; + return thumb_base_for_frame(n) + ".png"; } string diff --git a/src/lib/imagemagick_encoder.cc b/src/lib/imagemagick_encoder.cc new file mode 100644 index 000000000..b7b79ed0c --- /dev/null +++ b/src/lib/imagemagick_encoder.cc @@ -0,0 +1,99 @@ +/* + 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. + +*/ + +/** @file src/imagemagick_encoder.cc + * @brief An encoder that writes image files using ImageMagick (and does nothing with audio). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "imagemagick_encoder.h" +#include "film.h" +#include "film_state.h" +#include "options.h" +#include "exceptions.h" +#include "image.h" +#include "subtitle.h" + +using namespace std; +using namespace boost; + +/** @param s FilmState of the film that we are encoding. + * @param o Options. + * @param l Log. + */ +ImageMagickEncoder::ImageMagickEncoder (shared_ptr s, shared_ptr o, Log* l) + : Encoder (s, o, l) +{ + +} + +void +ImageMagickEncoder::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); + Magick::Image thumb (_opt->out_size.width, _opt->out_size.height, "RGB", MagickCore::CharPixel, scaled->data()[0]); + thumb.magick ("PNG"); + thumb.write (tmp_file); + 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 << ".png"; + + 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); + + string tmp_sub_file = _opt->frame_out_path (frame, true, ext.str ()); + Magick::Image sub_thumb (scaled->size().width, scaled->size().height, "RGBA", MagickCore::CharPixel, scaled->data()[0]); + sub_thumb.magick ("PNG"); + sub_thumb.write (tmp_sub_file); + 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/imagemagick_encoder.h b/src/lib/imagemagick_encoder.h new file mode 100644 index 000000000..ce6ca3e8f --- /dev/null +++ b/src/lib/imagemagick_encoder.h @@ -0,0 +1,43 @@ +/* + 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. + +*/ + +/** @file src/imagemagick_encoder.h + * @brief An encoder that writes image files using ImageMagick (and does nothing with audio). + */ + +#include +#include +#include "encoder.h" + +class FilmState; +class Log; + +/** @class ImageMagickEncoder + * @brief An encoder that writes image files using ImageMagick files (and does nothing with audio). + */ +class ImageMagickEncoder : public Encoder +{ +public: + ImageMagickEncoder (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, boost::shared_ptr); + void process_audio (uint8_t *, int) {} + void process_end () {} +}; diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc new file mode 100644 index 000000000..18dded02c --- /dev/null +++ b/src/lib/subtitle.cc @@ -0,0 +1,70 @@ +/* + 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 "subtitle.h" +#include "image.h" +#include "exceptions.h" + +using namespace std; +using namespace boost; + +Subtitle::Subtitle (AVSubtitle const & sub) +{ + /* subtitle PTS in seconds */ + float const packet_time = (sub.pts / AV_TIME_BASE) + float (sub.pts % AV_TIME_BASE) / 1e6; + + /* hence start time for this sub */ + _from = packet_time + (double (sub.start_display_time) / 1e3); + _to = packet_time + (double (sub.end_display_time) / 1e3); + + for (unsigned int i = 0; i < sub.num_rects; ++i) { + _images.push_back (shared_ptr (new SubtitleImage (sub.rects[i]))); + } +} + +/** @param t Time in seconds from the start of the film */ +bool +Subtitle::displayed_at (double t) +{ + return t >= _from && t <= _to; +} + +SubtitleImage::SubtitleImage (AVSubtitleRect const * rect) + : _position (rect->x, rect->y) + , _image (new SimpleImage (PIX_FMT_RGBA, Size (rect->w, rect->h))) +{ + if (rect->type != SUBTITLE_BITMAP) { + throw DecodeError ("non-bitmap subtitles not yet supported"); + } + + /* 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]; + /* 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; + for (int x = 0; x < rect->w; ++x) { + *out_p++ = palette[*sub_line_p++]; + } + sub_p += rect->pict.linesize[0]; + } +} diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h new file mode 100644 index 000000000..18d2590eb --- /dev/null +++ b/src/lib/subtitle.h @@ -0,0 +1,63 @@ +/* + 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 "util.h" + +struct AVSubtitle; +class SubtitleImage; +class SimpleImage; + +class Subtitle +{ +public: + Subtitle (AVSubtitle const &); + + bool displayed_at (double t); + + std::list > images () const { + return _images; + } + +private: + /** display from time in seconds from the start of the film */ + double _from; + /** display to time in seconds from the start of the film */ + double _to; + std::list > _images; +}; + +class SubtitleImage +{ +public: + SubtitleImage (AVSubtitleRect const *); + + Position position () const { + return _position; + } + + boost::shared_ptr image () const { + return _image; + } + +private: + Position _position; + boost::shared_ptr _image; +}; diff --git a/src/lib/thumbs_job.cc b/src/lib/thumbs_job.cc index 779a1d5d1..16a8a7b01 100644 --- a/src/lib/thumbs_job.cc +++ b/src/lib/thumbs_job.cc @@ -24,7 +24,7 @@ #include #include "thumbs_job.h" #include "film_state.h" -#include "tiff_encoder.h" +#include "imagemagick_encoder.h" #include "transcoder.h" #include "options.h" @@ -51,7 +51,7 @@ void ThumbsJob::run () { try { - shared_ptr e (new TIFFEncoder (_fs, _opt, _log)); + shared_ptr e (new ImageMagickEncoder (_fs, _opt, _log)); Transcoder w (_fs, _opt, this, _log, e); w.go (); set_progress (1); diff --git a/src/lib/tiff_encoder.cc b/src/lib/tiff_encoder.cc deleted file mode 100644 index 9aa7b68c3..000000000 --- a/src/lib/tiff_encoder.cc +++ /dev/null @@ -1,129 +0,0 @@ -/* - 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. - -*/ - -/** @file src/tiff_encoder.h - * @brief An encoder that writes TIFF files (and does nothing with audio). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "tiff_encoder.h" -#include "film.h" -#include "film_state.h" -#include "options.h" -#include "exceptions.h" -#include "image.h" -#include "subtitle.h" - -using namespace std; -using namespace boost; - -/** @param s FilmState of the film that we are encoding. - * @param o Options. - * @param l Log. - */ -TIFFEncoder::TIFFEncoder (shared_ptr s, shared_ptr o, Log* l) - : Encoder (s, o, l) -{ - -} - -void -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); - TIFF* output = TIFFOpen (tmp_file.c_str (), "w"); - if (output == 0) { - throw CreateFileError (tmp_file); - } - - TIFFSetField (output, TIFFTAG_IMAGEWIDTH, _opt->out_size.width); - TIFFSetField (output, TIFFTAG_IMAGELENGTH, _opt->out_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, 3); - - if (TIFFWriteEncodedStrip (output, 0, scaled->data()[0], _opt->out_size.width * _opt->out_size.height * 3) == 0) { - throw WriteFileError (tmp_file, 0); - } - - TIFFClose (output); - - 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 deleted file mode 100644 index 1c9f33f4a..000000000 --- a/src/lib/tiff_encoder.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - 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. - -*/ - -/** @file src/tiff_encoder.h - * @brief An encoder that writes TIFF files (and does nothing with audio). - */ - -#include -#include -#include "encoder.h" - -class FilmState; -class Log; - -/** @class TIFFEncoder - * @brief An encoder that writes TIFF files (and does nothing with audio). - */ -class TIFFEncoder : public Encoder -{ -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, boost::shared_ptr); - void process_audio (uint8_t *, int) {} - void process_end () {} -}; diff --git a/src/lib/wscript b/src/lib/wscript index 67292047c..9539d57a1 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -30,6 +30,7 @@ def build(bld): format.cc image.cc imagemagick_decoder.cc + imagemagick_encoder.cc j2k_still_encoder.cc j2k_wav_encoder.cc job.cc @@ -45,7 +46,6 @@ def build(bld): subtitle.cc thumbs_job.cc tiff_decoder.cc - tiff_encoder.cc timer.cc transcode_job.cc transcoder.cc diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 8c298ea57..e49532cdb 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -326,7 +326,7 @@ FilmViewer::update_thumbs () _film->update_thumbs_pre_gui (); shared_ptr s = _film->state_copy (); - shared_ptr o (new Options (s->dir ("thumbs"), ".tiff", "")); + shared_ptr o (new Options (s->dir ("thumbs"), ".png", "")); o->out_size = _film->size (); o->apply_crop = false; o->decode_audio = false; -- cgit v1.2.3 From 8e8eb8a229a0ed385a53531fb1ad63aff1b0ee97 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 11 Oct 2012 01:28:17 +0100 Subject: Tidy up; make viewer respond with with subtitles button. --- src/wx/film_editor.cc | 1 + src/wx/film_viewer.cc | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index bc7414c41..97a98e54d 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -219,6 +219,7 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent) _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this); _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this); _change_dcp_range_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::change_dcp_range_clicked), 0, this); + _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this); setup_visibility (); setup_formats (); diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index e49532cdb..6a992f0db 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -54,7 +54,6 @@ public: _subtitles.clear (); - cout << "=== SUBS for " << _film->thumb_frame (_pending_index) << "\n"; list > s = _film->thumb_subtitles (_pending_index); for (list >::iterator i = s.begin(); i != s.end(); ++i) { _subtitles.push_back (SubtitleView (i->first, std_to_wx (i->second))); @@ -70,13 +69,13 @@ public: wxPaintDC dc (this); if (_bitmap) { - cout << "frame bm " << _bitmap->GetWidth() << " " << _bitmap->GetHeight() << "\n"; dc.DrawBitmap (*_bitmap, 0, 0, false); } - for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - dc.DrawBitmap (*i->bitmap, i->position.x, i->position.y, true); - cout << "\tsub bm at " << i->position.x << " " << i->position.y << " size " << i->bitmap->GetWidth() << " by " << i->bitmap->GetHeight() << "\n"; + if (_film->with_subtitles ()) { + for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + dc.DrawBitmap (*i->bitmap, i->position.x, i->position.y, true); + } } } @@ -178,9 +177,6 @@ private: Rectangle sub_rect (i->position.x, i->position.y, i->image.GetWidth(), i->image.GetHeight()); Rectangle cropped_sub_rect = sub_rect.intersection (cropped); - cout << "sub " << sub_rect.x << " " << sub_rect.y << " " << sub_rect.w << " " << sub_rect.h << "\n"; - cout << "cropped " << cropped_sub_rect.x << " " << cropped_sub_rect.y << " " << cropped_sub_rect.w << " " << cropped_sub_rect.h << "\n"; - i->cropped_image = i->image.GetSubImage ( wxRect ( cropped_sub_rect.x - sub_rect.x, @@ -197,9 +193,6 @@ private: cropped_sub_rect.y * y_scale ); - cout << "scales are " << x_scale << " " << y_scale << "\n"; - cout << "scaled to " << (cropped_sub_rect.w * x_scale) << " " << (cropped_sub_rect.h * y_scale) << "\n"; - i->bitmap.reset (new wxBitmap (i->cropped_image)); } } @@ -274,9 +267,11 @@ FilmViewer::slider_changed (wxCommandEvent &) void FilmViewer::film_changed (Film::Property p) { - if (p == Film::CROP) { + switch (p) { + case Film::CROP: _thumb_panel->set_crop (_film->crop ()); - } else if (p == Film::THUMBS) { + break; + case Film::THUMBS: if (_film && _film->num_thumbs() > 1) { _slider->SetRange (0, _film->num_thumbs () - 1); } else { @@ -286,12 +281,20 @@ FilmViewer::film_changed (Film::Property p) _slider->SetValue (0); set_thumbnail (0); - } else if (p == Film::FORMAT) { + break; + case Film::FORMAT: _thumb_panel->refresh (); - } else if (p == Film::CONTENT) { + break; + case Film::CONTENT: setup_visibility (); _film->examine_content (); update_thumbs (); + break; + case Film::WITH_SUBTITLES: + _thumb_panel->Refresh (); + break; + default: + break; } } -- cgit v1.2.3 From a8b56b931203f984ae7e3f66bc5b9e66d0942199 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 11 Oct 2012 13:54:14 +0100 Subject: Stop subtitles wandering about when re-setup. --- src/wx/film_viewer.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 6a992f0db..cd043b04e 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -74,7 +74,7 @@ public: if (_film->with_subtitles ()) { for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - dc.DrawBitmap (*i->bitmap, i->position.x, i->position.y, true); + dc.DrawBitmap (*i->bitmap, i->cropped_position.x, i->cropped_position.y, true); } } } @@ -188,7 +188,7 @@ private: i->cropped_image.Rescale (cropped_sub_rect.w * x_scale, cropped_sub_rect.h * y_scale, wxIMAGE_QUALITY_HIGH); - i->position = Position ( + i->cropped_position = Position ( cropped_sub_rect.x * x_scale, cropped_sub_rect.y * y_scale ); @@ -216,6 +216,7 @@ private: Position position; wxImage image; + Position cropped_position; wxImage cropped_image; shared_ptr bitmap; }; -- cgit v1.2.3 From f78ce22dc46e42e4f041ebf877fd7199232e4856 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 11 Oct 2012 21:54:52 +0100 Subject: Basic sub offset support. --- src/lib/film.cc | 14 ++++++++++++++ src/lib/film.h | 12 ++++++++++++ src/lib/film_state.cc | 6 ++++++ src/lib/film_state.h | 7 +++++++ src/wx/film_editor.cc | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/wx/film_editor.h | 4 ++++ src/wx/film_viewer.cc | 38 +++++++++++++++++++++++++++++------- 7 files changed, 127 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/lib/film.cc b/src/lib/film.cc index 08ef938d1..e4155c9f6 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -670,6 +670,20 @@ Film::set_with_subtitles (bool w) signal_changed (WITH_SUBTITLES); } +void +Film::set_subtitle_offset (int o) +{ + _state.subtitle_offset = o; + signal_changed (SUBTITLE_OFFSET); +} + +void +Film::set_subtitle_scale (float s) +{ + _state.subtitle_scale = s; + signal_changed (SUBTITLE_SCALE); +} + list > Film::thumb_subtitles (int n) const { diff --git a/src/lib/film.h b/src/lib/film.h index 1e01747a2..c006eae36 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -123,6 +123,14 @@ public: bool with_subtitles () const { return _state.with_subtitles; } + + int subtitle_offset () const { + return _state.subtitle_offset; + } + + float subtitle_scale () const { + return _state.subtitle_scale; + } void set_filters (std::vector const &); @@ -149,6 +157,8 @@ public: void set_audio_delay (int); void set_still_duration (int); void set_with_subtitles (bool); + void set_subtitle_offset (int); + void set_subtitle_scale (float); /** @return size, in pixels, of the source (ignoring cropping) */ Size size () const { @@ -230,6 +240,8 @@ public: AUDIO_SAMPLE_RATE, STILL_DURATION, WITH_SUBTITLES, + SUBTITLE_OFFSET, + SUBTITLE_SCALE }; boost::shared_ptr state_copy () const; diff --git a/src/lib/film_state.cc b/src/lib/film_state.cc index 610362dcd..a4d88d0e0 100644 --- a/src/lib/film_state.cc +++ b/src/lib/film_state.cc @@ -81,6 +81,8 @@ FilmState::write_metadata (ofstream& f) const f << "audio_delay " << audio_delay << "\n"; f << "still_duration " << still_duration << "\n"; f << "with_subtitles " << with_subtitles << "\n"; + f << "subtitle_offset " << subtitle_offset << "\n"; + f << "subtitle_scale " << subtitle_scale << "\n"; /* Cached stuff; this is information about our content; we could look it up each time, but that's slow. @@ -146,6 +148,10 @@ FilmState::read_metadata (string k, string v) still_duration = atoi (v.c_str ()); } else if (k == "with_subtitles") { with_subtitles = (v == "1"); + } else if (k == "subtitle_offset") { + subtitle_offset = atoi (v.c_str ()); + } else if (k == "subtitle_scale") { + subtitle_scale = atof (v.c_str ()); } /* Cached stuff */ diff --git a/src/lib/film_state.h b/src/lib/film_state.h index 5b3ef8367..d53c6a969 100644 --- a/src/lib/film_state.h +++ b/src/lib/film_state.h @@ -63,6 +63,8 @@ public: , audio_delay (0) , still_duration (10) , with_subtitles (false) + , subtitle_offset (0) + , subtitle_scale (1) , length (0) , audio_channels (0) , audio_sample_rate (0) @@ -130,6 +132,11 @@ public: /** Duration to make still-sourced films (in seconds) */ int still_duration; bool with_subtitles; + /** y offset for placing subtitles, in source pixels; +ve is further down + the frame, -ve is further up. + */ + int subtitle_offset; + float subtitle_scale; /* Data which is cached to speed things up */ diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 97a98e54d..ae1557c3e 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -135,6 +135,14 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent) _sizer->Add (_with_subtitles, 1); _sizer->AddSpacer (0); + video_control (add_label_to_sizer (_sizer, this, "Subtitle Offset")); + _subtitle_offset = new wxSpinCtrl (this); + _sizer->Add (video_control (_subtitle_offset), 1); + + video_control (add_label_to_sizer (_sizer, this, "Subtitle Scale")); + _subtitle_scale = new wxSpinCtrl (this); + _sizer->Add (video_control (_subtitle_scale), 1); + 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); @@ -186,6 +194,7 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent) _audio_gain->SetRange (-60, 60); _audio_delay->SetRange (-1000, 1000); _still_duration->SetRange (0, 60 * 60); + _subtitle_offset->SetRange (-1024, 1024); vector const ct = DCPContentType::all (); for (vector::const_iterator i = ct.begin(); i != ct.end(); ++i) { @@ -220,6 +229,8 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent) _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this); _change_dcp_range_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::change_dcp_range_clicked), 0, this); _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this); + _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this); + _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this); setup_visibility (); setup_formats (); @@ -327,6 +338,31 @@ FilmEditor::name_changed (wxCommandEvent &) _ignore_changes = Film::NONE; } +void +FilmEditor::subtitle_offset_changed (wxCommandEvent &) +{ + if (!_film) { + return; + } + + _ignore_changes = Film::SUBTITLE_OFFSET; + _film->set_subtitle_offset (_subtitle_offset->GetValue ()); + _ignore_changes = Film::NONE; +} + +void +FilmEditor::subtitle_scale_changed (wxCommandEvent &) +{ + if (!_film) { + return; + } + + _ignore_changes = Film::SUBTITLE_OFFSET; + _film->set_subtitle_scale (_subtitle_scale->GetValue ()); + _ignore_changes = Film::NONE; +} + + /** Called when the metadata stored in the Film object has changed; * so that we can update the GUI. * @param p Property of the Film that has changed. @@ -447,6 +483,14 @@ FilmEditor::film_changed (Film::Property p) break; case Film::WITH_SUBTITLES: _with_subtitles->SetValue (_film->with_subtitles ()); + _subtitle_scale->Enable (_film->with_subtitles ()); + _subtitle_offset->Enable (_film->with_subtitles ()); + break; + case Film::SUBTITLE_OFFSET: + _subtitle_offset->SetValue (_film->subtitle_offset ()); + break; + case Film::SUBTITLE_SCALE: + _subtitle_scale->SetValue (_film->subtitle_scale ()); break; } } @@ -520,6 +564,9 @@ FilmEditor::set_film (Film* f) film_changed (Film::AUDIO_GAIN); film_changed (Film::AUDIO_DELAY); film_changed (Film::STILL_DURATION); + film_changed (Film::WITH_SUBTITLES); + film_changed (Film::SUBTITLE_OFFSET); + film_changed (Film::SUBTITLE_SCALE); } /** Updates the sensitivity of lots of widgets to a given value. @@ -546,6 +593,9 @@ FilmEditor::set_things_sensitive (bool s) _audio_gain_calculate_button->Enable (s); _audio_delay->Enable (s); _still_duration->Enable (s); + _with_subtitles->Enable (s); + _subtitle_offset->Enable (s); + _subtitle_scale->Enable (s); } /** Called when the `Edit filters' button has been clicked */ @@ -724,6 +774,9 @@ FilmEditor::with_subtitles_toggled (wxCommandEvent &) _ignore_changes = Film::WITH_SUBTITLES; _film->set_with_subtitles (_with_subtitles->GetValue ()); _ignore_changes = Film::NONE; + + _subtitle_scale->Enable (_film->with_subtitles ()); + _subtitle_offset->Enable (_film->with_subtitles ()); } void diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index 720e71902..31d8ad39b 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -59,6 +59,8 @@ private: void audio_gain_calculate_button_clicked (wxCommandEvent &); void audio_delay_changed (wxCommandEvent &); void with_subtitles_toggled (wxCommandEvent &); + void subtitle_offset_changed (wxCommandEvent &); + void subtitle_scale_changed (wxCommandEvent &); void still_duration_changed (wxCommandEvent &); /* Handle changes to the model */ @@ -106,6 +108,8 @@ private: /** The Film's audio delay */ wxSpinCtrl* _audio_delay; wxCheckBox* _with_subtitles; + wxSpinCtrl* _subtitle_offset; + wxSpinCtrl* _subtitle_scale; /** The Film's DCP content type */ wxComboBox* _dcp_content_type; /** The Film's frames per second */ diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index cd043b04e..85632075c 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -42,6 +42,10 @@ public: ThumbPanel (wxPanel* parent, Film* film) : wxPanel (parent) , _film (film) + , _current_index (-1) + , _pending_index (-1) + , _current_subtitle_offset (0) + , _pending_subtitle_offset (0) { } @@ -67,6 +71,11 @@ public: setup (); } + if (_current_subtitle_offset != _pending_subtitle_offset) { + _current_subtitle_offset = _pending_subtitle_offset; + setup (); + } + wxPaintDC dc (this); if (_bitmap) { dc.DrawBitmap (*_bitmap, 0, 0, false); @@ -103,6 +112,12 @@ public: Refresh (); } + void set_subtitle_offset (int o) + { + _pending_subtitle_offset = o; + Refresh (); + } + void set_film (Film* f) { _film = f; @@ -162,21 +177,25 @@ private: if ((float (vw) / vh) > target) { /* view is longer (horizontally) than the ratio; fit height */ _cropped_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH); - x_scale = vh * target / _image->GetWidth (); - y_scale = float (vh) / _image->GetHeight (); + x_scale = vh * target / cropped.w; + y_scale = float (vh) / cropped.h; } else { /* view is shorter (horizontally) than the ratio; fit width */ _cropped_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH); - x_scale = float (vw) / _image->GetWidth (); - y_scale = (vw / target) / _image->GetHeight (); + x_scale = float (vw) / cropped.w; + y_scale = (vw / target) / cropped.h; } _bitmap.reset (new wxBitmap (_cropped_image)); for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - Rectangle sub_rect (i->position.x, i->position.y, i->image.GetWidth(), i->image.GetHeight()); + + /* Area of the subtitle graphic within the (uncropped) picture frame */ + Rectangle sub_rect (i->position.x, i->position.y + _current_subtitle_offset, i->image.GetWidth(), i->image.GetHeight()); + /* Hence the subtitle graphic after it has been cropped */ Rectangle cropped_sub_rect = sub_rect.intersection (cropped); + /* Get the cropped version of the subtitle image */ i->cropped_image = i->image.GetSubImage ( wxRect ( cropped_sub_rect.x - sub_rect.x, @@ -185,12 +204,12 @@ private: cropped_sub_rect.h ) ); - + i->cropped_image.Rescale (cropped_sub_rect.w * x_scale, cropped_sub_rect.h * y_scale, wxIMAGE_QUALITY_HIGH); i->cropped_position = Position ( cropped_sub_rect.x * x_scale, - cropped_sub_rect.y * y_scale + (cropped_sub_rect.y - _current_crop.top) * y_scale ); i->bitmap.reset (new wxBitmap (i->cropped_image)); @@ -206,6 +225,8 @@ private: shared_ptr _bitmap; Crop _current_crop; Crop _pending_crop; + int _current_subtitle_offset; + int _pending_subtitle_offset; struct SubtitleView { @@ -294,6 +315,9 @@ FilmViewer::film_changed (Film::Property p) case Film::WITH_SUBTITLES: _thumb_panel->Refresh (); break; + case Film::SUBTITLE_OFFSET: + _thumb_panel->set_subtitle_offset (_film->subtitle_offset ()); + break; default: break; } -- cgit v1.2.3 From 937c2bc377cb8f15d41cbd8166b7b3fe61b580a6 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 12 Oct 2012 00:22:53 +0100 Subject: Try to tidy up a bit. --- src/wx/film_viewer.cc | 95 ++++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 85632075c..0caa296ef 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -42,38 +42,31 @@ public: ThumbPanel (wxPanel* parent, Film* film) : wxPanel (parent) , _film (film) - , _current_index (-1) - , _pending_index (-1) - , _current_subtitle_offset (0) - , _pending_subtitle_offset (0) - { - } + , _frame_rebuild_needed (false) + , _composition_needed (false) + {} /** Handle a paint event */ void paint_event (wxPaintEvent& ev) { - if (_current_index != _pending_index) { - _image.reset (new wxImage (std_to_wx (_film->thumb_file (_pending_index)))); - _current_index = _pending_index; + if (_frame_rebuild_needed) { + _image.reset (new wxImage (std_to_wx (_film->thumb_file (_index)))); _subtitles.clear (); - - list > s = _film->thumb_subtitles (_pending_index); + list > s = _film->thumb_subtitles (_index); for (list >::iterator i = s.begin(); i != s.end(); ++i) { _subtitles.push_back (SubtitleView (i->first, std_to_wx (i->second))); } - setup (); - } + _frame_rebuild_needed = false; - if (_current_crop != _pending_crop) { - _current_crop = _pending_crop; - setup (); + compose (); + _composition_needed = false; } - if (_current_subtitle_offset != _pending_subtitle_offset) { - _current_subtitle_offset = _pending_subtitle_offset; - setup (); + if (_composition_needed) { + compose (); + _composition_needed = false; } wxPaintDC dc (this); @@ -81,7 +74,7 @@ public: dc.DrawBitmap (*_bitmap, 0, 0, false); } - if (_film->with_subtitles ()) { + if (_film && _film->with_subtitles ()) { for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { dc.DrawBitmap (*i->bitmap, i->cropped_position.x, i->cropped_position.y, true); } @@ -95,26 +88,14 @@ public: return; } - setup (); - Refresh (); + recompose (); } /** @param n Thumbnail index */ void set (int n) { - _pending_index = n; - Refresh (); - } - - void set_crop (Crop c) - { - _pending_crop = c; - Refresh (); - } - - void set_subtitle_offset (int o) - { - _pending_subtitle_offset = o; + _index = n; + _frame_rebuild_needed = true; Refresh (); } @@ -123,9 +104,10 @@ public: _film = f; if (!_film) { clear (); + _frame_rebuild_needed = true; Refresh (); } else { - setup (); + _frame_rebuild_needed = true; Refresh (); } } @@ -138,9 +120,9 @@ public: _subtitles.clear (); } - void refresh () + void recompose () { - setup (); + _composition_needed = true; Refresh (); } @@ -148,7 +130,7 @@ public: private: - void setup () + void compose () { if (!_film || !_image) { return; @@ -160,10 +142,10 @@ private: /* Cropped rectangle */ Rectangle cropped ( - _current_crop.left, - _current_crop.top, - _image->GetWidth() - (_current_crop.left + _current_crop.right), - _image->GetHeight() - (_current_crop.top + _current_crop.bottom) + _film->crop().left, + _film->crop().top, + _image->GetWidth() - (_film->crop().left + _film->crop().right), + _image->GetHeight() - (_film->crop().top + _film->crop().bottom) ); /* Target ratio */ @@ -191,7 +173,7 @@ private: for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { /* Area of the subtitle graphic within the (uncropped) picture frame */ - Rectangle sub_rect (i->position.x, i->position.y + _current_subtitle_offset, i->image.GetWidth(), i->image.GetHeight()); + Rectangle sub_rect (i->position.x, i->position.y + _film->subtitle_offset(), i->image.GetWidth(), i->image.GetHeight()); /* Hence the subtitle graphic after it has been cropped */ Rectangle cropped_sub_rect = sub_rect.intersection (cropped); @@ -209,7 +191,7 @@ private: i->cropped_position = Position ( cropped_sub_rect.x * x_scale, - (cropped_sub_rect.y - _current_crop.top) * y_scale + (cropped_sub_rect.y - _film->crop().top) * y_scale ); i->bitmap.reset (new wxBitmap (i->cropped_image)); @@ -220,13 +202,10 @@ private: shared_ptr _image; wxImage _cropped_image; /** currently-displayed thumbnail index */ - int _current_index; - int _pending_index; + int _index; shared_ptr _bitmap; - Crop _current_crop; - Crop _pending_crop; - int _current_subtitle_offset; - int _pending_subtitle_offset; + bool _frame_rebuild_needed; + bool _composition_needed; struct SubtitleView { @@ -290,9 +269,6 @@ void FilmViewer::film_changed (Film::Property p) { switch (p) { - case Film::CROP: - _thumb_panel->set_crop (_film->crop ()); - break; case Film::THUMBS: if (_film && _film->num_thumbs() > 1) { _slider->SetRange (0, _film->num_thumbs () - 1); @@ -304,19 +280,17 @@ FilmViewer::film_changed (Film::Property p) _slider->SetValue (0); set_thumbnail (0); break; - case Film::FORMAT: - _thumb_panel->refresh (); - break; case Film::CONTENT: setup_visibility (); _film->examine_content (); update_thumbs (); break; + case Film::CROP: + case Film::FORMAT: case Film::WITH_SUBTITLES: - _thumb_panel->Refresh (); - break; case Film::SUBTITLE_OFFSET: - _thumb_panel->set_subtitle_offset (_film->subtitle_offset ()); + case Film::SUBTITLE_SCALE: + _thumb_panel->recompose (); break; default: break; @@ -340,7 +314,6 @@ FilmViewer::set_film (Film* f) _film->Changed.connect (sigc::mem_fun (*this, &FilmViewer::film_changed)); film_changed (Film::CROP); film_changed (Film::THUMBS); - _thumb_panel->refresh (); setup_visibility (); } -- cgit v1.2.3 From 5ec4cafd9ed9966c0af6b3f33f78cc833950ee0c Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 12 Oct 2012 00:24:06 +0100 Subject: Fix potential crash. --- src/wx/film_viewer.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 0caa296ef..fb7317dc6 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -49,6 +49,11 @@ public: /** Handle a paint event */ void paint_event (wxPaintEvent& ev) { + if (!_film) { + wxPaintDC dc (this); + return; + } + if (_frame_rebuild_needed) { _image.reset (new wxImage (std_to_wx (_film->thumb_file (_index)))); @@ -74,7 +79,7 @@ public: dc.DrawBitmap (*_bitmap, 0, 0, false); } - if (_film && _film->with_subtitles ()) { + if (_film->with_subtitles ()) { for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { dc.DrawBitmap (*i->bitmap, i->cropped_position.x, i->cropped_position.y, true); } -- cgit v1.2.3 From 01f91d3d743b1d9c5306e8817cfd926eddc61736 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 00:24:26 +0100 Subject: Fix up tests; possibly working subtitle transforms. --- src/lib/subtitle.cc | 36 +++++++++++++++++++++++++++ src/lib/subtitle.h | 15 ++++++++++++ src/wx/film_viewer.cc | 67 +++++++++++++++++++++------------------------------ test/metadata.ref | 4 +++ test/test.cc | 20 +++------------ 5 files changed, 86 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc index 18dded02c..3559197a0 100644 --- a/src/lib/subtitle.cc +++ b/src/lib/subtitle.cc @@ -20,6 +20,7 @@ #include "subtitle.h" #include "image.h" #include "exceptions.h" +#include "film_state.h" using namespace std; using namespace boost; @@ -68,3 +69,38 @@ SubtitleImage::SubtitleImage (AVSubtitleRect const * rect) sub_p += rect->pict.linesize[0]; } } + +SubtitleTransform +subtitle_transform ( + int target_base_width, int target_base_height, + float target_x_scale, float target_y_scale, + Position sub_pos, int sub_width, int sub_height, + shared_ptr fs + ) +{ + SubtitleTransform tx; + + Rectangle sub_area (sub_pos.x, sub_pos.y + fs->subtitle_offset, sub_width, sub_height); + + Rectangle cropped_target_area ( + fs->crop.left, + fs->crop.top, + target_base_width - (fs->crop.left + fs->crop.right), + target_base_height - (fs->crop.top + fs->crop.bottom) + ); + + Rectangle cropped_sub_area = sub_area.intersection (cropped_target_area); + + tx.crop.x = cropped_sub_area.x - sub_area.x; + tx.crop.y = cropped_sub_area.y - sub_area.y; + tx.crop.w = cropped_sub_area.w; + tx.crop.h = cropped_sub_area.h; + + tx.transformed.w = cropped_sub_area.w * target_x_scale * fs->subtitle_scale; + tx.transformed.h = cropped_sub_area.h * target_y_scale * fs->subtitle_scale; + + tx.transformed.x = target_x_scale * ((sub_area.x - fs->crop.left) + (cropped_sub_area.w * (1 - fs->subtitle_scale) / 2)); + tx.transformed.y = target_y_scale * ((sub_area.y - fs->crop.top) + (cropped_sub_area.h * (1 - fs->subtitle_scale) / 2)); + + return tx; +} diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h index 18d2590eb..fcb6bc70c 100644 --- a/src/lib/subtitle.h +++ b/src/lib/subtitle.h @@ -24,6 +24,7 @@ struct AVSubtitle; class SubtitleImage; class SimpleImage; +class FilmState; class Subtitle { @@ -44,6 +45,20 @@ private: std::list > _images; }; +struct SubtitleTransform +{ +public: + Rectangle crop; + Rectangle transformed; +}; + +extern SubtitleTransform subtitle_transform ( + int target_base_width, int target_base_height, + float target_x_scale, float target_y_scale, + Position sub_pos, int sub_width, int sub_height, + boost::shared_ptr fs + ); + class SubtitleImage { public: diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index fb7317dc6..725ba57de 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -30,6 +30,7 @@ #include "lib/job_manager.h" #include "lib/film_state.h" #include "lib/options.h" +#include "lib/subtitle.h" #include "film_viewer.h" #include "wx_util.h" @@ -81,7 +82,7 @@ public: if (_film->with_subtitles ()) { for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - dc.DrawBitmap (*i->bitmap, i->cropped_position.x, i->cropped_position.y, true); + dc.DrawBitmap (*i->bitmap, i->transformed_position.x, i->transformed_position.y, true); } } } @@ -146,7 +147,7 @@ private: GetSize (&vw, &vh); /* Cropped rectangle */ - Rectangle cropped ( + Rectangle cropped_area ( _film->crop().left, _film->crop().top, _image->GetWidth() - (_film->crop().left + _film->crop().right), @@ -156,56 +157,44 @@ private: /* Target ratio */ float const target = _film->format() ? _film->format()->ratio_as_float (_film) : 1.78; - _cropped_image = _image->GetSubImage (wxRect (cropped.x, cropped.y, cropped.w, cropped.h)); + _transformed_image = _image->GetSubImage (wxRect (cropped_area.x, cropped_area.y, cropped_area.w, cropped_area.h)); float x_scale = 1; float y_scale = 1; if ((float (vw) / vh) > target) { /* view is longer (horizontally) than the ratio; fit height */ - _cropped_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH); - x_scale = vh * target / cropped.w; - y_scale = float (vh) / cropped.h; + _transformed_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH); + x_scale = vh * target / cropped_area.w; + y_scale = float (vh) / cropped_area.h; } else { /* view is shorter (horizontally) than the ratio; fit width */ - _cropped_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH); - x_scale = float (vw) / cropped.w; - y_scale = (vw / target) / cropped.h; + _transformed_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH); + x_scale = float (vw) / cropped_area.w; + y_scale = (vw / target) / cropped_area.h; } - _bitmap.reset (new wxBitmap (_cropped_image)); + _bitmap.reset (new wxBitmap (_transformed_image)); for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - /* Area of the subtitle graphic within the (uncropped) picture frame */ - Rectangle sub_rect (i->position.x, i->position.y + _film->subtitle_offset(), i->image.GetWidth(), i->image.GetHeight()); - /* Hence the subtitle graphic after it has been cropped */ - Rectangle cropped_sub_rect = sub_rect.intersection (cropped); - - /* Get the cropped version of the subtitle image */ - i->cropped_image = i->image.GetSubImage ( - wxRect ( - cropped_sub_rect.x - sub_rect.x, - cropped_sub_rect.y - sub_rect.y, - cropped_sub_rect.w, - cropped_sub_rect.h - ) + SubtitleTransform tx = subtitle_transform ( + _image->GetWidth(), _image->GetHeight(), + x_scale, y_scale, + i->base_position, i->base_image.GetWidth(), i->base_image.GetHeight(), + _film->state_copy() ); - i->cropped_image.Rescale (cropped_sub_rect.w * x_scale, cropped_sub_rect.h * y_scale, wxIMAGE_QUALITY_HIGH); - - i->cropped_position = Position ( - cropped_sub_rect.x * x_scale, - (cropped_sub_rect.y - _film->crop().top) * y_scale - ); - - i->bitmap.reset (new wxBitmap (i->cropped_image)); + i->transformed_image = i->base_image.GetSubImage (wxRect (tx.crop.x, tx.crop.y, tx.crop.w, tx.crop.h)); + i->transformed_image.Rescale (tx.transformed.w, tx.transformed.h, wxIMAGE_QUALITY_HIGH); + i->transformed_position = Position (tx.transformed.x, tx.transformed.y); + i->bitmap.reset (new wxBitmap (i->transformed_image)); } } Film* _film; shared_ptr _image; - wxImage _cropped_image; + wxImage _transformed_image; /** currently-displayed thumbnail index */ int _index; shared_ptr _bitmap; @@ -215,14 +204,14 @@ private: struct SubtitleView { SubtitleView (Position p, wxString const & i) - : position (p) - , image (i) + : base_position (p) + , base_image (i) {} - - Position position; - wxImage image; - Position cropped_position; - wxImage cropped_image; + + Position base_position; + Position transformed_position; + wxImage base_image; + wxImage transformed_image; shared_ptr bitmap; }; diff --git a/test/metadata.ref b/test/metadata.ref index 817fecffd..c24b77de8 100644 --- a/test/metadata.ref +++ b/test/metadata.ref @@ -16,6 +16,9 @@ dcp_ab 1 audio_gain 0 audio_delay 0 still_duration 10 +with_subtitles 0 +subtitle_offset 0 +subtitle_scale 1 width 0 height 0 length 0 @@ -23,3 +26,4 @@ audio_channels 0 audio_sample_rate 0 audio_sample_format Unknown content_digest +has_subtitles 0 diff --git a/test/test.cc b/test/test.cc index b14935da3..5a7625dd7 100644 --- a/test/test.cc +++ b/test/test.cc @@ -36,6 +36,7 @@ #include "server.h" #include "cross.h" #include "job.h" +#include "subtitle.h" #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE dvdomatic_test #include @@ -248,7 +249,7 @@ BOOST_AUTO_TEST_CASE (paths_test) FilmState s; s.directory = "build/test/a/b/c/d/e"; s.thumbs.push_back (42); - BOOST_CHECK_EQUAL (s.thumb_file (0), "build/test/a/b/c/d/e/thumbs/00000042.tiff"); + BOOST_CHECK_EQUAL (s.thumb_file (0), "build/test/a/b/c/d/e/thumbs/00000042.png"); s.content = "/foo/bar/baz"; BOOST_CHECK_EQUAL (s.content_path(), "/foo/bar/baz"); @@ -438,24 +439,9 @@ BOOST_AUTO_TEST_CASE (job_manager_test) dvdomatic_sleep (2); BOOST_CHECK_EQUAL (a->finished_ok(), true); - /* Two jobs, no dependency */ - a.reset (new TestJob (s, o, &log, shared_ptr ())); - shared_ptr b (new TestJob (s, o, &log, shared_ptr ())); - - JobManager::instance()->add (a); - JobManager::instance()->add (b); - dvdomatic_sleep (2); - BOOST_CHECK_EQUAL (a->running (), true); - BOOST_CHECK_EQUAL (b->running (), true); - a->set_finished_ok (); - b->set_finished_ok (); - dvdomatic_sleep (2); - BOOST_CHECK_EQUAL (a->finished_ok (), true); - BOOST_CHECK_EQUAL (b->finished_ok (), true); - /* Two jobs, dependency */ a.reset (new TestJob (s, o, &log, shared_ptr ())); - b.reset (new TestJob (s, o, &log, a)); + shared_ptr b (new TestJob (s, o, &log, a)); JobManager::instance()->add (a); JobManager::instance()->add (b); -- cgit v1.2.3 From b1fc0de953b1c2fce8a31b267b63dfcaf67830c7 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 00:39:21 +0100 Subject: Comment; fix up UI for subtitle scaling. --- src/lib/subtitle.cc | 25 ++++++++++++++++++++++--- src/wx/film_editor.cc | 16 +++++++++++----- 2 files changed, 33 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc index 3559197a0..040a057fc 100644 --- a/src/lib/subtitle.cc +++ b/src/lib/subtitle.cc @@ -80,25 +80,44 @@ subtitle_transform ( { SubtitleTransform tx; + /* The area of the original subtitle, in the coordinate space of the source video frame */ Rectangle sub_area (sub_pos.x, sub_pos.y + fs->subtitle_offset, sub_width, sub_height); - + + /* The cropped area of the source video frame, in the same coordinate space */ Rectangle cropped_target_area ( fs->crop.left, fs->crop.top, target_base_width - (fs->crop.left + fs->crop.right), target_base_height - (fs->crop.top + fs->crop.bottom) ); - + + /* Hence the area of the cropped subtitle, in the same coordinate space */ Rectangle cropped_sub_area = sub_area.intersection (cropped_target_area); - + + /* The crop that should be applied to the subtitle, in its coordinate space */ tx.crop.x = cropped_sub_area.x - sub_area.x; tx.crop.y = cropped_sub_area.y - sub_area.y; tx.crop.w = cropped_sub_area.w; tx.crop.h = cropped_sub_area.h; + /* We will scale the subtitle by the same amount as the video frame, and also by the additional + subtitle_scale + */ tx.transformed.w = cropped_sub_area.w * target_x_scale * fs->subtitle_scale; tx.transformed.h = cropped_sub_area.h * target_y_scale * fs->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 - fs->crop_left) * target_x_scale and (sub_area.y - fs->crop_top) * 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). + * + * Combining these two translations gives these expressions. + */ + tx.transformed.x = target_x_scale * ((sub_area.x - fs->crop.left) + (cropped_sub_area.w * (1 - fs->subtitle_scale) / 2)); tx.transformed.y = target_y_scale * ((sub_area.y - fs->crop.top) + (cropped_sub_area.h * (1 - fs->subtitle_scale) / 2)); diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index ae1557c3e..143b058b1 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -139,9 +139,14 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent) _subtitle_offset = new wxSpinCtrl (this); _sizer->Add (video_control (_subtitle_offset), 1); - video_control (add_label_to_sizer (_sizer, this, "Subtitle Scale")); - _subtitle_scale = new wxSpinCtrl (this); - _sizer->Add (video_control (_subtitle_scale), 1); + { + video_control (add_label_to_sizer (_sizer, this, "Subtitle Scale")); + wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); + _subtitle_scale = new wxSpinCtrl (this); + s->Add (video_control (_subtitle_scale)); + video_control (add_label_to_sizer (s, this, "%")); + _sizer->Add (s); + } video_control (add_label_to_sizer (_sizer, this, "Frames Per Second")); _frames_per_second = new wxStaticText (this, wxID_ANY, wxT ("")); @@ -195,6 +200,7 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent) _audio_delay->SetRange (-1000, 1000); _still_duration->SetRange (0, 60 * 60); _subtitle_offset->SetRange (-1024, 1024); + _subtitle_scale->SetRange (1, 1000); vector const ct = DCPContentType::all (); for (vector::const_iterator i = ct.begin(); i != ct.end(); ++i) { @@ -358,7 +364,7 @@ FilmEditor::subtitle_scale_changed (wxCommandEvent &) } _ignore_changes = Film::SUBTITLE_OFFSET; - _film->set_subtitle_scale (_subtitle_scale->GetValue ()); + _film->set_subtitle_scale (_subtitle_scale->GetValue() / 100.0); _ignore_changes = Film::NONE; } @@ -490,7 +496,7 @@ FilmEditor::film_changed (Film::Property p) _subtitle_offset->SetValue (_film->subtitle_offset ()); break; case Film::SUBTITLE_SCALE: - _subtitle_scale->SetValue (_film->subtitle_scale ()); + _subtitle_scale->SetValue (_film->subtitle_scale() * 100); break; } } -- cgit v1.2.3 From 48c8f23660184486efbb34df9d677108b0eab204 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 13:02:57 +0100 Subject: Try moving subtitle adjustment for crop into the decoder. --- src/lib/decoder.cc | 9 +++++++++ src/lib/subtitle.cc | 39 ++++++++++----------------------------- src/lib/subtitle.h | 16 ++++++---------- src/wx/film_viewer.cc | 34 ++++++++++++++++++---------------- 4 files changed, 43 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index e4b892ea4..c4759a872 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -48,6 +48,7 @@ extern "C" { #include "filter.h" #include "delay_line.h" #include "ffmpeg_compatibility.h" +#include "subtitle.h" using namespace std; using namespace boost; @@ -303,6 +304,14 @@ 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)); + } + } + TIMING ("Decoder emits %1", _video_frame); Video (image, _video_frame, sub); ++_video_frame; diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc index 040a057fc..0ea5a72d5 100644 --- a/src/lib/subtitle.cc +++ b/src/lib/subtitle.cc @@ -70,46 +70,27 @@ SubtitleImage::SubtitleImage (AVSubtitleRect const * rect) } } -SubtitleTransform -subtitle_transform ( - int target_base_width, int target_base_height, +Rectangle +transformed_subtitle_area ( float target_x_scale, float target_y_scale, - Position sub_pos, int sub_width, int sub_height, + Rectangle sub_area, shared_ptr fs ) { - SubtitleTransform tx; + Rectangle tx; - /* The area of the original subtitle, in the coordinate space of the source video frame */ - Rectangle sub_area (sub_pos.x, sub_pos.y + fs->subtitle_offset, sub_width, sub_height); - - /* The cropped area of the source video frame, in the same coordinate space */ - Rectangle cropped_target_area ( - fs->crop.left, - fs->crop.top, - target_base_width - (fs->crop.left + fs->crop.right), - target_base_height - (fs->crop.top + fs->crop.bottom) - ); - - /* Hence the area of the cropped subtitle, in the same coordinate space */ - Rectangle cropped_sub_area = sub_area.intersection (cropped_target_area); - - /* The crop that should be applied to the subtitle, in its coordinate space */ - tx.crop.x = cropped_sub_area.x - sub_area.x; - tx.crop.y = cropped_sub_area.y - sub_area.y; - tx.crop.w = cropped_sub_area.w; - tx.crop.h = cropped_sub_area.h; + sub_area.y += fs->subtitle_offset; /* We will scale the subtitle by the same amount as the video frame, and also by the additional subtitle_scale */ - tx.transformed.w = cropped_sub_area.w * target_x_scale * fs->subtitle_scale; - tx.transformed.h = cropped_sub_area.h * target_y_scale * fs->subtitle_scale; + tx.w = sub_area.w * target_x_scale * fs->subtitle_scale; + tx.h = sub_area.h * target_y_scale * fs->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 - fs->crop_left) * target_x_scale and (sub_area.y - fs->crop_top) * target_y_scale. + * 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 @@ -118,8 +99,8 @@ subtitle_transform ( * Combining these two translations gives these expressions. */ - tx.transformed.x = target_x_scale * ((sub_area.x - fs->crop.left) + (cropped_sub_area.w * (1 - fs->subtitle_scale) / 2)); - tx.transformed.y = target_y_scale * ((sub_area.y - fs->crop.top) + (cropped_sub_area.h * (1 - fs->subtitle_scale) / 2)); + 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)); return tx; } diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h index fcb6bc70c..0b82320a1 100644 --- a/src/lib/subtitle.h +++ b/src/lib/subtitle.h @@ -45,17 +45,9 @@ private: std::list > _images; }; -struct SubtitleTransform -{ -public: - Rectangle crop; - Rectangle transformed; -}; - -extern SubtitleTransform subtitle_transform ( - int target_base_width, int target_base_height, +extern Rectangle transformed_subtitle_area ( float target_x_scale, float target_y_scale, - Position sub_pos, int sub_width, int sub_height, + Rectangle sub_area, boost::shared_ptr fs ); @@ -64,6 +56,10 @@ class SubtitleImage public: SubtitleImage (AVSubtitleRect const *); + void set_position (Position p) { + _position = p; + } + Position position () const { return _position; } diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 725ba57de..bfeb44cb9 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -50,11 +50,11 @@ public: /** Handle a paint event */ void paint_event (wxPaintEvent& ev) { - if (!_film) { + if (!_film || _film->num_thumbs() == 0) { wxPaintDC dc (this); return; } - + if (_frame_rebuild_needed) { _image.reset (new wxImage (std_to_wx (_film->thumb_file (_index)))); @@ -82,7 +82,7 @@ public: if (_film->with_subtitles ()) { for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - dc.DrawBitmap (*i->bitmap, i->transformed_position.x, i->transformed_position.y, true); + dc.DrawBitmap (*i->bitmap, i->transformed_area.x, i->transformed_area.y, true); } } } @@ -178,16 +178,14 @@ private: for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - SubtitleTransform tx = subtitle_transform ( - _image->GetWidth(), _image->GetHeight(), - x_scale, y_scale, - i->base_position, i->base_image.GetWidth(), i->base_image.GetHeight(), - _film->state_copy() + i->transformed_area = transformed_subtitle_area ( + x_scale, y_scale, i->base_area, _film->state_copy() ); - i->transformed_image = i->base_image.GetSubImage (wxRect (tx.crop.x, tx.crop.y, tx.crop.w, tx.crop.h)); - i->transformed_image.Rescale (tx.transformed.w, tx.transformed.h, wxIMAGE_QUALITY_HIGH); - i->transformed_position = Position (tx.transformed.x, tx.transformed.y); + i->transformed_image = i->base_image; + i->transformed_image.Rescale (i->transformed_area.w, i->transformed_area.h, wxIMAGE_QUALITY_HIGH); + i->transformed_area.x -= _film->crop().left; + i->transformed_area.y -= _film->crop().top; i->bitmap.reset (new wxBitmap (i->transformed_image)); } } @@ -204,12 +202,16 @@ private: struct SubtitleView { SubtitleView (Position p, wxString const & i) - : base_position (p) - , base_image (i) - {} + : base_image (i) + { + base_area.x = p.x; + base_area.y = p.y; + base_area.w = base_image.GetWidth (); + base_area.h = base_image.GetHeight (); + } - Position base_position; - Position transformed_position; + Rectangle base_area; + Rectangle transformed_area; wxImage base_image; wxImage transformed_image; shared_ptr bitmap; -- cgit v1.2.3 From 429e42c019889e90c9e376170b08614654231337 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 13:15:36 +0100 Subject: Remove believed-unnecessary PostProcessImage. --- src/lib/image.cc | 45 ++------------------------------------------- src/lib/image.h | 23 ++--------------------- 2 files changed, 4 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/lib/image.cc b/src/lib/image.cc index 8fd43093d..98c5228a9 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -167,10 +167,10 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal * @param pp Flags for the required set of post processes. * @return Post-processed image. */ -shared_ptr +shared_ptr Image::post_process (string pp) const { - shared_ptr out (new PostProcessImage (PIX_FMT_YUV420P, size ())); + shared_ptr out (new SimpleImage (PIX_FMT_YUV420P, size ())); pp_mode* mode = pp_get_mode_by_name_and_quality (pp.c_str (), PP_QUALITY_MAX); pp_context* context = pp_get_context (size().width, size().height, PP_FORMAT_420 | PP_CPU_CAPS_MMX2); @@ -344,44 +344,3 @@ RGBFrameImage::size () const { return _size; } - -PostProcessImage::PostProcessImage (PixelFormat p, Size s) - : Image (p) - , _size (s) -{ - _data = new uint8_t*[4]; - _line_size = new int[4]; - - for (int i = 0; i < 4; ++i) { - _data[i] = (uint8_t *) av_malloc (s.width * s.height); - _line_size[i] = s.width; - } -} - -PostProcessImage::~PostProcessImage () -{ - for (int i = 0; i < 4; ++i) { - av_free (_data[i]); - } - - delete[] _data; - delete[] _line_size; -} - -uint8_t ** -PostProcessImage::data () const -{ - return _data; -} - -int * -PostProcessImage::line_size () const -{ - return _line_size; -} - -Size -PostProcessImage::size () const -{ - return _size; -} diff --git a/src/lib/image.h b/src/lib/image.h index d10bcae9e..e06a82b7f 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -34,7 +34,7 @@ extern "C" { class Scaler; class RGBFrameImage; -class PostProcessImage; +class SimpleImage; /** @class Image * @brief Parent class for wrappers of some image, in some format, that @@ -67,7 +67,7 @@ public: 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; + boost::shared_ptr post_process (std::string) const; void make_black (); @@ -137,23 +137,4 @@ private: uint8_t* _data; }; -/** @class PostProcessImage - * @brief An image that is the result of an FFmpeg post-processing run. - */ -class PostProcessImage : public Image -{ -public: - PostProcessImage (PixelFormat, Size); - ~PostProcessImage (); - - uint8_t ** data () const; - int * line_size () const; - Size size () const; - -private: - Size _size; - uint8_t** _data; - int* _line_size; -}; - #endif -- cgit v1.2.3 From 27fac0b4c6d42cb3b47bc1240d50ce11923fb66a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 13:22:47 +0100 Subject: Simplify use of Image hierarchy a bit. --- src/lib/image.cc | 12 ++++++------ src/lib/image.h | 4 ++-- src/lib/server.cc | 2 +- src/lib/subtitle.h | 6 +++--- test/test.cc | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/lib/image.cc b/src/lib/image.cc index 98c5228a9..602b20842 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -88,7 +88,7 @@ Image::scale (Size out_size, Scaler const * scaler) const { assert (scaler); - shared_ptr scaled (new SimpleImage (pixel_format(), out_size)); + shared_ptr scaled (new SimpleImage (pixel_format(), out_size)); struct SwsContext* scale_context = sws_getContext ( size().width, size().height, pixel_format(), @@ -112,7 +112,7 @@ Image::scale (Size out_size, Scaler const * scaler) const * @param out_size Output image size in pixels. * @param scaler Scaler to use. */ -shared_ptr +shared_ptr Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scaler) const { assert (scaler); @@ -120,7 +120,7 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal Size content_size = out_size; content_size.width -= (padding * 2); - shared_ptr rgb (new RGBFrameImage (content_size)); + shared_ptr rgb (new SimpleImage (PIX_FMT_RGB24, content_size)); struct SwsContext* scale_context = sws_getContext ( size().width, size().height, pixel_format(), @@ -141,7 +141,7 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal scheme of things. */ if (padding > 0) { - shared_ptr padded_rgb (new RGBFrameImage (out_size)); + shared_ptr padded_rgb (new SimpleImage (PIX_FMT_RGB24, out_size)); padded_rgb->make_black (); /* XXX: we are cheating a bit here; we know the frame is RGB so we can @@ -167,10 +167,10 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal * @param pp Flags for the required set of post processes. * @return Post-processed image. */ -shared_ptr +shared_ptr Image::post_process (string pp) const { - shared_ptr out (new SimpleImage (PIX_FMT_YUV420P, size ())); + shared_ptr out (new SimpleImage (PIX_FMT_YUV420P, size ())); pp_mode* mode = pp_get_mode_by_name_and_quality (pp.c_str (), PP_QUALITY_MAX); pp_context* context = pp_get_context (size().width, size().height, PP_FORMAT_420 | PP_CPU_CAPS_MMX2); diff --git a/src/lib/image.h b/src/lib/image.h index e06a82b7f..970750719 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -65,9 +65,9 @@ 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_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; + boost::shared_ptr post_process (std::string) const; void make_black (); diff --git a/src/lib/server.cc b/src/lib/server.cc index 76989d078..2fda5952f 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -112,7 +112,7 @@ Server::process (shared_ptr socket) post_process = ""; } - shared_ptr image (new SimpleImage (pixel_format, in_size)); + shared_ptr image (new SimpleImage (pixel_format, in_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); diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h index 0b82320a1..d9717564e 100644 --- a/src/lib/subtitle.h +++ b/src/lib/subtitle.h @@ -23,7 +23,7 @@ struct AVSubtitle; class SubtitleImage; -class SimpleImage; +class Image; class FilmState; class Subtitle @@ -64,11 +64,11 @@ public: return _position; } - boost::shared_ptr image () const { + boost::shared_ptr image () const { return _image; } private: Position _position; - boost::shared_ptr _image; + boost::shared_ptr _image; }; diff --git a/test/test.cc b/test/test.cc index 5a7625dd7..789724b53 100644 --- a/test/test.cc +++ b/test/test.cc @@ -270,7 +270,7 @@ 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))); + shared_ptr image (new SimpleImage (PIX_FMT_RGB24, Size (1998, 1080))); uint8_t* p = image->data()[0]; -- 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') 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 From 49f9cb10018bf4ec07a60d1599cbe62d735baa23 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 18:58:50 +0100 Subject: Remove unused code. --- src/lib/util.h | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'src') diff --git a/src/lib/util.h b/src/lib/util.h index e1ad7fd64..66c052c48 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -190,20 +190,4 @@ private: int _buffer_data; }; -#define SCALEBITS 10 -#define ONE_HALF (1 << (SCALEBITS - 1)) -#define FIX(x) ((int) ((x) * (1<> SCALEBITS) - -#define RGB_TO_U_CCIR(r1, g1, b1, shift)\ -(((- FIX(0.16874*224.0/255.0) * r1 - FIX(0.33126*224.0/255.0) * g1 + \ - FIX(0.50000*224.0/255.0) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) - -#define RGB_TO_V_CCIR(r1, g1, b1, shift)\ -(((FIX(0.50000*224.0/255.0) * r1 - FIX(0.41869*224.0/255.0) * g1 - \ - FIX(0.08131*224.0/255.0) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) - #endif -- cgit v1.2.3 From 129afab72bfc026b5704c41a6bfc0f4b3a2c4033 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 20:29:20 +0100 Subject: Round image line sizes up to 32. Seems to help with LHS image corruption with both crop and a post-processing filter. --- src/lib/dcp_video_frame.cc | 58 ++++++++++++++++++++++++++++------------------ src/lib/filter.cc | 1 + src/lib/image.cc | 10 ++++---- src/lib/util.cc | 7 ++++++ src/lib/util.h | 1 + 5 files changed, 49 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index ce660add5..3e5c4b3aa 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -176,8 +176,6 @@ DCPVideoFrame::encode_locally () create_openjpeg_container (); - int const size = _out_size.width * _out_size.height; - struct { double r, g, b; } s; @@ -188,27 +186,41 @@ DCPVideoFrame::encode_locally () /* Copy our RGB into the openjpeg container, converting to XYZ in the process */ - uint8_t* p = prepared->data()[0]; - for (int i = 0; i < size; ++i) { - /* In gamma LUT (converting 8-bit input to 12-bit) */ - s.r = lut_in[_colour_lut_index][*p++ << 4]; - s.g = lut_in[_colour_lut_index][*p++ << 4]; - s.b = lut_in[_colour_lut_index][*p++ << 4]; - - /* RGB to XYZ Matrix */ - d.x = ((s.r * color_matrix[_colour_lut_index][0][0]) + (s.g * color_matrix[_colour_lut_index][0][1]) + (s.b * color_matrix[_colour_lut_index][0][2])); - d.y = ((s.r * color_matrix[_colour_lut_index][1][0]) + (s.g * color_matrix[_colour_lut_index][1][1]) + (s.b * color_matrix[_colour_lut_index][1][2])); - d.z = ((s.r * color_matrix[_colour_lut_index][2][0]) + (s.g * color_matrix[_colour_lut_index][2][1]) + (s.b * color_matrix[_colour_lut_index][2][2])); - - /* DCI companding */ - d.x = d.x * DCI_COEFFICENT * (DCI_LUT_SIZE - 1); - d.y = d.y * DCI_COEFFICENT * (DCI_LUT_SIZE - 1); - d.z = d.z * DCI_COEFFICENT * (DCI_LUT_SIZE - 1); - - /* Out gamma LUT */ - _image->comps[0].data[i] = lut_out[LO_DCI][(int) d.x]; - _image->comps[1].data[i] = lut_out[LO_DCI][(int) d.y]; - _image->comps[2].data[i] = lut_out[LO_DCI][(int) d.z]; + int jn = 0; + for (int y = 0; y < _out_size.height; ++y) { + uint8_t* p = prepared->data()[0] + y * prepared->line_size()[0]; + for (int x = 0; x < _out_size.width; ++x) { + + /* In gamma LUT (converting 8-bit input to 12-bit) */ + s.r = lut_in[_colour_lut_index][*p++ << 4]; + s.g = lut_in[_colour_lut_index][*p++ << 4]; + s.b = lut_in[_colour_lut_index][*p++ << 4]; + + /* RGB to XYZ Matrix */ + d.x = ((s.r * color_matrix[_colour_lut_index][0][0]) + + (s.g * color_matrix[_colour_lut_index][0][1]) + + (s.b * color_matrix[_colour_lut_index][0][2])); + + d.y = ((s.r * color_matrix[_colour_lut_index][1][0]) + + (s.g * color_matrix[_colour_lut_index][1][1]) + + (s.b * color_matrix[_colour_lut_index][1][2])); + + d.z = ((s.r * color_matrix[_colour_lut_index][2][0]) + + (s.g * color_matrix[_colour_lut_index][2][1]) + + (s.b * color_matrix[_colour_lut_index][2][2])); + + /* DCI companding */ + d.x = d.x * DCI_COEFFICENT * (DCI_LUT_SIZE - 1); + d.y = d.y * DCI_COEFFICENT * (DCI_LUT_SIZE - 1); + d.z = d.z * DCI_COEFFICENT * (DCI_LUT_SIZE - 1); + + /* Out gamma LUT */ + _image->comps[0].data[jn] = lut_out[LO_DCI][(int) d.x]; + _image->comps[1].data[jn] = lut_out[LO_DCI][(int) d.y]; + _image->comps[2].data[jn] = lut_out[LO_DCI][(int) d.z]; + + ++jn; + } } /* Set the max image and component sizes based on frame_rate */ diff --git a/src/lib/filter.cc b/src/lib/filter.cc index ab5a6316f..446cc111d 100644 --- a/src/lib/filter.cc +++ b/src/lib/filter.cc @@ -72,6 +72,7 @@ Filter::setup_filters () _filters.push_back (new Filter ("ppl5", "FIR low-pass deinterlacer", "", "l5")); _filters.push_back (new Filter ("mcdeint", "Motion compensating deinterlacer", "mcdeint", "")); _filters.push_back (new Filter ("kerndeint", "Kernel deinterlacer", "kerndeint", "")); + _filters.push_back (new Filter ("yadif", "Yet Another Deinterlacing Filter", "yadif", "")); _filters.push_back (new Filter ("pptn", "Temporal noise reducer", "", "tn")); _filters.push_back (new Filter ("ppfq", "Force quantizer", "", "fq")); _filters.push_back (new Filter ("gradfun", "Gradient debander", "gradfun", "")); diff --git a/src/lib/image.cc b/src/lib/image.cc index ce44ec95b..c8849303c 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -260,15 +260,15 @@ SimpleImage::SimpleImage (PixelFormat p, Size s) switch (p) { case PIX_FMT_RGB24: - _line_size[0] = s.width * 3; + _line_size[0] = round_up (s.width * 3, 32); break; case PIX_FMT_RGBA: - _line_size[0] = s.width * 4; + _line_size[0] = round_up (s.width * 4, 32); break; case PIX_FMT_YUV420P: - _line_size[0] = s.width; - _line_size[1] = s.width / 2; - _line_size[2] = s.width / 2; + _line_size[0] = round_up (s.width, 32); + _line_size[1] = round_up (s.width / 2, 32); + _line_size[2] = round_up (s.width / 2, 32); break; default: assert (false); diff --git a/src/lib/util.cc b/src/lib/util.cc index 82aaf8ff5..c3dd13d7c 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -613,3 +613,10 @@ Rectangle::intersection (Rectangle const & other) const min (y + h, other.y + other.h) - ty ); } + +int +round_up (int a, int t) +{ + a += (t - 1); + return a - (a % t); +} diff --git a/src/lib/util.h b/src/lib/util.h index 66c052c48..244c01855 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -149,6 +149,7 @@ struct Rectangle extern std::string crop_string (Position, Size); extern int dcp_audio_sample_rate (int); extern std::string colour_lut_index_to_name (int index); +extern int round_up (int, int); /** @class Socket * @brief A class to wrap a boost::asio::ip::tcp::socket with some things -- cgit v1.2.3 From ade28a703b15af710161faa017cddf95d66c4118 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 20:51:43 +0100 Subject: Try to clarify the difference between line size and stride. --- src/lib/ab_transcoder.cc | 5 ++-- src/lib/dcp_video_frame.cc | 4 +-- src/lib/image.cc | 66 +++++++++++++++++++++++++++++++--------------- src/lib/image.h | 12 +++++++-- src/lib/server.cc | 2 +- 5 files changed, 61 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index a32d82c54..54153ec76 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -80,14 +80,15 @@ ABTranscoder::process_video (shared_ptr yuv, int frame, shared_ptrcomponents(); ++i) { int const line_size = yuv->line_size()[i]; int const half_line_size = line_size / 2; + int const stride = yuv->stride()[i]; uint8_t* p = _image->data()[i]; uint8_t* q = yuv->data()[i]; for (int j = 0; j < yuv->lines (i); ++j) { memcpy (p + half_line_size, q + half_line_size, half_line_size); - p += line_size; - q += line_size; + p += stride; + q += stride; } } diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index 3e5c4b3aa..04735269c 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -188,7 +188,7 @@ DCPVideoFrame::encode_locally () int jn = 0; for (int y = 0; y < _out_size.height; ++y) { - uint8_t* p = prepared->data()[0] + y * prepared->line_size()[0]; + uint8_t* p = prepared->data()[0] + y * prepared->stride()[0]; for (int x = 0; x < _out_size.width; ++x) { /* In gamma LUT (converting 8-bit input to 12-bit) */ @@ -332,7 +332,7 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv) socket.write ((uint8_t *) s.str().c_str(), s.str().length() + 1, 30); for (int i = 0; i < _input->components(); ++i) { - socket.write (_input->data()[i], _input->line_size()[i] * _input->lines(i), 30); + socket.write (_input->data()[i], _input->stride()[i] * _input->lines(i), 30); } char buffer[32]; diff --git a/src/lib/image.cc b/src/lib/image.cc index c8849303c..2c0338b53 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -98,9 +98,9 @@ Image::scale (Size out_size, Scaler const * scaler) const sws_scale ( scale_context, - data(), line_size(), + data(), stride(), 0, size().height, - scaled->data (), scaled->line_size () + scaled->data(), scaled->stride() ); sws_freeContext (scale_context); @@ -131,9 +131,9 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal /* Scale and convert to RGB from whatever its currently in (which may be RGB) */ sws_scale ( scale_context, - data(), line_size(), + data(), stride(), 0, size().height, - rgb->data (), rgb->line_size () + rgb->data(), rgb->stride() ); /* Put the image in the right place in a black frame if are padding; this is @@ -151,8 +151,8 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal uint8_t* q = rgb->data()[0]; for (int j = 0; j < rgb->lines(0); ++j) { memcpy (p, q, rgb->line_size()[0]); - p += padded_rgb->line_size()[0]; - q += rgb->line_size()[0]; + p += padded_rgb->stride()[0]; + q += rgb->stride()[0]; } rgb = padded_rgb; @@ -176,8 +176,8 @@ Image::post_process (string pp) const pp_context* context = pp_get_context (size().width, size().height, PP_FORMAT_420 | PP_CPU_CAPS_MMX2); pp_postprocess ( - (const uint8_t **) data(), line_size(), - out->data(), out->line_size(), + (const uint8_t **) data(), stride(), + out->data(), out->stride(), size().width, size().height, 0, 0, mode, context, 0 ); @@ -193,13 +193,13 @@ Image::make_black () { switch (_pixel_format) { case PIX_FMT_YUV420P: - memset (data()[0], 0, lines(0) * line_size()[0]); - memset (data()[1], 0x80, lines(1) * line_size()[1]); - memset (data()[2], 0x80, lines(2) * line_size()[2]); + memset (data()[0], 0, lines(0) * stride()[0]); + memset (data()[1], 0x80, lines(1) * stride()[1]); + memset (data()[2], 0x80, lines(2) * stride()[2]); break; case PIX_FMT_RGB24: - memset (data()[0], 0, lines(0) * line_size()[0]); + memset (data()[0], 0, lines(0) * stride()[0]); break; default: @@ -230,8 +230,8 @@ Image::alpha_blend (shared_ptr other, Position position) } 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]; + uint8_t* tp = data()[0] + ty * stride()[0] + position.x * 3; + uint8_t* op = other->data()[0] + oy * other->stride()[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; @@ -257,25 +257,28 @@ SimpleImage::SimpleImage (PixelFormat p, Size s) _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; + _stride = (int *) av_malloc (4); + _stride[0] = _stride[1] = _stride[2] = _stride[3] = 0; switch (p) { case PIX_FMT_RGB24: - _line_size[0] = round_up (s.width * 3, 32); + _line_size[0] = s.width * 3; break; case PIX_FMT_RGBA: - _line_size[0] = round_up (s.width * 4, 32); + _line_size[0] = s.width * 4; break; case PIX_FMT_YUV420P: - _line_size[0] = round_up (s.width, 32); - _line_size[1] = round_up (s.width / 2, 32); - _line_size[2] = round_up (s.width / 2, 32); + _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] = (uint8_t *) av_malloc (_line_size[i] * lines (i)); + _stride[i] = round_up (_line_size[i], 32); + _data[i] = (uint8_t *) av_malloc (_stride[i] * lines (i)); } } @@ -288,6 +291,7 @@ SimpleImage::~SimpleImage () av_free (_data); av_free (_line_size); + av_free (_stride); } uint8_t ** @@ -302,6 +306,12 @@ SimpleImage::line_size () const return _line_size; } +int * +SimpleImage::stride () const +{ + return _stride; +} + Size SimpleImage::size () const { @@ -333,6 +343,13 @@ FilterBufferImage::line_size () const return _buffer->linesize; } +int * +FilterBufferImage::stride () const +{ + /* XXX? */ + return _buffer->linesize; +} + Size FilterBufferImage::size () const { @@ -377,6 +394,13 @@ RGBFrameImage::line_size () const return _frame->linesize; } +int * +RGBFrameImage::stride () const +{ + /* XXX? */ + return line_size (); +} + Size RGBFrameImage::size () const { diff --git a/src/lib/image.h b/src/lib/image.h index ea35fa0b9..3e16d43bf 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -57,9 +57,12 @@ public: /** @return Array of pointers to arrays of the component data */ virtual uint8_t ** data () const = 0; - /** @return Array of sizes of each line, in pixels */ + /** @return Array of sizes of the data in each line, in bytes (without any alignment padding bytes) */ virtual int * line_size () const = 0; + /** @return Array of strides for each line (including any alignment padding bytes) */ + virtual int * stride () const = 0; + /** @return Size of the image, in pixels */ virtual Size size () const = 0; @@ -91,6 +94,7 @@ public: uint8_t ** data () const; int * line_size () const; + int * stride () const; Size size () const; private: @@ -108,12 +112,15 @@ public: uint8_t ** data () const; int * line_size () const; + int * stride () const; Size size () const; private: Size _size; ///< size in pixels uint8_t** _data; ///< array of pointers to components - int* _line_size; ///< array of widths of each line, in bytes + int* _line_size; ///< array of sizes of the data in each line, in pixels (without any alignment padding bytes) + int* _stride; ///< array of strides for each line (including any alignment padding bytes) + }; /** @class RGBFrameImage @@ -127,6 +134,7 @@ public: uint8_t ** data () const; int * line_size () const; + int * stride () const; Size size () const; AVFrame * frame () const { return _frame; diff --git a/src/lib/server.cc b/src/lib/server.cc index 26b2be7c7..659418b8f 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -119,7 +119,7 @@ Server::process (shared_ptr socket) shared_ptr image (new SimpleImage (pixel_format, in_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); + socket->read_definite_and_consume (image->data()[i], image->stride()[i] * image->lines(i), 30); } /* XXX: subtitle */ -- cgit v1.2.3