From: Carl Hetherington Date: Tue, 4 Mar 2014 20:27:27 +0000 (+0000) Subject: Merge master. X-Git-Tag: v2.0.48~900 X-Git-Url: https://main.carlh.net/gitweb/?a=commitdiff_plain;h=a79d78d8bb6d51f6662f1f63b9f8fd19e1a0c5f1;hp=-c;p=dcpomatic.git Merge master. --- a79d78d8bb6d51f6662f1f63b9f8fd19e1a0c5f1 diff --combined src/lib/ffmpeg_content.cc index 4a6cf9e04,4ae5546c2..bd5648ecb --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_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 @@@ -60,7 -60,7 +60,7 @@@ FFmpegContent::FFmpegContent (shared_pt FFmpegContent::FFmpegContent (shared_ptr f, shared_ptr node, int version) : Content (f, node) - , VideoContent (f, node) + , VideoContent (f, node, version) , AudioContent (f, node) , SubtitleContent (f, node, version) { @@@ -163,8 -163,9 +163,8 @@@ FFmpegContent::examine (shared_ptr examiner (new FFmpegExaminer (shared_from_this ())); - VideoContent::Frame video_length = 0; - video_length = examiner->video_length (); - film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length)); + ContentTime video_length = examiner->video_length (); + film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length.frames (video_frame_rate ()))); { boost::mutex::scoped_lock lm (_mutex); @@@ -227,7 -228,7 +227,7 @@@ FFmpegContent::technical_summary () con string FFmpegContent::information () const { - if (video_length() == 0 || video_frame_rate() == 0) { + if (video_length() == ContentTime (0) || video_frame_rate() == ContentTime (0)) { return ""; } @@@ -261,17 -262,19 +261,17 @@@ FFmpegContent::set_audio_stream (shared signal_changed (FFmpegContentProperty::AUDIO_STREAM); } -AudioContent::Frame +ContentTime FFmpegContent::audio_length () const { - int const cafr = content_audio_frame_rate (); - int const vfr = video_frame_rate (); - VideoContent::Frame const vl = video_length (); - - boost::mutex::scoped_lock lm (_mutex); - if (!_audio_stream) { - return 0; + { + boost::mutex::scoped_lock lm (_mutex); + if (!_audio_stream) { + return ContentTime (); + } } - - return video_frames_to_audio_frames (vl, cafr, vfr); + + return video_length(); } int @@@ -307,15 -310,16 +307,15 @@@ FFmpegContent::output_audio_frame_rate /* Resample to a DCI-approved sample rate */ double t = dcp_audio_frame_rate (content_audio_frame_rate ()); - FrameRateConversion frc (video_frame_rate(), film->video_frame_rate()); + FrameRateChange frc (video_frame_rate(), film->video_frame_rate()); /* 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); @@@ -413,12 -417,14 +413,12 @@@ FFmpegSubtitleStream::as_xml (xmlpp::No FFmpegStream::as_xml (root); } -Time +DCPTime FFmpegContent::full_length () const { shared_ptr film = _film.lock (); assert (film); - - FrameRateConversion frc (video_frame_rate (), film->video_frame_rate ()); - return video_length() * frc.factor() * TIME_HZ / film->video_frame_rate (); + return DCPTime (video_length(), FrameRateChange (video_frame_rate (), film->video_frame_rate ())); } AudioMapping diff --combined src/lib/film.cc index 11fb9e4b0,00beb870f..aecb389fd --- a/src/lib/film.cc +++ b/src/lib/film.cc @@@ -31,11 -31,11 +31,11 @@@ #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include "film.h" #include "job.h" #include "util.h" @@@ -78,15 -78,17 +78,17 @@@ using boost::to_upper_copy using boost::ends_with; using boost::starts_with; using boost::optional; -using libdcp::Size; -using libdcp::Signer; +using dcp::Size; +using dcp::Signer; /* 5 -> 6 * AudioMapping XML changed. * 6 -> 7 * Subtitle offset changed to subtitle y offset, and subtitle x offset added. + * 7 -> 8 + * Use tag in rather than . */ - int const Film::current_state_version = 7; + int const Film::current_state_version = 8; /** Construct a Film object in a given directory. * @@@ -426,7 -428,7 +428,7 @@@ Film::read_metadata ( _sequence_video = f.bool_child ("SequenceVideo"); _three_d = f.bool_child ("ThreeD"); _interop = f.bool_child ("Interop"); - _key = libdcp::Key (f.string_child ("Key")); + _key = dcp::Key (f.string_child ("Key")); _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version); _dirty = false; @@@ -752,7 -754,7 +754,7 @@@ Film::j2c_path (int f, Eyes e, bool t) return file (p); } -/** @return List of subdirectories (not full paths) containing DCPs that can be successfully libdcp::DCP::read() */ +/** @return List of subdirectories (not full paths) containing DCPs that can be successfully dcp::DCP::read() */ list Film::dcps () const { @@@ -766,7 -768,7 +768,7 @@@ ) { try { - libdcp::DCP dcp (*i); + dcp::DCP dcp (*i); dcp.read (); out.push_back (i->path().leaf ()); } catch (...) { @@@ -861,7 -863,7 +863,7 @@@ Film::move_content_later (shared_ptrmove_later (c); } -Time +DCPTime Film::length () const { return _playlist->length (); @@@ -873,18 -875,12 +875,18 @@@ Film::has_subtitles () cons return _playlist->has_subtitles (); } -OutputVideoFrame +int Film::best_video_frame_rate () const { return _playlist->best_dcp_frame_rate (); } +FrameRateChange +Film::active_frame_rate_change (DCPTime t) const +{ + return _playlist->active_frame_rate_change (t, video_frame_rate ()); +} + void Film::playlist_content_changed (boost::weak_ptr c, int p) { @@@ -903,7 -899,31 +905,7 @@@ Film::playlist_changed ( signal_changed (CONTENT); } -OutputAudioFrame -Film::time_to_audio_frames (Time t) const -{ - return divide_with_round (t * audio_frame_rate (), TIME_HZ); -} - -OutputVideoFrame -Film::time_to_video_frames (Time t) const -{ - return divide_with_round (t * video_frame_rate (), TIME_HZ); -} - -Time -Film::audio_frames_to_time (OutputAudioFrame f) const -{ - return divide_with_round (f * TIME_HZ, audio_frame_rate ()); -} - -Time -Film::video_frames_to_time (OutputVideoFrame f) const -{ - return divide_with_round (f * TIME_HZ, video_frame_rate ()); -} - -OutputAudioFrame +int Film::audio_frame_rate () const { /* XXX */ @@@ -918,23 -938,23 +920,23 @@@ Film::set_sequence_video (bool s signal_changed (SEQUENCE_VIDEO); } -libdcp::Size +dcp::Size Film::full_frame () const { switch (_resolution) { case RESOLUTION_2K: - return libdcp::Size (2048, 1080); + return dcp::Size (2048, 1080); case RESOLUTION_4K: - return libdcp::Size (4096, 2160); + return dcp::Size (4096, 2160); } assert (false); - return libdcp::Size (); + return dcp::Size (); } -libdcp::KDM +dcp::KDM Film::make_kdm ( - shared_ptr target, + shared_ptr target, boost::filesystem::path dcp_dir, boost::posix_time::ptime from, boost::posix_time::ptime until @@@ -942,7 -962,7 +944,7 @@@ { shared_ptr signer = make_signer (); - libdcp::DCP dcp (dir (dcp_dir.string ())); + dcp::DCP dcp (dir (dcp_dir.string ())); try { dcp.read (); @@@ -952,14 -972,14 +954,14 @@@ time_t now = time (0); struct tm* tm = localtime (&now); - string const issue_date = libdcp::tm_to_string (tm); + string const issue_date = dcp::tm_to_string (tm); dcp.cpls().front()->set_mxf_keys (key ()); - return libdcp::KDM (dcp.cpls().front(), signer, target, from, until, "DCP-o-matic", issue_date); + return dcp::KDM (dcp.cpls().front(), signer, target, from, until, "DCP-o-matic", issue_date); } -list +list Film::make_kdms ( list > screens, boost::filesystem::path dcp, @@@ -967,7 -987,7 +969,7 @@@ boost::posix_time::ptime until ) const { - list kdms; + list kdms; for (list >::iterator i = screens.begin(); i != screens.end(); ++i) { kdms.push_back (make_kdm ((*i)->certificate, dcp, from, until)); @@@ -982,7 -1002,7 +984,7 @@@ uint64_t Film::required_disk_space () const { - return uint64_t (j2k_bandwidth() / 8) * length() / TIME_HZ; + return uint64_t (j2k_bandwidth() / 8) * length().seconds(); } /** This method checks the disk that the Film is on and tries to decide whether or not diff --combined src/lib/image.cc index b706f7fdc,c7dfc91cb..98645c299 --- 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 @@@ -31,13 -31,15 +31,16 @@@ extern "C" #include "image.h" #include "exceptions.h" #include "scaler.h" +#include "timer.h" + #include "i18n.h" + using std::string; using std::min; using std::cout; + using std::cerr; using boost::shared_ptr; -using libdcp::Size; +using dcp::Size; int Image::line_factor (int n) const @@@ -81,7 -83,7 +84,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 @@@ -89,26 -91,38 +92,38 @@@ */ assert (aligned ()); + assert (out_size.width >= inter_size.width); + assert (out_size.height >= inter_size.height); + + /* Here's an image of out_size */ shared_ptr out (new Image (out_format, out_size, out_aligned)); out->make_black (); - - dcp::Size cropped_size = crop.apply (size ()); + /* 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) { + throw StringError (N_("Could not allocate SwsContext")); + } + + /* Prepare input data pointers with crop */ uint8_t* scale_in_data[components()]; for (int c = 0; c < components(); ++c) { scale_in_data[c] = data()[c] + int (rint (bytes_per_pixel(c) * crop.left)) + stride()[c] * (crop.top / line_factor(c)); } + /* Corner of the image within out_size */ Position const corner ((out_size.width - inter_size.width) / 2, (out_size.height - inter_size.height) / 2); - uint8_t* scale_out_data[components()]; - for (int c = 0; c < components(); ++c) { + uint8_t* scale_out_data[out->components()]; + for (int c = 0; c < out->components(); ++c) { scale_out_data[c] = out->data()[c] + int (rint (out->bytes_per_pixel(c) * corner.x)) + out->stride()[c] * corner.y; } @@@ -125,7 -139,7 +140,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 @@@ -201,7 -215,7 +216,7 @@@ Image::post_process (string pp, bool al 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) { @@@ -376,18 -390,8 +391,18 @@@ 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 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; @@@ -406,15 -410,15 +421,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; } } } @@@ -498,8 -502,8 +513,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) { @@@ -536,7 -540,7 +551,7 @@@ Image::allocate ( } Image::Image (Image const & other) - : libdcp::Image (other) + : dcp::Image (other) , _pixel_format (other._pixel_format) , _aligned (other._aligned) { @@@ -554,7 -558,7 +569,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) { @@@ -573,7 -577,7 +588,7 @@@ } Image::Image (shared_ptr other, bool aligned) - : libdcp::Image (other) + : dcp::Image (other) , _pixel_format (other->_pixel_format) , _aligned (aligned) { @@@ -606,7 -610,7 +621,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); @@@ -649,7 -653,7 +664,7 @@@ Image::stride () cons return _stride; } -libdcp::Size +dcp::Size Image::size () const { return _size; diff --combined src/lib/image_content.cc index 2713280b4,fd0b57894..db02c6059 --- a/src/lib/image_content.cc +++ b/src/lib/image_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 @@@ -50,9 -50,9 +50,9 @@@ ImageContent::ImageContent (shared_ptr< } - ImageContent::ImageContent (shared_ptr f, shared_ptr node, int) + ImageContent::ImageContent (shared_ptr f, shared_ptr node, int version) : Content (f, node) - , VideoContent (f, node) + , VideoContent (f, node, version) { } @@@ -110,7 -110,7 +110,7 @@@ ImageContent::examine (shared_ptr } void -ImageContent::set_video_length (VideoContent::Frame len) +ImageContent::set_video_length (ContentTime len) { { boost::mutex::scoped_lock lm (_mutex); @@@ -120,12 -120,14 +120,12 @@@ signal_changed (ContentProperty::LENGTH); } -Time +DCPTime ImageContent::full_length () const { shared_ptr film = _film.lock (); assert (film); - - FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ()); - return video_length() * frc.factor() * TIME_HZ / video_frame_rate(); + return DCPTime (video_length(), FrameRateChange (video_frame_rate(), film->video_frame_rate())); } string diff --combined src/lib/image_content.h index 1aff043d2,e56abce4a..6db24767d --- a/src/lib/image_content.h +++ b/src/lib/image_content.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 @@@ -41,11 -41,11 +41,11 @@@ public std::string summary () const; std::string technical_summary () const; void as_xml (xmlpp::Node *) const; - Time full_length () const; + DCPTime full_length () const; std::string identifier () const; - void set_video_length (VideoContent::Frame); + void set_video_length (ContentTime); bool still () const; void set_video_frame_rate (float); }; diff --combined src/lib/player.cc index fc2f32d40,99aece8d6..59d046956 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@@ -18,26 -18,22 +18,26 @@@ */ #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 "ratio.h" -#include "resampler.h" #include "log.h" #include "scaler.h" +#include "render_subtitles.h" using std::list; using std::cout; @@@ -49,20 -45,71 +49,20 @@@ using std::map using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; +using boost::optional; class Piece { public: - Piece (shared_ptr c) - : content (c) - , video_position (c->position ()) - , audio_position (c->position ()) - , repeat_to_do (0) - , repeat_done (0) - {} - - Piece (shared_ptr c, shared_ptr d) + Piece (shared_ptr c, shared_ptr d, FrameRateChange f) : content (c) , decoder (d) - , video_position (c->position ()) - , audio_position (c->position ()) - , repeat_to_do (0) - , repeat_done (0) + , frc (f) {} - /** Set this piece to repeat a video frame a given number of times */ - void set_repeat (IncomingVideo video, int num) - { - repeat_video = video; - repeat_to_do = num; - repeat_done = 0; - } - - void reset_repeat () - { - repeat_video.image.reset (); - repeat_to_do = 0; - repeat_done = 0; - } - - bool repeating () const - { - return repeat_done != repeat_to_do; - } - - void repeat (Player* player) - { - player->process_video ( - repeat_video.weak_piece, - repeat_video.image, - repeat_video.eyes, - repeat_done > 0, - repeat_video.frame, - (repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ()) - ); - - ++repeat_done; - } - shared_ptr content; shared_ptr decoder; - /** Time of the last video we emitted relative to the start of the DCP */ - Time video_position; - /** Time of the last audio we emitted relative to the start of the DCP */ - Time audio_position; - - IncomingVideo repeat_video; - int repeat_to_do; - int repeat_done; + FrameRateChange frc; }; Player::Player (shared_ptr f, shared_ptr p) @@@ -73,10 -120,8 +73,10 @@@ , _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)) + , _audio_merger (f->audio_channels(), f->audio_frame_rate ()) , _last_emit_was_black (false) + , _just_did_inaccurate_seek (false) + , _approximate_size (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)); @@@ -103,167 -148,111 +103,167 @@@ Player::pass ( setup_pieces (); } - Time earliest_t = TIME_MAX; - shared_ptr earliest; - enum { - VIDEO, - AUDIO - } type = VIDEO; + /* Interrogate all our pieces to find the one with the earliest decoded data */ + + shared_ptr earliest_piece; + shared_ptr earliest_decoded; + DCPTime earliest_time = DCPTime::max (); + DCPTime earliest_audio = DCPTime::max (); for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { - if ((*i)->decoder->done ()) { - continue; - } - shared_ptr vd = dynamic_pointer_cast ((*i)->decoder); - shared_ptr ad = dynamic_pointer_cast ((*i)->decoder); + DCPTime const offset = (*i)->content->position() - (*i)->content->trim_start(); + + bool done = false; + shared_ptr dec; + while (!done) { + dec = (*i)->decoder->peek (); + if (!dec) { + /* Decoder has nothing else to give us */ + break; + } - if (_video && vd) { - if ((*i)->video_position < earliest_t) { - earliest_t = (*i)->video_position; - earliest = *i; - type = VIDEO; + + dec->set_dcp_times ((*i)->frc, offset); + DCPTime const t = dec->dcp_time - offset; + cout << "Peeked " << (*i)->content->paths()[0] << " for " << t << " cf " << ((*i)->content->full_length() - (*i)->content->trim_end ()) << "\n"; + if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) { + /* In the end-trimmed part; decoder has nothing else to give us */ + dec.reset (); + done = true; + } else if (t >= (*i)->content->trim_start ()) { + /* Within the un-trimmed part; everything's ok */ + done = true; + } else { + /* Within the start-trimmed part; get something else */ + (*i)->decoder->consume (); } } - if (_audio && ad && ad->has_audio ()) { - if ((*i)->audio_position < earliest_t) { - earliest_t = (*i)->audio_position; - earliest = *i; - type = AUDIO; - } + if (!dec) { + continue; } - } - if (!earliest) { + if (dec->dcp_time < earliest_time) { + earliest_piece = *i; + earliest_decoded = dec; + earliest_time = dec->dcp_time; + } + + if (dynamic_pointer_cast (dec) && dec->dcp_time < earliest_audio) { + earliest_audio = dec->dcp_time; + } + } + + if (!earliest_piece) { flush (); return true; } - switch (type) { - case VIDEO: - if (earliest_t > _video_position) { - emit_black (); - } else { - if (earliest->repeating ()) { - earliest->repeat (this); - } else { - earliest->decoder->pass (); - } - } - 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 ()); - } - } - } + if (earliest_audio != DCPTime::max ()) { + if (earliest_audio.get() < 0) { + earliest_audio = DCPTime (); } - break; + TimedAudioBuffers tb = _audio_merger.pull (earliest_audio); + Audio (tb.audio, tb.time); + /* This assumes that the audio-frames-to-time conversion is exact + so that there are no accumulated errors caused by rounding. + */ + _audio_position += DCPTime::from_frames (tb.audio->frames(), _film->audio_frame_rate ()); } - if (_audio) { - boost::optional