From: Carl Hetherington Date: Wed, 4 Jun 2014 11:33:41 +0000 (+0100) Subject: Merge master. X-Git-Tag: v2.0.48~804 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=8102046b2f29e0c7b234c29bf204b056cb30e64f;hp=-c Merge master. --- 8102046b2f29e0c7b234c29bf204b056cb30e64f diff --combined ChangeLog index e31fc719e,a0e776b0e..78954c82d --- a/ChangeLog +++ b/ChangeLog @@@ -1,7 -1,25 +1,29 @@@ +2014-03-07 Carl Hetherington + + * Add subtitle view. + + 2014-06-03 Carl Hetherington + + * Fix bad resampling of separate sound file sources that + have specified video frame rates. + + * Version 1.69.20 released. + + 2014-06-03 Carl Hetherington + + * Re-calculate and update audio plots when the mapping is changed. + + * Change the -3dB preset to -6dB since we are talking about + amplitude, not power. + + * Version 1.69.19 released. + + 2014-06-02 Carl Hetherington + + * Empirical hack to prevent over-read of array + by libswscale; may fix crashes at the start of + DCP encodes. + 2014-05-29 Carl Hetherington * Version 1.69.18 released. diff --combined src/lib/audio_content.cc index c78dd3e69,79912f1ae..03bfe9630 --- a/src/lib/audio_content.cc +++ b/src/lib/audio_content.cc @@@ -1,5 -1,5 +1,5 @@@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2014 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 @@@ -18,7 -18,7 +18,7 @@@ */ #include -#include +#include #include "audio_content.h" #include "analyse_audio_job.h" #include "job_manager.h" @@@ -29,10 -29,11 +29,11 @@@ #include "i18n.h" using std::string; + using std::cout; using std::vector; using boost::shared_ptr; using boost::dynamic_pointer_cast; -using libdcp::raw_convert; +using dcp::raw_convert; int const AudioContentProperty::AUDIO_CHANNELS = 200; int const AudioContentProperty::AUDIO_LENGTH = 201; @@@ -41,7 -42,7 +42,7 @@@ int const AudioContentProperty::AUDIO_G int const AudioContentProperty::AUDIO_DELAY = 204; int const AudioContentProperty::AUDIO_MAPPING = 205; -AudioContent::AudioContent (shared_ptr f, Time s) +AudioContent::AudioContent (shared_ptr f, DCPTime s) : Content (f, s) , _audio_gain (0) , _audio_delay (Config::instance()->default_audio_delay ()) @@@ -57,7 -58,7 +58,7 @@@ AudioContent::AudioContent (shared_ptr< } -AudioContent::AudioContent (shared_ptr f, shared_ptr node) +AudioContent::AudioContent (shared_ptr f, cxml::ConstNodePtr node) : Content (f, node) { _audio_gain = node->number_child ("AudioGain"); @@@ -139,50 -140,37 +140,50 @@@ AudioContent::audio_analysis_path () co } boost::filesystem::path p = film->audio_analysis_dir (); - p /= digest (); + p /= digest() + "_" + audio_mapping().digest(); return p; } string AudioContent::technical_summary () const { - return String::compose ("audio: channels %1, length %2, raw rate %3, out rate %4", audio_channels(), audio_length(), content_audio_frame_rate(), output_audio_frame_rate()); + return String::compose ( + "audio: channels %1, length %2, content rate %3, resampled rate %4", + audio_channels(), + audio_length().seconds(), + audio_frame_rate(), + resampled_audio_frame_rate() + ); } +void +AudioContent::set_audio_mapping (AudioMapping) +{ + signal_changed (AudioContentProperty::AUDIO_MAPPING); +} + +/** @return the frame rate that this content should be resampled to in order + * that it is in sync with the active video content at its start time. + */ int -AudioContent::output_audio_frame_rate () const +AudioContent::resampled_audio_frame_rate () const { shared_ptr film = _film.lock (); assert (film); /* Resample to a DCI-approved sample rate */ - double t = dcp_audio_frame_rate (content_audio_frame_rate ()); + double t = dcp_audio_frame_rate (audio_frame_rate ()); - FrameRateConversion frc (video_frame_rate(), film->video_frame_rate()); + FrameRateChange frc = film->active_frame_rate_change (position ()); /* Compensate if the DCP is being run at a different frame rate to the source; that is, if the video is run such that it will look different in the DCP compared to the source (slower or faster). - skip/repeat doesn't come into effect here. */ if (frc.change_speed) { - t *= video_frame_rate() * frc.factor() / film->video_frame_rate(); + t /= frc.speed_up; } return rint (t); } - diff --combined src/lib/audio_mapping.cc index 7d7e9f828,e35c1ae94..b3757c5f1 --- a/src/lib/audio_mapping.cc +++ b/src/lib/audio_mapping.cc @@@ -1,5 -1,5 +1,5 @@@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2014 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 @@@ -19,9 -19,10 +19,10 @@@ #include #include -#include +#include #include "audio_mapping.h" #include "util.h" + #include "md5_digester.h" using std::list; using std::cout; @@@ -31,7 -32,7 +32,7 @@@ using std::string using std::min; using boost::shared_ptr; using boost::dynamic_pointer_cast; -using libdcp::raw_convert; +using dcp::raw_convert; AudioMapping::AudioMapping () : _content_channels (0) @@@ -40,11 -41,11 +41,11 @@@ } /** Create a default AudioMapping for a given channel count. - * @param c Number of channels. + * @param channels Number of channels. */ -AudioMapping::AudioMapping (int c) +AudioMapping::AudioMapping (int channels) { - setup (c); + setup (channels); } void @@@ -69,16 -70,16 +70,16 @@@ AudioMapping::make_default ( if (_content_channels == 1) { /* Mono -> Centre */ - set (0, libdcp::CENTRE, 1); + set (0, dcp::CENTRE, 1); } else { /* 1:1 mapping */ for (int i = 0; i < min (_content_channels, MAX_DCP_AUDIO_CHANNELS); ++i) { - set (i, static_cast (i), 1); + set (i, static_cast (i), 1); } } } -AudioMapping::AudioMapping (shared_ptr node, int state_version) +AudioMapping::AudioMapping (cxml::ConstNodePtr node, int state_version) { setup (node->number_child ("ContentChannels")); @@@ -86,14 -87,14 +87,14 @@@ /* Old-style: on/off mapping */ list const c = node->node_children ("Map"); for (list::const_iterator i = c.begin(); i != c.end(); ++i) { - set ((*i)->number_child ("ContentIndex"), static_cast ((*i)->number_child ("DCP")), 1); + set ((*i)->number_child ("ContentIndex"), static_cast ((*i)->number_child ("DCP")), 1); } } else { list const c = node->node_children ("Gain"); for (list::const_iterator i = c.begin(); i != c.end(); ++i) { set ( (*i)->number_attribute ("Content"), - static_cast ((*i)->number_attribute ("DCP")), + static_cast ((*i)->number_attribute ("DCP")), raw_convert ((*i)->content ()) ); } @@@ -101,13 -102,13 +102,13 @@@ } void -AudioMapping::set (int c, libdcp::Channel d, float g) +AudioMapping::set (int c, dcp::Channel d, float g) { _gain[c][d] = g; } float -AudioMapping::get (int c, libdcp::Channel d) const +AudioMapping::get (int c, dcp::Channel d) const { return _gain[c][d]; } @@@ -122,7 -123,24 +123,24 @@@ AudioMapping::as_xml (xmlpp::Node* node xmlpp::Element* t = node->add_child ("Gain"); t->set_attribute ("Content", raw_convert (c)); t->set_attribute ("DCP", raw_convert (d)); - t->add_child_text (raw_convert (get (c, static_cast (d)))); + t->add_child_text (raw_convert (get (c, static_cast (d)))); } } } + + /** @return a string which is unique for a given AudioMapping configuration, for + * differentiation between different AudioMappings. + */ + string + AudioMapping::digest () const + { + MD5Digester digester; + digester.add (_content_channels); + for (int i = 0; i < _content_channels; ++i) { + for (int j = 0; j < MAX_DCP_AUDIO_CHANNELS; ++j) { + digester.add (_gain[i][j]); + } + } + + return digester.get (); + } diff --combined src/lib/audio_mapping.h index 7bf974c22,b0b75ac06..8be8eeb6f --- a/src/lib/audio_mapping.h +++ b/src/lib/audio_mapping.h @@@ -1,5 -1,5 +1,5 @@@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2014 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 @@@ -21,9 -21,8 +21,9 @@@ #define DCPOMATIC_AUDIO_MAPPING_H #include -#include #include +#include +#include namespace xmlpp { class Node; @@@ -42,8 -41,8 +42,8 @@@ class AudioMappin { public: AudioMapping (); - AudioMapping (int); - AudioMapping (boost::shared_ptr, int); + AudioMapping (int channels); + AudioMapping (cxml::ConstNodePtr, int); /* Default copy constructor is fine */ @@@ -51,12 -50,14 +51,14 @@@ void make_default (); - void set (int, libdcp::Channel, float); - float get (int, libdcp::Channel) const; + void set (int, dcp::Channel, float); + float get (int, dcp::Channel) const; int content_channels () const { return _content_channels; } + + std::string digest () const; private: void setup (int); diff --combined src/lib/colour_conversion.cc index 48fd6ed9c,f8675900e..aacefaa05 --- a/src/lib/colour_conversion.cc +++ b/src/lib/colour_conversion.cc @@@ -18,12 -18,13 +18,13 @@@ */ #include -#include -#include +#include +#include #include #include "config.h" #include "colour_conversion.h" #include "util.h" + #include "md5_digester.h" #include "i18n.h" @@@ -34,7 -35,7 +35,7 @@@ using std::cout using std::vector; using boost::shared_ptr; using boost::optional; -using libdcp::raw_convert; +using dcp::raw_convert; ColourConversion::ColourConversion () : input_gamma (2.4) @@@ -44,7 -45,7 +45,7 @@@ { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - matrix (i, j) = libdcp::colour_matrix::srgb_to_xyz[i][j]; + matrix (i, j) = dcp::colour_matrix::srgb_to_xyz[i][j]; } } } @@@ -121,21 -122,18 +122,18 @@@ ColourConversion::preset () cons string ColourConversion::identifier () const { - double numbers[12]; - - int n = 0; - numbers[n++] = input_gamma; - numbers[n++] = input_gamma_linearised; + MD5Digester digester; + + digester.add (input_gamma); + digester.add (input_gamma_linearised); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - numbers[n++] = matrix (i, j); + digester.add (matrix (i, j)); } } - numbers[n++] = output_gamma; - - assert (n == 12); - - return md5_digest (numbers, 12 * sizeof (double)); + digester.add (output_gamma); + + return digester.get (); } PresetColourConversion::PresetColourConversion () diff --combined src/lib/dcp_video_frame.cc index 1aae64ac7,09b909696..4054f05cd --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@@ -42,13 -42,13 +42,12 @@@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include - #include +#include +#include +#include +#include +#include #include #include "film.h" #include "dcp_video_frame.h" @@@ -70,9 -70,8 +69,9 @@@ using std::string using std::stringstream; using std::cout; using boost::shared_ptr; -using libdcp::Size; -using libdcp::raw_convert; +using boost::lexical_cast; +using dcp::Size; +using dcp::raw_convert; #define DCI_COEFFICENT (48.0 / 52.37) @@@ -111,10 -110,13 +110,10 @@@ DCPVideoFrame::DCPVideoFrame (shared_pt shared_ptr DCPVideoFrame::encode_locally () { - shared_ptr in_lut; - if (_frame->colour_conversion().input_gamma_linearised) { - in_lut = libdcp::SRGBLinearisedGammaLUT::cache.get (12, _frame->colour_conversion().input_gamma); - } else { - in_lut = libdcp::GammaLUT::cache.get (12, _frame->colour_conversion().input_gamma); - } - + shared_ptr in_lut = dcp::GammaLUT::cache.get ( + 12, _frame->colour_conversion().input_gamma, _frame->colour_conversion().input_gamma_linearised + ); + /* XXX: libdcp should probably use boost */ double matrix[3][3]; @@@ -124,28 -126,13 +123,13 @@@ } } - shared_ptr xyz = libdcp::rgb_to_xyz ( + shared_ptr xyz = dcp::rgb_to_xyz ( _frame->image(), in_lut, - libdcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma), + dcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma, false), matrix ); - { - MD5_CTX md5_context; - MD5_Init (&md5_context); - MD5_Update (&md5_context, xyz->data(0), 1998 * 1080 * 4); - MD5_Update (&md5_context, xyz->data(1), 1998 * 1080 * 4); - MD5_Update (&md5_context, xyz->data(2), 1998 * 1080 * 4); - unsigned char digest[MD5_DIGEST_LENGTH]; - MD5_Final (digest, &md5_context); - - stringstream s; - for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { - s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]); - } - } - /* Set the max image and component sizes based on frame_rate */ int max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second; if (_frame->eyes() == EYES_LEFT || _frame->eyes() == EYES_RIGHT) { @@@ -387,7 -374,7 +371,7 @@@ EncodedData::write (shared_ptr film, int frame, Eyes eyes, libdcp::FrameInfo fin) const +EncodedData::write_info (shared_ptr film, int frame, Eyes eyes, dcp::FrameInfo fin) const { boost::filesystem::path const info = film->info_path (frame, eyes); FILE* h = fopen_boost (info, "w"); diff --combined src/lib/image.cc index d4ec6f99a,8a8fb1c7b..8e7a51fd8 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@@ -1,5 -1,5 +1,5 @@@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 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 @@@ -22,7 -22,6 +22,6 @@@ */ #include - #include extern "C" { #include #include @@@ -31,8 -30,7 +30,9 @@@ #include "image.h" #include "exceptions.h" #include "scaler.h" +#include "timer.h" +#include "rect.h" + #include "md5_digester.h" #include "i18n.h" @@@ -40,10 -38,9 +40,10 @@@ using std::string using std::min; using std::cout; using std::cerr; +using std::list; using std::stringstream; using boost::shared_ptr; -using libdcp::Size; +using dcp::Size; int Image::line_factor (int n) const @@@ -87,7 -84,7 +87,7 @@@ Image::components () cons /** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size' */ shared_ptr -Image::crop_scale_window (Crop crop, libdcp::Size inter_size, libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const +Image::crop_scale_window (Crop crop, dcp::Size inter_size, dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const { assert (scaler); /* Empirical testing suggests that sws_scale() will crash if @@@ -103,13 -100,13 +103,13 @@@ out->make_black (); /* Size of the image after any crop */ - libdcp::Size const cropped_size = crop.apply (size ()); + dcp::Size const cropped_size = crop.apply (size ()); /* Scale context for a scale from cropped_size to inter_size */ struct SwsContext* scale_context = sws_getContext ( - cropped_size.width, cropped_size.height, pixel_format(), - inter_size.width, inter_size.height, out_format, - scaler->ffmpeg_id (), 0, 0, 0 + cropped_size.width, cropped_size.height, pixel_format(), + inter_size.width, inter_size.height, out_format, + scaler->ffmpeg_id (), 0, 0, 0 ); if (!scale_context) { @@@ -143,7 -140,7 +143,7 @@@ } shared_ptr -Image::scale (libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const +Image::scale (dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const { assert (scaler); /* Empirical testing suggests that sws_scale() will crash if @@@ -174,7 -171,7 +174,7 @@@ shared_ptr Image::crop (Crop crop, bool aligned) const { - libdcp::Size cropped_size = crop.apply (size ()); + dcp::Size cropped_size = crop.apply (size ()); shared_ptr out (new Image (pixel_format(), cropped_size, aligned)); for (int c = 0; c < components(); ++c) { @@@ -346,31 -343,11 +346,31 @@@ Image::make_black ( } } +void +Image::make_transparent () +{ + if (_pixel_format != PIX_FMT_RGBA) { + throw PixelFormatError ("make_transparent()", _pixel_format); + } + + memset (data()[0], 0, lines(0) * stride()[0]); +} + 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 this_bpp = 0; + int other_bpp = 0; + + if (_pixel_format == PIX_FMT_BGRA && other->pixel_format() == PIX_FMT_RGBA) { + this_bpp = 4; + other_bpp = 4; + } else if (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA) { + this_bpp = 3; + other_bpp = 4; + } else { + assert (false); + } int start_tx = position.x; int start_ox = 0; @@@ -389,15 -366,15 +389,15 @@@ } for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) { - uint8_t* tp = data()[0] + ty * stride()[0] + position.x * 3; + uint8_t* tp = data()[0] + ty * stride()[0] + position.x * this_bpp; 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; tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha; tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha; - tp += 3; - op += 4; + tp += this_bpp; + op += other_bpp; } } } @@@ -481,8 -458,8 +481,8 @@@ Image::bytes_per_pixel (int c) cons * @param p Pixel format. * @param s Size in pixels. */ -Image::Image (AVPixelFormat p, libdcp::Size s, bool aligned) - : libdcp::Image (s) +Image::Image (AVPixelFormat p, dcp::Size s, bool aligned) + : dcp::Image (s) , _pixel_format (p) , _aligned (aligned) { @@@ -513,13 -490,18 +513,18 @@@ Image::allocate ( OS X crashes on this illegal read, though other operating systems don't seem to mind. The nasty + 1 in this malloc makes sure there is always a byte for that instruction to read safely. + + Further to the above, valgrind is now telling me that ff_rgb24ToY_ssse3 + over-reads by more then _avx. I can't follow the code to work out how much, + so I'll just over-allocate by 32 bytes and have done with it. Empirical + testing suggests that it works. */ - _data[i] = (uint8_t *) wrapped_av_malloc (_stride[i] * lines (i) + 1); + _data[i] = (uint8_t *) wrapped_av_malloc (_stride[i] * lines (i) + 32); } } Image::Image (Image const & other) - : libdcp::Image (other) + : dcp::Image (other) , _pixel_format (other._pixel_format) , _aligned (other._aligned) { @@@ -537,7 -519,7 +542,7 @@@ } Image::Image (AVFrame* frame) - : libdcp::Image (libdcp::Size (frame->width, frame->height)) + : dcp::Image (dcp::Size (frame->width, frame->height)) , _pixel_format (static_cast (frame->format)) , _aligned (true) { @@@ -556,7 -538,7 +561,7 @@@ } Image::Image (shared_ptr other, bool aligned) - : libdcp::Image (other) + : dcp::Image (other) , _pixel_format (other->_pixel_format) , _aligned (aligned) { @@@ -589,7 -571,7 +594,7 @@@ Image::operator= (Image const & other void Image::swap (Image & other) { - libdcp::Image::swap (other); + dcp::Image::swap (other); std::swap (_pixel_format, other._pixel_format); @@@ -632,7 -614,7 +637,7 @@@ Image::stride () cons return _stride; } -libdcp::Size +dcp::Size Image::size () const { return _size; @@@ -644,44 -626,15 +649,35 @@@ Image::aligned () cons return _aligned; } +PositionImage +merge (list images) +{ + if (images.empty ()) { + return PositionImage (); + } + + dcpomatic::Rect all (images.front().position, images.front().image->size().width, images.front().image->size().height); + for (list::const_iterator i = images.begin(); i != images.end(); ++i) { + all.extend (dcpomatic::Rect (i->position, i->image->size().width, i->image->size().height)); + } + + shared_ptr merged (new Image (images.front().image->pixel_format (), dcp::Size (all.width, all.height), true)); + merged->make_transparent (); + for (list::const_iterator i = images.begin(); i != images.end(); ++i) { + merged->alpha_blend (i->image, i->position); + } + + return PositionImage (merged, all.position ()); +} + string Image::digest () const { - MD5_CTX md5_context; - MD5_Init (&md5_context); + MD5Digester digester; for (int i = 0; i < components(); ++i) { - MD5_Update (&md5_context, data()[i], line_size()[i]); - } - - unsigned char digest[MD5_DIGEST_LENGTH]; - MD5_Final (digest, &md5_context); - - stringstream s; - for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { - s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]); + digester.add (data()[i], line_size()[i]); } - return s.str (); + return digester.get (); } - diff --combined src/lib/player.cc index 77def1e60,68df8ea70..c3489b7e1 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@@ -18,29 -18,23 +18,29 @@@ */ #include +#include #include "player.h" #include "film.h" #include "ffmpeg_decoder.h" +#include "audio_buffers.h" #include "ffmpeg_content.h" #include "image_decoder.h" #include "image_content.h" #include "sndfile_decoder.h" #include "sndfile_content.h" #include "subtitle_content.h" +#include "subrip_decoder.h" +#include "subrip_content.h" #include "playlist.h" #include "job.h" #include "image.h" #include "image_proxy.h" #include "ratio.h" -#include "resampler.h" #include "log.h" #include "scaler.h" +#include "render_subtitles.h" +#include "config.h" +#include "content_video.h" #include "player_video_frame.h" #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL); @@@ -49,22 -43,23 +49,22 @@@ using std::list using std::cout; using std::min; using std::max; +using std::min; using std::vector; using std::pair; using std::map; +using std::make_pair; using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; +using boost::optional; Player::Player (shared_ptr f, shared_ptr p) : _film (f) , _playlist (p) - , _video (true) - , _audio (true) , _have_valid_pieces (false) - , _video_position (0) - , _audio_position (0) - , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1)) - , _last_emit_was_black (false) + , _approximate_size (false) + , _burn_subtitles (false) { _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this)); _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3)); @@@ -73,477 -68,573 +73,478 @@@ } void -Player::disable_video () -{ - _video = false; -} - -void -Player::disable_audio () +Player::setup_pieces () { - _audio = false; -} + list > old_pieces = _pieces; + _pieces.clear (); -bool -Player::pass () -{ - if (!_have_valid_pieces) { - setup_pieces (); - } + ContentList content = _playlist->content (); - Time earliest_t = TIME_MAX; - shared_ptr earliest; - enum { - VIDEO, - AUDIO - } type = VIDEO; + for (ContentList::iterator i = content.begin(); i != content.end(); ++i) { - for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { - if ((*i)->decoder->done ()) { + if (!(*i)->paths_valid ()) { continue; } - - shared_ptr vd = dynamic_pointer_cast ((*i)->decoder); - shared_ptr ad = dynamic_pointer_cast ((*i)->decoder); - - if (_video && vd) { - if ((*i)->video_position < earliest_t) { - earliest_t = (*i)->video_position; - earliest = *i; - type = VIDEO; + + shared_ptr decoder; + optional frc; + + /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */ + DCPTime best_overlap_t; + shared_ptr best_overlap; + for (ContentList::iterator j = content.begin(); j != content.end(); ++j) { + shared_ptr vc = dynamic_pointer_cast (*j); + if (!vc) { + continue; } - } - - if (_audio && ad && ad->has_audio ()) { - if ((*i)->audio_position < earliest_t) { - earliest_t = (*i)->audio_position; - earliest = *i; - type = AUDIO; + + DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end()); + if (overlap > best_overlap_t) { + best_overlap = vc; + best_overlap_t = overlap; } } - } - - if (!earliest) { - flush (); - return true; - } - switch (type) { - case VIDEO: - if (earliest_t > _video_position) { - emit_black (); + optional best_overlap_frc; + if (best_overlap) { + best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ()); } else { - if (earliest->repeating ()) { - earliest->repeat (this); - } else { - earliest->decoder->pass (); - } + /* No video overlap; e.g. if the DCP is just audio */ + best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ()); } - break; - case AUDIO: - if (earliest_t > _audio_position) { - emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position)); - } else { - earliest->decoder->pass (); - - if (earliest->decoder->done()) { - shared_ptr ac = dynamic_pointer_cast (earliest->content); - assert (ac); - shared_ptr re = resampler (ac, false); - if (re) { - shared_ptr b = re->flush (); - if (b->frames ()) { - process_audio (earliest, b, ac->audio_length (), true); - } - } - } + /* FFmpeg */ + shared_ptr fc = dynamic_pointer_cast (*i); + if (fc) { + decoder.reset (new FFmpegDecoder (fc, _film->log())); + frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate()); } - break; - } - if (_audio) { - boost::optional