diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-06-26 01:21:21 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-06-26 17:04:31 +0100 |
| commit | 09a9ac376db005a40a351736bcff4077f098825d (patch) | |
| tree | 64ea69741155d15d114ad96daf0f90e24b3abe28 /src | |
| parent | 46cd0fe7b5b514f0d9456b25f670679cc584a218 (diff) | |
Another try at sorting out the thorny question of timing.
Diffstat (limited to 'src')
56 files changed, 566 insertions, 1622 deletions
diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index 73a00ca7d..9bf53e0ab 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -41,6 +41,8 @@ public: class AudioContent : public virtual Content { public: + typedef int64_t Frame; + AudioContent (boost::shared_ptr<const Film>, Time); AudioContent (boost::shared_ptr<const Film>, boost::filesystem::path); AudioContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); @@ -49,7 +51,7 @@ public: void as_xml (xmlpp::Node *) const; virtual int audio_channels () const = 0; - virtual ContentAudioFrame audio_length () const = 0; + virtual AudioContent::Frame audio_length () const = 0; virtual int content_audio_frame_rate () const = 0; virtual int output_audio_frame_rate () const = 0; virtual AudioMapping audio_mapping () const = 0; diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index a9e01908c..dc49a1846 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -31,57 +31,12 @@ using std::cout; using boost::optional; using boost::shared_ptr; -AudioDecoder::AudioDecoder (shared_ptr<const Film> f, shared_ptr<const AudioContent> c) +AudioDecoder::AudioDecoder (shared_ptr<const Film> f) : Decoder (f) - , _next_audio (0) - , _audio_content (c) + , _audio_position (0) { - if (_audio_content->content_audio_frame_rate() != _audio_content->output_audio_frame_rate()) { - - shared_ptr<const Film> film = _film.lock (); - assert (film); - - stringstream s; - s << String::compose ( - "Will resample audio from %1 to %2", - _audio_content->content_audio_frame_rate(), _audio_content->output_audio_frame_rate() - ); - - film->log()->log (s.str ()); - - /* We will be using planar float data when we call the - resampler. As far as I can see, the audio channel - layout is not necessary for our purposes; it seems - only to be used get the number of channels and - decide if rematrixing is needed. It won't be, since - input and output layouts are the same. - */ - - _swr_context = swr_alloc_set_opts ( - 0, - av_get_default_channel_layout (_audio_content->audio_channels ()), - AV_SAMPLE_FMT_FLTP, - _audio_content->output_audio_frame_rate(), - av_get_default_channel_layout (_audio_content->audio_channels ()), - AV_SAMPLE_FMT_FLTP, - _audio_content->content_audio_frame_rate(), - 0, 0 - ); - - swr_init (_swr_context); - } else { - _swr_context = 0; - } } -AudioDecoder::~AudioDecoder () -{ - if (_swr_context) { - swr_free (&_swr_context); - } -} - - #if 0 void AudioDecoder::process_end () @@ -113,54 +68,8 @@ AudioDecoder::process_end () #endif void -AudioDecoder::audio (shared_ptr<const AudioBuffers> data, Time time) -{ - /* Maybe resample */ - if (_swr_context) { - - /* Compute the resampled frames count and add 32 for luck */ - int const max_resampled_frames = ceil ( - (int64_t) data->frames() * _audio_content->output_audio_frame_rate() / _audio_content->content_audio_frame_rate() - ) + 32; - - shared_ptr<AudioBuffers> resampled (new AudioBuffers (data->channels(), max_resampled_frames)); - - /* Resample audio */ - int const resampled_frames = swr_convert ( - _swr_context, (uint8_t **) resampled->data(), max_resampled_frames, (uint8_t const **) data->data(), data->frames() - ); - - if (resampled_frames < 0) { - throw EncodeError (_("could not run sample-rate converter")); - } - - resampled->set_frames (resampled_frames); - - /* And point our variables at the resampled audio */ - data = resampled; - } - - shared_ptr<const Film> film = _film.lock (); - assert (film); - - /* Remap channels */ - shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (film->dcp_audio_channels(), data->frames())); - dcp_mapped->make_silent (); - list<pair<int, libdcp::Channel> > map = _audio_content->audio_mapping().content_to_dcp (); - for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) { - dcp_mapped->accumulate_channel (data.get(), i->first, i->second); - } - - Audio (dcp_mapped, time); - _next_audio = time + film->audio_frames_to_time (data->frames()); -} - -bool -AudioDecoder::audio_done () const +AudioDecoder::audio (shared_ptr<const AudioBuffers> data, AudioContent::Frame frame) { - shared_ptr<const Film> film = _film.lock (); - assert (film); - - return (_audio_content->length() - _next_audio) < film->audio_frames_to_time (1); + Audio (data, frame); + _audio_position = frame + data->frames (); } - diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index 1da8a676f..ddfb296c9 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -24,33 +24,26 @@ #ifndef DCPOMATIC_AUDIO_DECODER_H #define DCPOMATIC_AUDIO_DECODER_H -#include "audio_source.h" #include "decoder.h" -extern "C" { -#include <libswresample/swresample.h> -} +#include "content.h" -class AudioContent; +class AudioBuffers; /** @class AudioDecoder. * @brief Parent class for audio decoders. */ -class AudioDecoder : public AudioSource, public virtual Decoder +class AudioDecoder : public virtual Decoder { public: - AudioDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const AudioContent>); - ~AudioDecoder (); + AudioDecoder (boost::shared_ptr<const Film>); -protected: - - void audio (boost::shared_ptr<const AudioBuffers>, Time); - bool audio_done () const; + /** Emitted when some audio data is ready */ + boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame)> Audio; - Time _next_audio; - boost::shared_ptr<const AudioContent> _audio_content; +protected: -private: - SwrContext* _swr_context; + void audio (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame); + AudioContent::Frame _audio_position; }; #endif diff --git a/src/lib/audio_sink.h b/src/lib/audio_sink.h deleted file mode 100644 index 1aad5edf9..000000000 --- a/src/lib/audio_sink.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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. - -*/ - -#ifndef DCPOMATIC_AUDIO_SINK_H -#define DCPOMATIC_AUDIO_SINK_H - -class AudioBuffers; - -class AudioSink -{ -public: - /** Call with some audio data */ - virtual void process_audio (boost::shared_ptr<const AudioBuffers>, Time) = 0; -}; - -#endif diff --git a/src/lib/audio_source.cc b/src/lib/audio_source.cc deleted file mode 100644 index e61721646..000000000 --- a/src/lib/audio_source.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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 "audio_source.h" -#include "audio_sink.h" - -using boost::shared_ptr; -using boost::weak_ptr; -using boost::bind; - -static void -process_audio_proxy (weak_ptr<AudioSink> sink, shared_ptr<const AudioBuffers> audio, Time time) -{ - shared_ptr<AudioSink> p = sink.lock (); - if (p) { - p->process_audio (audio, time); - } -} - -void -AudioSource::connect_audio (shared_ptr<AudioSink> s) -{ - Audio.connect (bind (process_audio_proxy, weak_ptr<AudioSink> (s), _1, _2)); -} - - diff --git a/src/lib/audio_source.h b/src/lib/audio_source.h deleted file mode 100644 index ef47e969b..000000000 --- a/src/lib/audio_source.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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/audio_source.h - * @brief Parent class for classes which emit audio data. - */ - -#ifndef DCPOMATIC_AUDIO_SOURCE_H -#define DCPOMATIC_AUDIO_SOURCE_H - -#include <boost/signals2.hpp> -#include "types.h" - -class AudioBuffers; -class AudioSink; - -/** A class that emits audio data */ -class AudioSource -{ -public: - /** Emitted when some audio data is ready */ - boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio; - - void connect_audio (boost::shared_ptr<AudioSink>); -}; - -#endif diff --git a/src/lib/black_decoder.cc b/src/lib/black_decoder.cc deleted file mode 100644 index 0b231edd3..000000000 --- a/src/lib/black_decoder.cc +++ /dev/null @@ -1,102 +0,0 @@ -/* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> - - 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 "black_decoder.h" -#include "image.h" -#include "null_content.h" - -using boost::shared_ptr; - -BlackDecoder::BlackDecoder (shared_ptr<const Film> f, shared_ptr<NullContent> c) - : Decoder (f) - , VideoDecoder (f, c) -{ - -} - -void -BlackDecoder::pass () -{ - if (!_image) { - _image.reset (new SimpleImage (AV_PIX_FMT_RGB24, video_size(), true)); - _image->make_black (); - video (_image, false, _next_video); - } else { - video (_image, true, _next_video); - } -} - -float -BlackDecoder::video_frame_rate () const -{ - boost::shared_ptr<const Film> f = _film.lock (); - if (!f) { - return 24; - } - - return f->dcp_video_frame_rate (); -} - -ContentVideoFrame -BlackDecoder::video_length () const -{ - return _video_content->length() * video_frame_rate() / TIME_HZ; -} - -Time -BlackDecoder::position () const -{ - return _next_video; -} - -void -BlackDecoder::seek (Time t) -{ - _next_video = t; -} - -void -BlackDecoder::seek_back () -{ - boost::shared_ptr<const Film> f = _film.lock (); - if (!f) { - return; - } - - _next_video -= f->video_frames_to_time (2); -} - -void -BlackDecoder::seek_forward () -{ - boost::shared_ptr<const Film> f = _film.lock (); - if (!f) { - return; - } - - _next_video += f->video_frames_to_time (1); -} - -bool -BlackDecoder::done () const -{ - return video_done (); -} - - diff --git a/src/lib/black_decoder.h b/src/lib/black_decoder.h deleted file mode 100644 index 4591881a1..000000000 --- a/src/lib/black_decoder.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> - - 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 "video_decoder.h" - -class NullContent; - -class BlackDecoder : public VideoDecoder -{ -public: - BlackDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<NullContent>); - - /* Decoder */ - - void pass (); - void seek (Time); - void seek_back (); - void seek_forward (); - Time position () const; - bool done () const; - - /* VideoDecoder */ - - float video_frame_rate () const; - libdcp::Size video_size () const { - return libdcp::Size (256, 256); - } - ContentVideoFrame video_length () const; - -private: - boost::shared_ptr<Image> _image; -}; diff --git a/src/lib/combiner.cc b/src/lib/combiner.cc deleted file mode 100644 index 44971d135..000000000 --- a/src/lib/combiner.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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 "combiner.h" -#include "image.h" - -using boost::shared_ptr; - -Combiner::Combiner () -{ - -} - -/** Process video for the left half of the frame. - * Subtitle parameter will be ignored. - * @param image Frame image. - */ -void -Combiner::process_video (shared_ptr<const Image> image, bool, Time) -{ - _image.reset (new SimpleImage (image, true)); -} - -/** Process video for the right half of the frame. - * @param image Frame image. - * @param sub Subtitle (which will be put onto the whole frame) - */ -void -Combiner::process_video_b (shared_ptr<const Image> image, bool, Time t) -{ - /* Copy the right half of this image into our _image */ - /* XXX: this should probably be in the Image class */ - for (int i = 0; i < image->components(); ++i) { - int const line_size = image->line_size()[i]; - int const half_line_size = line_size / 2; - - uint8_t* p = _image->data()[i]; - uint8_t* q = image->data()[i]; - - for (int j = 0; j < image->lines (i); ++j) { - memcpy (p + half_line_size, q + half_line_size, half_line_size); - p += _image->stride()[i]; - q += image->stride()[i]; - } - } - - Video (_image, false, t); - _image.reset (); -} diff --git a/src/lib/combiner.h b/src/lib/combiner.h deleted file mode 100644 index 46c90b4d8..000000000 --- a/src/lib/combiner.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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/lib/combiner.h - * @brief Class for combining two video streams. - */ - -#include "video_source.h" -#include "video_sink.h" - -/** @class Combiner - * @brief A class which can combine two video streams into one, with - * one image used for the left half of the screen and the other for - * the right. - */ -class Combiner : public VideoSource, public VideoSink -{ -public: - Combiner (); - - void process_video (boost::shared_ptr<const Image> i, bool, Time); - void process_video_b (boost::shared_ptr<const Image> i, bool, Time); - -private: - /** The image that we are currently working on */ - boost::shared_ptr<Image> _image; -}; diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 391b9d19a..cfca6867f 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -29,8 +29,6 @@ #include <stdint.h> #include <boost/shared_ptr.hpp> #include <boost/signals2.hpp> -#include "video_source.h" -#include "audio_source.h" #include "film.h" class Image; @@ -54,24 +52,6 @@ public: */ virtual void pass () = 0; - /** Seek this decoder to as close as possible to some time, - * expressed relative to our source's start. - * @param t Time. - * @param a true to try hard to be accurate, otherwise false. - */ - virtual void seek (Time) = 0; - - /** Seek back one video frame */ - virtual void seek_back () = 0; - - /** Seek forward one video frame */ - virtual void seek_forward () = 0; - - /** @return Approximate time of the next content that we will emit, - * expressed relative to the start of our source. - */ - virtual Time position () const = 0; - virtual bool done () const = 0; protected: diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 8b2db0eb3..c3865d2c1 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -169,7 +169,7 @@ Encoder::frame_done () } void -Encoder::process_video (shared_ptr<const Image> image, bool same, Time) +Encoder::process_video (shared_ptr<const Image> image, bool same) { boost::mutex::scoped_lock lock (_mutex); @@ -215,7 +215,7 @@ Encoder::process_video (shared_ptr<const Image> image, bool same, Time) } void -Encoder::process_audio (shared_ptr<const AudioBuffers> data, Time) +Encoder::process_audio (shared_ptr<const AudioBuffers> data) { _writer->write (data); } diff --git a/src/lib/encoder.h b/src/lib/encoder.h index 3fe707b51..b5a641f50 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -36,8 +36,6 @@ extern "C" { #include <libswresample/swresample.h> } #include "util.h" -#include "video_sink.h" -#include "audio_sink.h" class Image; class AudioBuffers; @@ -55,7 +53,7 @@ class Job; * is supplied as uncompressed PCM in blocks of various sizes. */ -class Encoder : public VideoSink, public AudioSink +class Encoder { public: Encoder (boost::shared_ptr<const Film> f, boost::shared_ptr<Job>); @@ -68,10 +66,10 @@ public: * @param i Video frame image. * @param same true if i is the same as the last time we were called. */ - void process_video (boost::shared_ptr<const Image> i, bool same, Time); + void process_video (boost::shared_ptr<const Image> i, bool same); /** Call with some audio data */ - void process_audio (boost::shared_ptr<const AudioBuffers>, Time); + void process_audio (boost::shared_ptr<const AudioBuffers>); /** Called when a processing run has finished */ void process_end (); diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 68132c5ab..1135cc9a3 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -139,7 +139,7 @@ FFmpegContent::examine (shared_ptr<Job> job) shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ())); - ContentVideoFrame video_length = 0; + 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)); @@ -214,12 +214,12 @@ FFmpegContent::set_audio_stream (shared_ptr<FFmpegAudioStream> s) signal_changed (FFmpegContentProperty::AUDIO_STREAM); } -ContentAudioFrame +AudioContent::Frame FFmpegContent::audio_length () const { int const cafr = content_audio_frame_rate (); int const vfr = video_frame_rate (); - ContentVideoFrame const vl = video_length (); + VideoContent::Frame const vl = video_length (); boost::mutex::scoped_lock lm (_mutex); if (!_audio_stream) { @@ -296,7 +296,7 @@ FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node) frame_rate = node->number_child<int> ("FrameRate"); channels = node->number_child<int64_t> ("Channels"); mapping = AudioMapping (node->node_child ("Mapping")); - start = node->optional_number_child<Time> ("Start"); + first_audio = node->optional_number_child<Time> ("FirstAudio"); } void @@ -306,8 +306,8 @@ FFmpegAudioStream::as_xml (xmlpp::Node* root) const root->add_child("Id")->add_child_text (lexical_cast<string> (id)); root->add_child("FrameRate")->add_child_text (lexical_cast<string> (frame_rate)); root->add_child("Channels")->add_child_text (lexical_cast<string> (channels)); - if (start) { - root->add_child("Start")->add_child_text (lexical_cast<string> (start)); + if (first_audio) { + root->add_child("FirstAudio")->add_child_text (lexical_cast<string> (first_audio)); } mapping.as_xml (root->add_child("Mapping")); } diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index 36c24c2b3..c5ccee77a 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -46,7 +46,7 @@ public: int frame_rate; int channels; AudioMapping mapping; - boost::optional<Time> start; + boost::optional<double> first_audio; }; extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b); @@ -99,7 +99,7 @@ public: /* AudioContent */ int audio_channels () const; - ContentAudioFrame audio_length () const; + AudioContent::Frame audio_length () const; int content_audio_frame_rate () const; int output_audio_frame_rate () const; AudioMapping audio_mapping () const; @@ -134,6 +134,11 @@ public: void set_subtitle_stream (boost::shared_ptr<FFmpegSubtitleStream>); void set_audio_stream (boost::shared_ptr<FFmpegAudioStream>); + + boost::optional<Time> first_video () const { + boost::mutex::scoped_lock lm (_mutex); + return _first_video; + } private: std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams; diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index f1d984ee1..2d1792390 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -59,15 +59,37 @@ using libdcp::Size; FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio) : Decoder (f) - , VideoDecoder (f, c) - , AudioDecoder (f, c) + , VideoDecoder (f) + , AudioDecoder (f) , FFmpeg (c) , _subtitle_codec_context (0) , _subtitle_codec (0) , _decode_video (video) , _decode_audio (audio) + , _pts_offset (0) { setup_subtitle (); + + if (video && audio && c->audio_stream() && c->first_video() && c->audio_stream()->first_audio) { + _pts_offset = compute_pts_offset (c->first_video().get(), c->audio_stream()->first_audio.get(), c->video_frame_rate()); + } +} + +double +FFmpegDecoder::compute_pts_offset (double first_video, double first_audio, float video_frame_rate) +{ + assert (first_video >= 0); + assert (first_audio >= 0); + + double const old_first_video = first_video; + + /* Round the first video to a frame boundary */ + if (fabs (rint (first_video * video_frame_rate) - first_video * video_frame_rate) > 1e-6) { + first_video = ceil (first_video * video_frame_rate) / video_frame_rate; + } + + /* Compute the required offset (also removing any common start delay) */ + return first_video - old_first_video - min (first_video, first_audio); } FFmpegDecoder::~FFmpegDecoder () @@ -108,7 +130,8 @@ FFmpegDecoder::pass () } /* Stop us being asked for any more data */ - _next_video = _next_audio = _ffmpeg_content->length (); + _video_position = _ffmpeg_content->video_length (); + _audio_position = _ffmpeg_content->audio_length (); return; } @@ -119,6 +142,7 @@ FFmpegDecoder::pass () } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->id && _decode_audio) { decode_audio_packet (); } else if (_ffmpeg_content->subtitle_stream() && _packet.stream_index == _ffmpeg_content->subtitle_stream()->id) { +#if 0 int got_subtitle; AVSubtitle sub; @@ -138,6 +162,7 @@ FFmpegDecoder::pass () } avsubtitle_free (&sub); } +#endif } av_free_packet (&_packet); @@ -256,38 +281,25 @@ FFmpegDecoder::bytes_per_audio_sample () const } void -FFmpegDecoder::seek (Time t) +FFmpegDecoder::seek (VideoContent::Frame frame) { - do_seek (t, false, false); - VideoDecoder::seek (t); + do_seek (frame, false, false); } void FFmpegDecoder::seek_back () { - if (position() < (2.5 * TIME_HZ / _ffmpeg_content->video_frame_rate())) { - return; - } - - do_seek (position() - 2.5 * TIME_HZ / _ffmpeg_content->video_frame_rate(), true, true); - VideoDecoder::seek_back (); -} - -void -FFmpegDecoder::seek_forward () -{ - if (position() >= (_ffmpeg_content->length() - 0.5 * TIME_HZ / _ffmpeg_content->video_frame_rate())) { + if (_video_position == 0) { return; } - do_seek (position() - 0.5 * TIME_HZ / _ffmpeg_content->video_frame_rate(), true, true); - VideoDecoder::seek_forward (); + do_seek (_video_position - 1, true, true); } void -FFmpegDecoder::do_seek (Time t, bool backwards, bool accurate) +FFmpegDecoder::do_seek (VideoContent::Frame frame, bool backwards, bool accurate) { - int64_t const vt = t / (av_q2d (_format_context->streams[_video_stream]->time_base) * TIME_HZ); + int64_t const vt = frame * _ffmpeg_content->video_frame_rate() / av_q2d (_format_context->streams[_video_stream]->time_base); av_seek_frame (_format_context, _video_stream, vt, backwards ? AVSEEK_FLAG_BACKWARD : 0); avcodec_flush_buffers (video_codec_context()); @@ -337,17 +349,33 @@ FFmpegDecoder::decode_audio_packet () int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet); if (decode_result >= 0) { if (frame_finished) { - - /* Where we are in the source, in seconds */ - double const source_pts_seconds = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base) - * av_frame_get_best_effort_timestamp(_frame); + + if (_audio_position == 0) { + /* Where we are in the source, in seconds */ + double const pts = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base) + * av_frame_get_best_effort_timestamp(_frame) - _pts_offset; + + if (pts > 0) { + /* Emit some silence */ + shared_ptr<AudioBuffers> silence ( + new AudioBuffers ( + _ffmpeg_content->audio_channels(), + pts * _ffmpeg_content->content_audio_frame_rate() + ) + ); + + silence->make_silent (); + audio (silence, _audio_position); + } + } + int const data_size = av_samples_get_buffer_size ( 0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1 ); assert (audio_codec_context()->channels == _ffmpeg_content->audio_channels()); - audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds * TIME_HZ); + audio (deinterleave_audio (_frame->data, data_size), _audio_position); } copy_packet.data += decode_result; @@ -398,11 +426,33 @@ FFmpegDecoder::decode_video_packet () int64_t const bet = av_frame_get_best_effort_timestamp (_frame); if (bet != AV_NOPTS_VALUE) { - /* XXX: may need to insert extra frames / remove frames here ... - (as per old Matcher) - */ - Time const t = bet * av_q2d (_format_context->streams[_video_stream]->time_base) * TIME_HZ; - video (image, false, t); + + double const pts = bet * av_q2d (_format_context->streams[_video_stream]->time_base) - _pts_offset; + double const next = _video_position / _ffmpeg_content->video_frame_rate(); + double const one_frame = 1 / _ffmpeg_content->video_frame_rate (); + double delta = pts - next; + + while (delta > one_frame) { + /* This PTS is more than one frame forward in time of where we think we should be; emit + a black frame. + */ + boost::shared_ptr<Image> black ( + new SimpleImage ( + static_cast<AVPixelFormat> (_frame->format), + libdcp::Size (video_codec_context()->width, video_codec_context()->height), + true + ) + ); + + black->make_black (); + video (image, false, _video_position); + delta -= one_frame; + } + + if (delta > -one_frame) { + /* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */ + video (image, false, _video_position); + } } else { shared_ptr<const Film> film = _film.lock (); assert (film); @@ -413,27 +463,6 @@ FFmpegDecoder::decode_video_packet () return true; } -Time -FFmpegDecoder::position () const -{ - if (_decode_video && _decode_audio && _ffmpeg_content->audio_stream()) { - return min (_next_video, _next_audio); - } - - if (_decode_audio && _ffmpeg_content->audio_stream()) { - return _next_audio; - } - - return _next_video; -} - -bool -FFmpegDecoder::done () const -{ - bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || audio_done(); - bool const vd = !_decode_video || video_done(); - return ad && vd; -} void FFmpegDecoder::setup_subtitle () @@ -455,3 +484,12 @@ FFmpegDecoder::setup_subtitle () throw DecodeError (N_("could not open subtitle decoder")); } } + +bool +FFmpegDecoder::done () const +{ + bool const vd = !_decode_video || (_video_position >= _ffmpeg_content->video_length()); + bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || (_audio_position >= _ffmpeg_content->audio_length()); + return vd && ad; +} + diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 331d9be70..8f0482aad 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -38,6 +38,7 @@ extern "C" { #include "ffmpeg.h" class Film; +class ffmpeg_pts_offset_test; /** @class FFmpegDecoder * @brief A decoder using FFmpeg to decode content. @@ -49,23 +50,24 @@ public: ~FFmpegDecoder (); void pass (); - void seek (Time); + void seek (VideoContent::Frame); void seek_back (); - void seek_forward (); - Time position () const; bool done () const; private: + friend class ::ffmpeg_pts_offset_test; /* No copy construction */ FFmpegDecoder (FFmpegDecoder const &); FFmpegDecoder& operator= (FFmpegDecoder const &); + static double compute_pts_offset (double, double, float); + void setup_subtitle (); AVSampleFormat audio_sample_format () const; int bytes_per_audio_sample () const; - void do_seek (Time, bool, bool); + void do_seek (VideoContent::Frame, bool, bool); bool decode_video_packet (); void decode_audio_packet (); @@ -81,4 +83,6 @@ private: bool _decode_video; bool _decode_audio; + + double _pts_offset; }; diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index c09395e76..f45b0fe52 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -79,9 +79,9 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c) } } else { for (size_t i = 0; i < _audio_streams.size(); ++i) { - if (_packet.stream_index == _audio_streams[i]->id && !_audio_streams[i]->start) { + if (_packet.stream_index == _audio_streams[i]->id && !_audio_streams[i]->first_audio) { if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - _audio_streams[i]->start = frame_time (_audio_streams[i]->id); + _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->id); } } } @@ -90,7 +90,7 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c) bool have_all_audio = true; size_t i = 0; while (i < _audio_streams.size() && have_all_audio) { - have_all_audio = _audio_streams[i]->start; + have_all_audio = _audio_streams[i]->first_audio; ++i; } @@ -102,14 +102,14 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c) } } -optional<Time> +optional<double> FFmpegExaminer::frame_time (int stream) const { - optional<Time> t; + optional<double> t; int64_t const bet = av_frame_get_best_effort_timestamp (_frame); if (bet != AV_NOPTS_VALUE) { - t = bet * av_q2d (_format_context->streams[stream]->time_base) * TIME_HZ; + t = bet * av_q2d (_format_context->streams[stream]->time_base); } return t; @@ -134,7 +134,7 @@ FFmpegExaminer::video_size () const } /** @return Length (in video frames) according to our content's header */ -ContentVideoFrame +VideoContent::Frame FFmpegExaminer::video_length () const { return (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate(); diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h index 57b7775d4..ec84865ed 100644 --- a/src/lib/ffmpeg_examiner.h +++ b/src/lib/ffmpeg_examiner.h @@ -31,7 +31,7 @@ public: float video_frame_rate () const; libdcp::Size video_size () const; - ContentVideoFrame video_length () const; + VideoContent::Frame video_length () const; std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const { return _subtitle_streams; @@ -41,15 +41,15 @@ public: return _audio_streams; } - boost::optional<Time> first_video () const { + boost::optional<double> first_video () const { return _first_video; } private: std::string stream_name (AVStream* s) const; - boost::optional<Time> frame_time (int) const; + boost::optional<double> frame_time (int) const; std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams; std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams; - boost::optional<Time> _first_video; + boost::optional<double> _first_video; }; diff --git a/src/lib/image.cc b/src/lib/image.cc index 0bff1a7cc..722ff5d3c 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -43,8 +43,9 @@ extern "C" { #include "i18n.h" -using namespace std; -using namespace boost; +using std::string; +using std::min; +using boost::shared_ptr; using libdcp::Size; void diff --git a/src/lib/imagemagick_content.cc b/src/lib/imagemagick_content.cc index f9ee8cc84..2fd65ffa0 100644 --- a/src/lib/imagemagick_content.cc +++ b/src/lib/imagemagick_content.cc @@ -87,7 +87,7 @@ ImageMagickContent::clone () const } void -ImageMagickContent::set_video_length (ContentVideoFrame len) +ImageMagickContent::set_video_length (VideoContent::Frame len) { { boost::mutex::scoped_lock lm (_mutex); diff --git a/src/lib/imagemagick_content.h b/src/lib/imagemagick_content.h index d7673d870..04425af08 100644 --- a/src/lib/imagemagick_content.h +++ b/src/lib/imagemagick_content.h @@ -43,7 +43,7 @@ public: boost::shared_ptr<Content> clone () const; Time length () const; - void set_video_length (ContentVideoFrame); + void set_video_length (VideoContent::Frame); static bool valid_file (boost::filesystem::path); }; diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index c9123c77c..04d3d9df7 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -34,7 +34,7 @@ using libdcp::Size; ImageMagickDecoder::ImageMagickDecoder (shared_ptr<const Film> f, shared_ptr<const ImageMagickContent> c) : Decoder (f) - , VideoDecoder (f, c) + , VideoDecoder (f) , ImageMagick (c) { @@ -43,12 +43,12 @@ ImageMagickDecoder::ImageMagickDecoder (shared_ptr<const Film> f, shared_ptr<con void ImageMagickDecoder::pass () { - if (_next_video >= _imagemagick_content->length ()) { + if (_video_position >= _imagemagick_content->video_length ()) { return; } if (_image) { - video (_image, true, _next_video); + video (_image, true, _video_position); return; } @@ -71,48 +71,25 @@ ImageMagickDecoder::pass () delete magick_image; - _image = _image->crop (_imagemagick_content->crop(), true); - video (_image, false, _next_video); + video (_image, false, _video_position); } void -ImageMagickDecoder::seek (Time t) +ImageMagickDecoder::seek (VideoContent::Frame frame) { - _next_video = t; + _video_position = frame; } void ImageMagickDecoder::seek_back () { - boost::shared_ptr<const Film> f = _film.lock (); - if (!f) { - return; - } - - _next_video -= f->video_frames_to_time (2); -} - -void -ImageMagickDecoder::seek_forward () -{ - boost::shared_ptr<const Film> f = _film.lock (); - if (!f) { - return; + if (_video_position > 0) { + _video_position--; } - - _next_video += f->video_frames_to_time (1); } -Time -ImageMagickDecoder::position () const -{ - return _next_video; -} - - bool ImageMagickDecoder::done () const { - return video_done (); + return _video_position >= _imagemagick_content->video_length (); } - diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index e169f9bc3..286f47337 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -34,10 +34,8 @@ public: /* Decoder */ void pass (); - void seek (Time); + void seek (VideoContent::Frame); void seek_back (); - void seek_forward (); - Time position () const; bool done () const; private: diff --git a/src/lib/imagemagick_examiner.h b/src/lib/imagemagick_examiner.h index 827dad67e..801ede442 100644 --- a/src/lib/imagemagick_examiner.h +++ b/src/lib/imagemagick_examiner.h @@ -33,7 +33,7 @@ public: float video_frame_rate () const; libdcp::Size video_size () const; - ContentVideoFrame video_length () const; + VideoContent::Frame video_length () const; private: boost::weak_ptr<const Film> _film; diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc deleted file mode 100644 index 4acb82afa..000000000 --- a/src/lib/matcher.cc +++ /dev/null @@ -1,224 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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 "matcher.h" -#include "image.h" -#include "log.h" - -#include "i18n.h" - -using std::min; -using std::cout; -using std::list; -using boost::shared_ptr; - -Matcher::Matcher (shared_ptr<Log> log, int sample_rate, float frames_per_second) - : Processor (log) - , _sample_rate (sample_rate) - , _frames_per_second (frames_per_second) - , _video_frames (0) - , _audio_frames (0) - , _had_first_video (false) - , _had_first_audio (false) -{ - -} - -void -Matcher::process_video (boost::shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t) -{ - _pixel_format = image->pixel_format (); - _size = image->size (); - - _log->log(String::compose("Matcher video @ %1 [audio=%2, video=%3, pending_audio=%4]", t, _audio_frames, _video_frames, _pending_audio.size())); - - if (!_first_input || t < _first_input.get()) { - _first_input = t; - } - - bool const this_is_first_video = !_had_first_video; - _had_first_video = true; - - if (!_had_first_audio) { - /* No audio yet; we must postpone these data until we have some */ - _pending_video.push_back (VideoRecord (image, same, sub, t)); - } else if (this_is_first_video && _had_first_audio) { - /* First video since we got audio */ - _pending_video.push_back (VideoRecord (image, same, sub, t)); - fix_start (); - } else { - /* Normal running */ - - /* Difference between where this video is and where it should be */ - double const delta = t - _first_input.get() - _video_frames / _frames_per_second; - double const one_frame = 1 / _frames_per_second; - - if (delta > one_frame) { - /* Insert frames to make up the difference */ - int const extra = rint (delta / one_frame); - for (int i = 0; i < extra; ++i) { - repeat_last_video (); - _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second)); - } - } - - if (delta > -one_frame) { - Video (image, same, sub); - ++_video_frames; - } else { - /* We are omitting a frame to keep things right */ - _log->log (String::compose ("Frame removed at %1s; delta %2; first input was at %3", t, delta, _first_input.get())); - } - - _last_image = image; - _last_subtitle = sub; - } -} - -void -Matcher::process_audio (boost::shared_ptr<const AudioBuffers> b, double t) -{ - _channels = b->channels (); - - _log->log (String::compose ( - "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_video=%5, pending_audio=%6]", - b->frames(), t, _video_frames, _audio_frames, _pending_video.size(), _pending_audio.size() - ) - ); - - if (!_first_input || t < _first_input.get()) { - _first_input = t; - } - - bool const this_is_first_audio = !_had_first_audio; - _had_first_audio = true; - - if (!_had_first_video) { - /* No video yet; we must postpone these data until we have some */ - _pending_audio.push_back (AudioRecord (b, t)); - } else if (this_is_first_audio && _had_first_video) { - /* First audio since we got video */ - _pending_audio.push_back (AudioRecord (b, t)); - fix_start (); - } else { - /* Normal running. We assume audio time stamps are consecutive, so there's no equivalent of - the checking / insertion of repeat frames that there is for video. - */ - Audio (b); - _audio_frames += b->frames (); - } -} - -void -Matcher::process_end () -{ - if (_audio_frames == 0 || !_pixel_format || !_size || !_channels) { - /* We won't do anything */ - return; - } - - _log->log (String::compose ("Matcher has seen %1 video frames (which equals %2 audio frames) and %3 audio frames", - _video_frames, video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second), _audio_frames)); - - match ((double (_audio_frames) / _sample_rate) - (double (_video_frames) / _frames_per_second)); -} - -void -Matcher::fix_start () -{ - assert (!_pending_video.empty ()); - assert (!_pending_audio.empty ()); - - _log->log (String::compose ("Fixing start; video at %1, audio at %2", _pending_video.front().time, _pending_audio.front().time)); - - match (_pending_video.front().time - _pending_audio.front().time); - - for (list<VideoRecord>::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) { - process_video (i->image, i->same, i->subtitle, i->time); - } - - _pending_video.clear (); - - for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { - process_audio (i->audio, i->time); - } - - _pending_audio.clear (); -} - -void -Matcher::match (double extra_video_needed) -{ - _log->log (String::compose ("Match %1", extra_video_needed)); - - if (extra_video_needed > 0) { - - /* Emit black video frames */ - - int const black_video_frames = ceil (extra_video_needed * _frames_per_second); - - _log->log (String::compose (N_("Emitting %1 frames of black video"), black_video_frames)); - - shared_ptr<Image> black (new SimpleImage (_pixel_format.get(), _size.get(), true)); - black->make_black (); - for (int i = 0; i < black_video_frames; ++i) { - Video (black, i != 0, shared_ptr<Subtitle>()); - ++_video_frames; - } - - extra_video_needed -= black_video_frames / _frames_per_second; - } - - if (extra_video_needed < 0) { - - /* Emit silence */ - - int64_t to_do = -extra_video_needed * _sample_rate; - _log->log (String::compose (N_("Emitting %1 frames of silence"), to_do)); - - /* Do things in half second blocks as I think there may be limits - to what FFmpeg (and in particular the resampler) can cope with. - */ - int64_t const block = _sample_rate / 2; - shared_ptr<AudioBuffers> b (new AudioBuffers (_channels.get(), block)); - b->make_silent (); - - while (to_do > 0) { - int64_t const this_time = min (to_do, block); - b->set_frames (this_time); - Audio (b); - _audio_frames += b->frames (); - to_do -= this_time; - } - } -} - -void -Matcher::repeat_last_video () -{ - if (!_last_image) { - shared_ptr<Image> im (new SimpleImage (_pixel_format.get(), _size.get(), true)); - im->make_black (); - _last_image = im; - } - - Video (_last_image, true, _last_subtitle); - ++_video_frames; -} - diff --git a/src/lib/matcher.h b/src/lib/matcher.h deleted file mode 100644 index 61fd81436..000000000 --- a/src/lib/matcher.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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 <boost/optional.hpp> -#include "processor.h" - -class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, public AudioSource, public VideoSource -{ -public: - Matcher (boost::shared_ptr<Log> log, int sample_rate, float frames_per_second); - void process_video (boost::shared_ptr<const Image> i, bool, boost::shared_ptr<Subtitle> s, double); - void process_audio (boost::shared_ptr<const AudioBuffers>, double); - void process_end (); - -private: - void fix_start (); - void match (double); - void repeat_last_video (); - - int _sample_rate; - float _frames_per_second; - int _video_frames; - int64_t _audio_frames; - boost::optional<AVPixelFormat> _pixel_format; - boost::optional<libdcp::Size> _size; - boost::optional<int> _channels; - - struct VideoRecord { - VideoRecord (boost::shared_ptr<const Image> i, bool s, boost::shared_ptr<Subtitle> u, double t) - : image (i) - , same (s) - , subtitle (u) - , time (t) - {} - - boost::shared_ptr<const Image> image; - bool same; - boost::shared_ptr<Subtitle> subtitle; - double time; - }; - - struct AudioRecord { - AudioRecord (boost::shared_ptr<const AudioBuffers> a, double t) - : audio (a) - , time (t) - {} - - boost::shared_ptr<const AudioBuffers> audio; - double time; - }; - - std::list<VideoRecord> _pending_video; - std::list<AudioRecord> _pending_audio; - - boost::optional<double> _first_input; - boost::shared_ptr<const Image> _last_image; - boost::shared_ptr<Subtitle> _last_subtitle; - - bool _had_first_video; - bool _had_first_audio; -}; diff --git a/src/lib/null_content.cc b/src/lib/null_content.cc deleted file mode 100644 index 3bbda92da..000000000 --- a/src/lib/null_content.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> - - 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 "null_content.h" -#include "film.h" - -using boost::shared_ptr; - -NullContent::NullContent (shared_ptr<const Film> f, Time s, Time len) - : Content (f, s) - , VideoContent (f, s, f->time_to_video_frames (len)) - , AudioContent (f, s) - , _audio_length (f->time_to_audio_frames (len)) - , _length (len) -{ - -} - -int -NullContent::content_audio_frame_rate () const -{ - return output_audio_frame_rate (); -} - - -int -NullContent::output_audio_frame_rate () const -{ - shared_ptr<const Film> film = _film.lock (); - assert (film); - return film->dcp_audio_frame_rate (); -} - -int -NullContent::audio_channels () const -{ - shared_ptr<const Film> film = _film.lock (); - assert (film); - return film->dcp_audio_channels (); -} - diff --git a/src/lib/null_content.h b/src/lib/null_content.h deleted file mode 100644 index 889ff7a0d..000000000 --- a/src/lib/null_content.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> - - 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 <string> -#include <boost/shared_ptr.hpp> -#include "video_content.h" -#include "audio_content.h" - -class NullContent : public VideoContent, public AudioContent -{ -public: - NullContent (boost::shared_ptr<const Film>, Time, Time); - - std::string summary () const { - return ""; - } - - std::string information () const { - return ""; - } - - void as_xml (xmlpp::Node *) const {} - - boost::shared_ptr<Content> clone () const { - return boost::shared_ptr<Content> (); - } - - int audio_channels () const; - - ContentAudioFrame audio_length () const { - return _audio_length; - } - - int content_audio_frame_rate () const; - - int output_audio_frame_rate () const; - - AudioMapping audio_mapping () const { - return AudioMapping (); - } - - void set_audio_mapping (AudioMapping) {} - - Time length () const { - return _length; - } - -private: - ContentAudioFrame _audio_length; - Time _length; -}; diff --git a/src/lib/player.cc b/src/lib/player.cc index cd1c54d5b..9969fbf9e 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -29,43 +29,48 @@ #include "playlist.h" #include "job.h" #include "image.h" -#include "null_content.h" -#include "black_decoder.h" -#include "silence_decoder.h" +#include "ratio.h" +#include "resampler.h" using std::list; using std::cout; using std::min; using std::max; using std::vector; +using std::pair; +using std::map; using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; #define DEBUG_PLAYER 1 -struct Piece +class Piece { +public: + Piece (shared_ptr<Content> c) + : content (c) + , video_position (c->start ()) + , audio_position (c->start ()) + {} + Piece (shared_ptr<Content> c, shared_ptr<Decoder> d) : content (c) , decoder (d) + , video_position (c->start ()) + , audio_position (c->start ()) {} shared_ptr<Content> content; shared_ptr<Decoder> decoder; + Time video_position; + Time audio_position; }; - #ifdef DEBUG_PLAYER std::ostream& operator<<(std::ostream& s, Piece const & p) { - if (dynamic_pointer_cast<NullContent> (p.content)) { - if (dynamic_pointer_cast<SilenceDecoder> (p.decoder)) { - s << "\tsilence "; - } else { - s << "\tblack "; - } - } else if (dynamic_pointer_cast<FFmpegContent> (p.content)) { + if (dynamic_pointer_cast<FFmpegContent> (p.content)) { s << "\tffmpeg "; } else if (dynamic_pointer_cast<ImageMagickContent> (p.content)) { s << "\timagemagick"; @@ -85,12 +90,13 @@ Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p) , _video (true) , _audio (true) , _have_valid_pieces (false) - , _position (0) + , _video_position (0) + , _audio_position (0) , _audio_buffers (f->dcp_audio_channels(), 0) - , _next_audio (0) { _playlist->Changed.connect (bind (&Player::playlist_changed, this)); _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2)); + set_video_container_size (_film->container()->size (_film->full_frame ())); } void @@ -113,77 +119,186 @@ Player::pass () _have_valid_pieces = true; } - /* Here we are just finding the active decoder with the earliest last emission time, then - calling pass on it. - */ +#ifdef DEBUG_PLAYER + cout << "= PASS\n"; +#endif Time earliest_t = TIME_MAX; shared_ptr<Piece> earliest; + enum { + VIDEO, + AUDIO + } type = VIDEO; for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { if ((*i)->decoder->done ()) { continue; } - if (!_audio && dynamic_pointer_cast<AudioDecoder> ((*i)->decoder) && !dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) { - continue; + if (dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) { + if ((*i)->video_position < earliest_t) { + earliest_t = (*i)->video_position; + earliest = *i; + type = VIDEO; + } } - - Time const t = (*i)->content->start() + (*i)->decoder->position(); - if (t < earliest_t) { - earliest_t = t; - earliest = *i; + + if (dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) { + if ((*i)->audio_position < earliest_t) { + earliest_t = (*i)->audio_position; + earliest = *i; + type = AUDIO; + } } } if (!earliest) { +#ifdef DEBUG_PLAYER + cout << "no earliest piece.\n"; +#endif + flush (); return true; } - earliest->decoder->pass (); - _position = earliest->content->start() + earliest->decoder->position (); + switch (type) { + case VIDEO: + if (earliest_t > _video_position) { +#ifdef DEBUG_PLAYER + cout << "no video here; emitting black frame.\n"; +#endif + emit_black (); + } else { +#ifdef DEBUG_PLAYER + cout << "Pass " << *earliest << "\n"; +#endif + earliest->decoder->pass (); + } + break; + + case AUDIO: + if (earliest_t > _audio_position) { +#ifdef DEBUG_PLAYER + cout << "no audio here; emitting silence.\n"; +#endif + emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position)); + } else { +#ifdef DEBUG_PLAYER + cout << "Pass " << *earliest << "\n"; +#endif + earliest->decoder->pass (); + } + break; + } + +#ifdef DEBUG_PLAYER + cout << "\tpost pass " << _video_position << " " << _audio_position << "\n"; +#endif return false; } void -Player::process_video (weak_ptr<Content> weak_content, shared_ptr<const Image> image, bool same, Time time) +Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, bool same, VideoContent::Frame frame) { - shared_ptr<Content> content = weak_content.lock (); - if (!content) { + shared_ptr<Piece> piece = weak_piece.lock (); + if (!piece) { return; } + + shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content); + assert (content); + + FrameRateConversion frc (content->video_frame_rate(), _film->dcp_video_frame_rate()); + if (frc.skip && (frame % 2) == 1) { + return; + } + + image = image->crop (content->crop(), true); + + libdcp::Size const image_size = content->ratio()->size (_video_container_size); - time += content->start (); + image = image->scale_and_convert_to_rgb (image_size, _film->scaler(), true); + +#if 0 + if (film->with_subtitles ()) { + shared_ptr<Subtitle> sub; + if (_timed_subtitle && _timed_subtitle->displayed_at (t)) { + sub = _timed_subtitle->subtitle (); + } + + if (sub) { + dcpomatic::Rect const tx = subtitle_transformed_area ( + float (image_size.width) / content->video_size().width, + float (image_size.height) / content->video_size().height, + sub->area(), film->subtitle_offset(), film->subtitle_scale() + ); + + shared_ptr<Image> im = sub->image()->scale (tx.size(), film->scaler(), true); + image->alpha_blend (im, tx.position()); + } + } +#endif + + if (image_size != _video_container_size) { + assert (image_size.width <= _video_container_size.width); + assert (image_size.height <= _video_container_size.height); + shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true)); + im->make_black (); + im->copy (image, Position ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2)); + image = im; + } + + Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate()); Video (image, same, time); + time += TIME_HZ / _film->dcp_video_frame_rate(); + + if (frc.repeat) { + Video (image, true, time); + time += TIME_HZ / _film->dcp_video_frame_rate(); + } + + _video_position = piece->video_position = time; } void -Player::process_audio (weak_ptr<Content> weak_content, shared_ptr<const AudioBuffers> audio, Time time) +Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame) { - shared_ptr<Content> content = weak_content.lock (); - if (!content) { + shared_ptr<Piece> piece = weak_piece.lock (); + if (!piece) { return; } - + + shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content); + assert (content); + + if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) { + audio = resampler(content)->run (audio); + } + + /* Remap channels */ + shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->dcp_audio_channels(), audio->frames())); + dcp_mapped->make_silent (); + list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp (); + for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) { + dcp_mapped->accumulate_channel (audio.get(), i->first, i->second); + } + /* The time of this audio may indicate that some of our buffered audio is not going to be added to any more, so it can be emitted. */ - time += content->start (); + Time const time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate()); - cout << "Player gets " << audio->frames() << " @ " << time << " cf " << _next_audio << "\n"; - - if (time > _next_audio) { + if (time > _audio_position) { /* We can emit some audio from our buffers */ - OutputAudioFrame const N = _film->time_to_audio_frames (time - _next_audio); + OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position); assert (N <= _audio_buffers.frames()); shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N)); emit->copy_from (&_audio_buffers, N, 0, 0); - Audio (emit, _next_audio); - _next_audio += _film->audio_frames_to_time (N); + Audio (emit, _audio_position); + _audio_position = piece->audio_position = time + _film->audio_frames_to_time (N); /* And remove it from our buffers */ if (_audio_buffers.frames() > N) { @@ -204,10 +319,19 @@ Player::flush () if (_audio_buffers.frames() > 0) { shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames())); emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0); - Audio (emit, _next_audio); - _next_audio += _film->audio_frames_to_time (_audio_buffers.frames ()); + Audio (emit, _audio_position); + _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ()); _audio_buffers.set_frames (0); } + + while (_video_position < _audio_position) { + emit_black (); + } + + while (_audio_position < _video_position) { + emit_silence (_film->time_to_audio_frames (_video_position - _audio_position)); + } + } /** @return true on error */ @@ -224,10 +348,18 @@ Player::seek (Time t) } for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { - Time s = t - (*i)->content->start (); + shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content); + if (!vc) { + continue; + } + + Time s = t - vc->start (); s = max (static_cast<Time> (0), s); - s = min ((*i)->content->length(), s); - (*i)->decoder->seek (s); + s = min (vc->length(), s); + + FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate()); + VideoContent::Frame f = s * _film->dcp_video_frame_rate() / (frc.factor() * TIME_HZ); + dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f); } /* XXX: don't seek audio because we don't need to... */ @@ -241,32 +373,6 @@ Player::seek_back () } void -Player::seek_forward () -{ - -} - -void -Player::add_black_piece (Time s, Time len) -{ - shared_ptr<NullContent> nc (new NullContent (_film, s, len)); - nc->set_ratio (_film->container ()); - shared_ptr<BlackDecoder> bd (new BlackDecoder (_film, nc)); - bd->Video.connect (bind (&Player::process_video, this, nc, _1, _2, _3)); - _pieces.push_back (shared_ptr<Piece> (new Piece (nc, bd))); -} - -void -Player::add_silent_piece (Time s, Time len) -{ - shared_ptr<NullContent> nc (new NullContent (_film, s, len)); - shared_ptr<SilenceDecoder> sd (new SilenceDecoder (_film, nc)); - sd->Audio.connect (bind (&Player::process_audio, this, nc, _1, _2)); - _pieces.push_back (shared_ptr<Piece> (new Piece (nc, sd))); -} - - -void Player::setup_pieces () { list<shared_ptr<Piece> > old_pieces = _pieces; @@ -278,21 +384,18 @@ Player::setup_pieces () for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) { - shared_ptr<Decoder> decoder; - + shared_ptr<Piece> piece (new Piece (*i)); + /* XXX: into content? */ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i); if (fc) { shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio)); - fd->Video.connect (bind (&Player::process_video, this, *i, _1, _2, _3)); - fd->Audio.connect (bind (&Player::process_audio, this, *i, _1, _2)); - if (_video_container_size) { - fd->set_video_container_size (_video_container_size.get ()); - } + fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3)); + fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2)); - decoder = fd; + piece->decoder = fd; } shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i); @@ -309,54 +412,21 @@ Player::setup_pieces () if (!id) { id.reset (new ImageMagickDecoder (_film, ic)); - id->Video.connect (bind (&Player::process_video, this, *i, _1, _2, _3)); - if (_video_container_size) { - id->set_video_container_size (_video_container_size.get ()); - } + id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3)); } - decoder = id; + piece->decoder = id; } shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i); if (sc) { shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc)); - sd->Audio.connect (bind (&Player::process_audio, this, *i, _1, _2)); + sd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2)); - decoder = sd; + piece->decoder = sd; } - _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder))); - } - - /* Fill in visual gaps with black and audio gaps with silence */ - - Time video_pos = 0; - Time audio_pos = 0; - list<shared_ptr<Piece> > pieces_copy = _pieces; - for (list<shared_ptr<Piece> >::iterator i = pieces_copy.begin(); i != pieces_copy.end(); ++i) { - if (dynamic_pointer_cast<VideoContent> ((*i)->content)) { - Time const diff = (*i)->content->start() - video_pos; - if (diff > 0) { - add_black_piece (video_pos, diff); - } - video_pos = (*i)->content->end(); - } - - shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> ((*i)->content); - if (ac && ac->audio_channels()) { - Time const diff = (*i)->content->start() - audio_pos; - if (diff > 0) { - add_silent_piece (video_pos, diff); - } - audio_pos = (*i)->content->end(); - } - } - - if (video_pos < audio_pos) { - add_black_piece (video_pos, audio_pos - video_pos); - } else if (audio_pos < video_pos) { - add_silent_piece (audio_pos, video_pos - audio_pos); + _pieces.push_back (piece); } #ifdef DEBUG_PLAYER @@ -390,10 +460,40 @@ void Player::set_video_container_size (libdcp::Size s) { _video_container_size = s; - for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { - shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> ((*i)->decoder); - if (vd) { - vd->set_video_container_size (s); - } + _black_frame.reset (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true)); + _black_frame->make_black (); +} + +shared_ptr<Resampler> +Player::resampler (shared_ptr<AudioContent> c) +{ + map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c); + if (i != _resamplers.end ()) { + return i->second; } + + shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels())); + _resamplers[c] = r; + return r; } + +void +Player::emit_black () +{ + /* XXX: use same here */ + Video (_black_frame, false, _video_position); + _video_position += _film->video_frames_to_time (1); +} + +void +Player::emit_silence (OutputAudioFrame most) +{ + OutputAudioFrame N = min (most, _film->dcp_audio_frame_rate() / 2); + shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->dcp_audio_channels(), N)); + silence->make_silent (); + Audio (silence, _audio_position); + _audio_position += _film->audio_frames_to_time (N); +} + + + diff --git a/src/lib/player.h b/src/lib/player.h index e4fb83220..bbdb14ec2 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -23,24 +23,23 @@ #include <list> #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> -#include "video_source.h" -#include "audio_source.h" -#include "video_sink.h" -#include "audio_sink.h" #include "playlist.h" #include "audio_buffers.h" +#include "content.h" class Job; class Film; class Playlist; class AudioContent; class Piece; +class Image; +class Resampler; /** @class Player * @brief A class which can `play' a Playlist; emitting its audio and video. */ -class Player : public VideoSource, public AudioSource, public boost::enable_shared_from_this<Player> +class Player : public boost::enable_shared_from_this<Player> { public: Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist>); @@ -51,26 +50,35 @@ public: bool pass (); void seek (Time); void seek_back (); - void seek_forward (); - /** @return position that we are at; ie the time of the next thing we will emit on pass() */ - Time position () const { - return _position; + Time video_position () const { + return _video_position; } void set_video_container_size (libdcp::Size); + /** Emitted when a video frame is ready. + * First parameter is the video image. + * Second parameter is true if the image is the same as the last one that was emitted. + * Third parameter is the time. + */ + boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, Time)> Video; + + /** Emitted when some audio data is ready */ + boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio; + private: - void process_video (boost::weak_ptr<Content>, boost::shared_ptr<const Image>, bool, Time); - void process_audio (boost::weak_ptr<Content>, boost::shared_ptr<const AudioBuffers>, Time); + void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, bool, VideoContent::Frame); + void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame); void setup_pieces (); void playlist_changed (); void content_changed (boost::weak_ptr<Content>, int); void do_seek (Time, bool); - void add_black_piece (Time, Time); - void add_silent_piece (Time, Time); void flush (); + void emit_black (); + void emit_silence (OutputAudioFrame); + boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>); boost::shared_ptr<const Film> _film; boost::shared_ptr<const Playlist> _playlist; @@ -81,10 +89,17 @@ private: /** Our pieces are ready to go; if this is false the pieces must be (re-)created before they are used */ bool _have_valid_pieces; std::list<boost::shared_ptr<Piece> > _pieces; - Time _position; + + /** The time after the last video that we emitted */ + Time _video_position; + /** The time after the last audio that we emitted */ + Time _audio_position; + AudioBuffers _audio_buffers; - Time _next_audio; - boost::optional<libdcp::Size> _video_container_size; + + libdcp::Size _video_container_size; + boost::shared_ptr<Image> _black_frame; + std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers; }; #endif diff --git a/src/lib/playlist.h b/src/lib/playlist.h index e4f4c79f0..2d243fe8f 100644 --- a/src/lib/playlist.h +++ b/src/lib/playlist.h @@ -23,10 +23,6 @@ #include <list> #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> -#include "video_source.h" -#include "audio_source.h" -#include "video_sink.h" -#include "audio_sink.h" #include "ffmpeg_content.h" #include "audio_mapping.h" diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc new file mode 100644 index 000000000..1235b9038 --- /dev/null +++ b/src/lib/resampler.cc @@ -0,0 +1,61 @@ +extern "C" { +#include "libavutil/channel_layout.h" +} +#include "resampler.h" +#include "audio_buffers.h" +#include "exceptions.h" + +#include "i18n.h" + +using boost::shared_ptr; + +Resampler::Resampler (int in, int out, int channels) + : _in_rate (in) + , _out_rate (out) + , _channels (channels) +{ + /* We will be using planar float data when we call the + resampler. As far as I can see, the audio channel + layout is not necessary for our purposes; it seems + only to be used get the number of channels and + decide if rematrixing is needed. It won't be, since + input and output layouts are the same. + */ + + _swr_context = swr_alloc_set_opts ( + 0, + av_get_default_channel_layout (_channels), + AV_SAMPLE_FMT_FLTP, + _out_rate, + av_get_default_channel_layout (_channels), + AV_SAMPLE_FMT_FLTP, + _in_rate, + 0, 0 + ); + + swr_init (_swr_context); +} + +Resampler::~Resampler () +{ + swr_free (&_swr_context); +} + +shared_ptr<const AudioBuffers> +Resampler::run (shared_ptr<const AudioBuffers> in) +{ + /* Compute the resampled frames count and add 32 for luck */ + int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32; + shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames)); + + int const resampled_frames = swr_convert ( + _swr_context, (uint8_t **) resampled->data(), max_resampled_frames, (uint8_t const **) in->data(), in->frames() + ); + + if (resampled_frames < 0) { + throw EncodeError (_("could not run sample-rate converter")); + } + + resampled->set_frames (resampled_frames); + return resampled; +} diff --git a/src/lib/resampler.h b/src/lib/resampler.h new file mode 100644 index 000000000..cda718934 --- /dev/null +++ b/src/lib/resampler.h @@ -0,0 +1,21 @@ +#include <boost/shared_ptr.hpp> +extern "C" { +#include <libswresample/swresample.h> +} + +class AudioBuffers; + +class Resampler +{ +public: + Resampler (int, int, int); + ~Resampler (); + + boost::shared_ptr<const AudioBuffers> run (boost::shared_ptr<const AudioBuffers>); + +private: + SwrContext* _swr_context; + int _in_rate; + int _out_rate; + int _channels; +}; diff --git a/src/lib/silence_decoder.cc b/src/lib/silence_decoder.cc deleted file mode 100644 index 0380117ce..000000000 --- a/src/lib/silence_decoder.cc +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> - - 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 "silence_decoder.h" -#include "null_content.h" -#include "audio_buffers.h" - -using std::min; -using std::cout; -using boost::shared_ptr; - -SilenceDecoder::SilenceDecoder (shared_ptr<const Film> f, shared_ptr<NullContent> c) - : Decoder (f) - , AudioDecoder (f, c) -{ - -} - -void -SilenceDecoder::pass () -{ - shared_ptr<const Film> film = _film.lock (); - assert (film); - - Time const this_time = min (_audio_content->length() - _next_audio, TIME_HZ / 2); - cout << "silence emit " << this_time << " from " << _audio_content->length() << "\n"; - shared_ptr<AudioBuffers> data (new AudioBuffers (film->dcp_audio_channels(), film->time_to_audio_frames (this_time))); - data->make_silent (); - audio (data, _next_audio); -} - -void -SilenceDecoder::seek (Time t) -{ - _next_audio = t; -} - -void -SilenceDecoder::seek_back () -{ - boost::shared_ptr<const Film> f = _film.lock (); - if (!f) { - return; - } - - _next_audio -= f->video_frames_to_time (2); -} - -void -SilenceDecoder::seek_forward () -{ - boost::shared_ptr<const Film> f = _film.lock (); - if (!f) { - return; - } - - _next_audio += f->video_frames_to_time (1); -} - -Time -SilenceDecoder::position () const -{ - return _next_audio; -} - -bool -SilenceDecoder::done () const -{ - return audio_done (); -} - diff --git a/src/lib/silence_decoder.h b/src/lib/silence_decoder.h deleted file mode 100644 index a8a335d39..000000000 --- a/src/lib/silence_decoder.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> - - 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 <boost/shared_ptr.hpp> -#include "audio_decoder.h" - -class Film; -class NullContent; - -class SilenceDecoder : public AudioDecoder -{ -public: - SilenceDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<NullContent>); - - void pass (); - void seek (Time); - void seek_back (); - void seek_forward (); - Time position () const; - bool done () const; -}; diff --git a/src/lib/sndfile_content.cc b/src/lib/sndfile_content.cc index 8eede89f4..beee7cd9d 100644 --- a/src/lib/sndfile_content.cc +++ b/src/lib/sndfile_content.cc @@ -46,7 +46,7 @@ SndfileContent::SndfileContent (shared_ptr<const Film> f, shared_ptr<const cxml: , AudioContent (f, node) { _audio_channels = node->number_child<int> ("AudioChannels"); - _audio_length = node->number_child<ContentAudioFrame> ("AudioLength"); + _audio_length = node->number_child<AudioContent::Frame> ("AudioLength"); _audio_frame_rate = node->number_child<int> ("AudioFrameRate"); _audio_mapping = AudioMapping (node->node_child ("AudioMapping")); } diff --git a/src/lib/sndfile_content.h b/src/lib/sndfile_content.h index 30eb23a4e..876d66088 100644 --- a/src/lib/sndfile_content.h +++ b/src/lib/sndfile_content.h @@ -49,7 +49,7 @@ public: return _audio_channels; } - ContentAudioFrame audio_length () const { + AudioContent::Frame audio_length () const { boost::mutex::scoped_lock lm (_mutex); return _audio_length; } @@ -72,7 +72,7 @@ public: private: int _audio_channels; - ContentAudioFrame _audio_length; + AudioContent::Frame _audio_length; int _audio_frame_rate; AudioMapping _audio_mapping; }; diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index ff56f1062..80a6afd2b 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -35,7 +35,7 @@ using boost::shared_ptr; SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> c) : Decoder (f) - , AudioDecoder (f, c) + , AudioDecoder (f) , _sndfile_content (c) , _deinterleave_buffer (0) { @@ -100,7 +100,7 @@ SndfileDecoder::audio_channels () const return _info.channels; } -ContentAudioFrame +AudioContent::Frame SndfileDecoder::audio_length () const { return _info.frames; @@ -112,14 +112,8 @@ SndfileDecoder::audio_frame_rate () const return _info.samplerate; } -Time -SndfileDecoder::position () const -{ - return _next_audio; -} - bool SndfileDecoder::done () const { - return audio_done (); + return _audio_position >= _sndfile_content->audio_length (); } diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h index e904340b3..77fa6d177 100644 --- a/src/lib/sndfile_decoder.h +++ b/src/lib/sndfile_decoder.h @@ -30,21 +30,17 @@ public: ~SndfileDecoder (); void pass (); - void seek (Time) {} - void seek_back () {} - void seek_forward () {} - Time position () const; bool done () const; int audio_channels () const; - ContentAudioFrame audio_length () const; + AudioContent::Frame audio_length () const; int audio_frame_rate () const; private: boost::shared_ptr<const SndfileContent> _sndfile_content; SNDFILE* _sndfile; SF_INFO _info; - ContentAudioFrame _done; - ContentAudioFrame _remaining; + AudioContent::Frame _done; + AudioContent::Frame _remaining; float* _deinterleave_buffer; }; diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc index 5e719f977..7013f1d7d 100644 --- a/src/lib/subtitle.cc +++ b/src/lib/subtitle.cc @@ -27,8 +27,7 @@ #include "i18n.h" -using namespace std; -using namespace boost; +using boost::shared_ptr; using libdcp::Size; /** Construct a TimedSubtitle. This is a subtitle image, position, diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index f4637a05c..f4a52639a 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -36,8 +36,27 @@ using std::string; using boost::shared_ptr; +using boost::weak_ptr; using boost::dynamic_pointer_cast; +static void +video_proxy (weak_ptr<Encoder> encoder, shared_ptr<const Image> image, bool same) +{ + shared_ptr<Encoder> e = encoder.lock (); + if (e) { + e->process_video (image, same); + } +} + +static void +audio_proxy (weak_ptr<Encoder> encoder, shared_ptr<const AudioBuffers> audio) +{ + shared_ptr<Encoder> e = encoder.lock (); + if (e) { + e->process_audio (audio); + } +} + /** Construct a transcoder using a Decoder that we create and a supplied Encoder. * @param f Film that we are transcoding. * @param j Job that we are running under, or 0. @@ -48,8 +67,8 @@ Transcoder::Transcoder (shared_ptr<const Film> f, shared_ptr<Job> j) , _player (f->player ()) , _encoder (new Encoder (f, j)) { - _player->connect_video (_encoder); - _player->connect_audio (_encoder); + _player->Video.connect (bind (video_proxy, _encoder, _1, _2)); + _player->Audio.connect (bind (audio_proxy, _encoder, _1)); } void diff --git a/src/lib/types.h b/src/lib/types.h index 70262afb0..33f8239d8 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -27,8 +27,6 @@ class Content; -typedef int64_t ContentAudioFrame; -typedef int ContentVideoFrame; typedef int64_t Time; #define TIME_MAX INT64_MAX #define TIME_HZ ((Time) 96000) diff --git a/src/lib/util.cc b/src/lib/util.cc index eda0d0236..d425fc8fe 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -710,7 +710,7 @@ ensure_ui_thread () * @return Equivalent number of audio frames for `v'. */ int64_t -video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second) +video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second) { return ((int64_t) v * audio_sample_rate / frames_per_second); } diff --git a/src/lib/util.h b/src/lib/util.h index 42514a12c..c68bb4f16 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -38,6 +38,7 @@ extern "C" { } #include "compose.hpp" #include "types.h" +#include "video_content.h" #ifdef DCPOMATIC_DEBUG #define TIMING(...) _film->log()->microsecond_log (String::compose (__VA_ARGS__), Log::TIMING); @@ -152,7 +153,7 @@ private: int _timeout; }; -extern int64_t video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second); +extern int64_t video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second); class LocaleGuard { diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index f9de6aa21..3818fa792 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -22,6 +22,7 @@ #include "video_content.h" #include "video_examiner.h" #include "ratio.h" +#include "compose.hpp" #include "i18n.h" @@ -37,7 +38,7 @@ using boost::shared_ptr; using boost::lexical_cast; using boost::optional; -VideoContent::VideoContent (shared_ptr<const Film> f, Time s, ContentVideoFrame len) +VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len) : Content (f, s) , _video_length (len) , _video_frame_rate (0) @@ -58,7 +59,7 @@ VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p) VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node) : Content (f, node) { - _video_length = node->number_child<ContentVideoFrame> ("VideoLength"); + _video_length = node->number_child<VideoContent::Frame> ("VideoLength"); _video_size.width = node->number_child<int> ("VideoWidth"); _video_size.height = node->number_child<int> ("VideoHeight"); _video_frame_rate = node->number_child<float> ("VideoFrameRate"); diff --git a/src/lib/video_content.h b/src/lib/video_content.h index 23bcaa89b..372cab3bd 100644 --- a/src/lib/video_content.h +++ b/src/lib/video_content.h @@ -21,7 +21,6 @@ #define DCPOMATIC_VIDEO_CONTENT_H #include "content.h" -#include "util.h" class VideoExaminer; class Ratio; @@ -38,7 +37,9 @@ public: class VideoContent : public virtual Content { public: - VideoContent (boost::shared_ptr<const Film>, Time, ContentVideoFrame); + typedef int Frame; + + VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame); VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path); VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); VideoContent (VideoContent const &); @@ -46,7 +47,7 @@ public: void as_xml (xmlpp::Node *) const; virtual std::string information () const; - ContentVideoFrame video_length () const { + VideoContent::Frame video_length () const { boost::mutex::scoped_lock lm (_mutex); return _video_length; } @@ -82,7 +83,7 @@ public: protected: void take_from_video_examiner (boost::shared_ptr<VideoExaminer>); - ContentVideoFrame _video_length; + VideoContent::Frame _video_length; private: libdcp::Size _video_size; diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index fcc0ccf41..f61e63d4d 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -28,78 +28,22 @@ using std::cout; using boost::shared_ptr; -VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoContent> c) +VideoDecoder::VideoDecoder (shared_ptr<const Film> f) : Decoder (f) - , _next_video (0) - , _video_content (c) - , _frame_rate_conversion (c->video_frame_rate(), f->dcp_video_frame_rate()) - , _odd (false) + , _video_position (0) { } -/** Called by subclasses when some video is ready. - * @param image frame to emit. - * @param same true if this frame is the same as the last one passed to this call. - * @param t Time of the frame within the source. - */ void -VideoDecoder::video (shared_ptr<Image> image, bool same, Time t) +VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoContent::Frame frame) { - if (_frame_rate_conversion.skip && _odd) { - _odd = !_odd; - return; - } - - image = image->crop (_video_content->crop(), true); - - shared_ptr<const Film> film = _film.lock (); - assert (film); - - libdcp::Size const container_size = _video_container_size.get_value_or (film->container()->size (film->full_frame ())); - libdcp::Size const image_size = _video_content->ratio()->size (container_size); - - shared_ptr<Image> out = image->scale_and_convert_to_rgb (image_size, film->scaler(), true); - - if (film->with_subtitles ()) { - shared_ptr<Subtitle> sub; - if (_timed_subtitle && _timed_subtitle->displayed_at (t)) { - sub = _timed_subtitle->subtitle (); - } - - if (sub) { - dcpomatic::Rect const tx = subtitle_transformed_area ( - float (image_size.width) / _video_content->video_size().width, - float (image_size.height) / _video_content->video_size().height, - sub->area(), film->subtitle_offset(), film->subtitle_scale() - ); - - shared_ptr<Image> im = sub->image()->scale (tx.size(), film->scaler(), true); - out->alpha_blend (im, tx.position()); - } - } - - if (image_size != container_size) { - assert (image_size.width <= container_size.width); - assert (image_size.height <= container_size.height); - shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, container_size, true)); - im->make_black (); - im->copy (out, Position ((container_size.width - image_size.width) / 2, (container_size.height - image_size.height) / 2)); - out = im; - } - - Video (out, same, t); - - if (_frame_rate_conversion.repeat) { - Video (image, true, t + film->video_frames_to_time (1)); - _next_video = t + film->video_frames_to_time (2); - } else { - _next_video = t + film->video_frames_to_time (1); - } - - _odd = !_odd; + Video (image, same, frame); + _video_position = frame + 1; } +#if 0 + /** Called by subclasses when a subtitle is ready. * s may be 0 to say that there is no current subtitle. * @param s New current subtitle, or 0. @@ -114,40 +58,5 @@ VideoDecoder::subtitle (shared_ptr<TimedSubtitle> s) _timed_subtitle->subtitle()->set_position (Position (p.x - _video_content->crop().left, p.y - _video_content->crop().top)); } } +#endif -bool -VideoDecoder::video_done () const -{ - shared_ptr<const Film> film = _film.lock (); - assert (film); - - return (_video_content->length() - _next_video) < film->video_frames_to_time (1); -} - -void -VideoDecoder::seek (Time t) -{ - _next_video = t; -} - -void -VideoDecoder::seek_back () -{ - shared_ptr<const Film> film = _film.lock (); - assert (film); - _next_video -= film->video_frames_to_time (1); -} - -void -VideoDecoder::seek_forward () -{ - shared_ptr<const Film> film = _film.lock (); - assert (film); - _next_video += film->video_frames_to_time (1); -} - -void -VideoDecoder::set_video_container_size (libdcp::Size s) -{ - _video_container_size = s; -} diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 8de76c10f..d24219d95 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -20,37 +20,30 @@ #ifndef DCPOMATIC_VIDEO_DECODER_H #define DCPOMATIC_VIDEO_DECODER_H -#include "video_source.h" #include "decoder.h" #include "util.h" class VideoContent; -class VideoDecoder : public VideoSource, public virtual Decoder +class VideoDecoder : public virtual Decoder { public: - VideoDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const VideoContent>); + VideoDecoder (boost::shared_ptr<const Film>); - virtual void seek (Time); - virtual void seek_back (); - virtual void seek_forward (); - - void set_video_container_size (libdcp::Size); + virtual void seek (VideoContent::Frame) = 0; + virtual void seek_back () = 0; -protected: + /** Emitted when a video frame is ready. + * First parameter is the video image. + * Second parameter is true if the image is the same as the last one that was emitted. + * Third parameter is the frame within our source. + */ + boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, VideoContent::Frame)> Video; - void video (boost::shared_ptr<Image>, bool, Time); - void subtitle (boost::shared_ptr<TimedSubtitle>); - bool video_done () const; - - Time _next_video; - boost::shared_ptr<const VideoContent> _video_content; - -private: - boost::shared_ptr<TimedSubtitle> _timed_subtitle; - FrameRateConversion _frame_rate_conversion; - bool _odd; - boost::optional<libdcp::Size> _video_container_size; +protected: + + void video (boost::shared_ptr<const Image>, bool, VideoContent::Frame); + VideoContent::Frame _video_position; }; #endif diff --git a/src/lib/video_examiner.h b/src/lib/video_examiner.h index 2f713291b..72f6ccc12 100644 --- a/src/lib/video_examiner.h +++ b/src/lib/video_examiner.h @@ -19,11 +19,12 @@ #include <libdcp/types.h> #include "types.h" +#include "video_content.h" class VideoExaminer { public: virtual float video_frame_rate () const = 0; virtual libdcp::Size video_size () const = 0; - virtual ContentVideoFrame video_length () const = 0; + virtual VideoContent::Frame video_length () const = 0; }; diff --git a/src/lib/video_sink.h b/src/lib/video_sink.h deleted file mode 100644 index 957aeb4b4..000000000 --- a/src/lib/video_sink.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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. - -*/ - -#ifndef DCPOMATIC_VIDEO_SINK_H -#define DCPOMATIC_VIDEO_SINK_H - -#include <boost/shared_ptr.hpp> -#include "util.h" - -class Subtitle; -class Image; - -class VideoSink -{ -public: - /** Call with a frame of video. - * @param i Video frame image. - * @param same true if i is the same as last time we were called. - */ - virtual void process_video (boost::shared_ptr<const Image> i, bool same, Time) = 0; -}; - -#endif diff --git a/src/lib/video_source.cc b/src/lib/video_source.cc deleted file mode 100644 index 824587bcb..000000000 --- a/src/lib/video_source.cc +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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 "video_source.h" -#include "video_sink.h" - -using boost::shared_ptr; -using boost::weak_ptr; -using boost::bind; - -static void -process_video_proxy (weak_ptr<VideoSink> sink, shared_ptr<const Image> image, bool same, Time time) -{ - shared_ptr<VideoSink> p = sink.lock (); - if (p) { - p->process_video (image, same, time); - } -} - -void -VideoSource::connect_video (shared_ptr<VideoSink> s) -{ - /* If we bind, say, a Player (as the VideoSink) to a Decoder (which is owned - by the Player) we create a cycle. Use a weak_ptr to break it. - */ - Video.connect (bind (process_video_proxy, weak_ptr<VideoSink> (s), _1, _2, _3)); -} - diff --git a/src/lib/video_source.h b/src/lib/video_source.h deleted file mode 100644 index 9242af444..000000000 --- a/src/lib/video_source.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - 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/video_source.h - * @brief Parent class for classes which emit video data. - */ - -#ifndef DCPOMATIC_VIDEO_SOURCE_H -#define DCPOMATIC_VIDEO_SOURCE_H - -#include <boost/shared_ptr.hpp> -#include <boost/signals2.hpp> -#include "util.h" - -class VideoSink; -class Subtitle; -class Image; - -/** @class VideoSource - * @param A class that emits video data. - */ -class VideoSource -{ -public: - - /** Emitted when a video frame is ready. - * First parameter is the video image. - * Second parameter is true if the image is the same as the last one that was emitted. - * Third parameter is the time relative to the start of this source's content. - */ - boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, Time)> Video; - - void connect_video (boost::shared_ptr<VideoSink>); -}; - -#endif diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 35874581b..cf81d5b70 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -133,7 +133,6 @@ Writer::fake_write (int frame) void Writer::write (shared_ptr<const AudioBuffers> audio) { - cout << "W: audio " << audio->frames() << "\n"; _sound_asset_writer->write (audio->data(), audio->frames()); } diff --git a/src/lib/wscript b/src/lib/wscript index d0f102998..3e7f2e33b 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -8,10 +8,7 @@ sources = """ audio_content.cc audio_decoder.cc audio_mapping.cc - audio_source.cc - black_decoder.cc config.cc - combiner.cc content.cc cross.cc dci_metadata.cc @@ -37,14 +34,13 @@ sources = """ job_manager.cc log.cc lut.cc - null_content.cc player.cc playlist.cc ratio.cc + resampler.cc scp_dcp_job.cc scaler.cc server.cc - silence_decoder.cc sndfile_content.cc sndfile_decoder.cc sound_processor.cc @@ -57,7 +53,6 @@ sources = """ util.cc video_content.cc video_decoder.cc - video_source.cc writer.cc """ diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index db2ab9dfe..8ef64d509 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -181,7 +181,7 @@ FilmViewer::update_from_decoder () return; } - _player->seek (_player->position() - _film->video_frames_to_time (1)); + _player->seek (_player->video_position() - _film->video_frames_to_time (1)); get_frame (); _panel->Refresh (); _panel->Update (); @@ -197,7 +197,7 @@ FilmViewer::timer (wxTimerEvent &) get_frame (); if (_film->length()) { - int const new_slider_position = 4096 * _player->position() / _film->length(); + int const new_slider_position = 4096 * _player->video_position() / _film->length(); if (new_slider_position != _slider->GetValue()) { _slider->SetValue (new_slider_position); } @@ -445,7 +445,6 @@ FilmViewer::forward_clicked (wxCommandEvent &) return; } - _player->seek_forward (); get_frame (); _panel->Refresh (); _panel->Update (); |
