--- /dev/null
+/*
+ Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/** @file src/dcp_encoder.cc
+ * @brief A class which takes a Film and some Options, then uses those to encode the film into a DCP.
+ *
+ * A decoder is selected according to the content type, and the encoder can be specified
+ * as a parameter to the constructor.
+ */
+
+#include "dcp_encoder.h"
+#include "j2k_encoder.h"
+#include "film.h"
+#include "video_decoder.h"
+#include "audio_decoder.h"
+#include "player.h"
+#include "job.h"
+#include "writer.h"
+#include "compose.hpp"
+#include "referenced_reel_asset.h"
+#include "subtitle_content.h"
+#include "player_video.h"
+#include <boost/signals2.hpp>
+#include <boost/foreach.hpp>
+#include <iostream>
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using std::list;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::dynamic_pointer_cast;
+
+/** Construct a DCP encoder.
+ * @param film Film that we are encoding.
+ * @param job Job that this encoder is being used in.
+ */
+DCPEncoder::DCPEncoder (shared_ptr<const Film> film, weak_ptr<Job> job)
+ : Encoder (film, job)
+ , _writer (new Writer (film, job))
+ , _j2k_encoder (new J2KEncoder (film, _writer))
+ , _finishing (false)
+ , _non_burnt_subtitles (false)
+{
+ BOOST_FOREACH (shared_ptr<const Content> c, _film->content ()) {
+ if (c->subtitle && c->subtitle->use() && !c->subtitle->burn()) {
+ _non_burnt_subtitles = true;
+ }
+ }
+}
+
+void
+DCPEncoder::go ()
+{
+ _writer->start ();
+ _j2k_encoder->begin ();
+
+ {
+ shared_ptr<Job> job = _job.lock ();
+ DCPOMATIC_ASSERT (job);
+ job->sub (_("Encoding"));
+ }
+
+ if (_non_burnt_subtitles) {
+ _writer->write (_player->get_subtitle_fonts ());
+ }
+
+ while (!_player->pass ()) {}
+
+ BOOST_FOREACH (ReferencedReelAsset i, _player->get_reel_assets ()) {
+ _writer->write (i);
+ }
+
+ _finishing = true;
+ _j2k_encoder->end ();
+ _writer->finish ();
+}
+
+void
+DCPEncoder::video (shared_ptr<PlayerVideo> data, DCPTime time)
+{
+ if (!_film->three_d() && data->eyes() == EYES_LEFT) {
+ /* Use left-eye images for both eyes */
+ data->set_eyes (EYES_BOTH);
+ }
+
+ _j2k_encoder->encode (data, time);
+}
+
+void
+DCPEncoder::audio (shared_ptr<AudioBuffers> data, DCPTime time)
+{
+ _writer->write (data);
+
+ shared_ptr<Job> job = _job.lock ();
+ DCPOMATIC_ASSERT (job);
+ job->set_progress (float(time.get()) / _film->length().get());
+}
+
+void
+DCPEncoder::subtitle (PlayerSubtitles data, DCPTimePeriod period)
+{
+ if (_non_burnt_subtitles) {
+ _writer->write (data, period);
+ }
+}
+
+float
+DCPEncoder::current_rate () const
+{
+ return _j2k_encoder->current_encoding_rate ();
+}
+
+Frame
+DCPEncoder::frames_done () const
+{
+ return _j2k_encoder->video_frames_enqueued ();
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "types.h"
+#include "player_subtitles.h"
+#include "encoder.h"
+#include <boost/weak_ptr.hpp>
+
+class Film;
+class J2KEncoder;
+class Player;
+class Writer;
+class Job;
+class PlayerVideo;
+class AudioBuffers;
+
+/** @class DCPEncoder */
+class DCPEncoder : public Encoder
+{
+public:
+ DCPEncoder (boost::shared_ptr<const Film> film, boost::weak_ptr<Job> job);
+
+ void go ();
+
+ float current_rate () const;
+ Frame frames_done () const;
+
+ /** @return true if we are in the process of calling Encoder::process_end */
+ bool finishing () const {
+ return _finishing;
+ }
+
+private:
+
+ void video (boost::shared_ptr<PlayerVideo>, DCPTime);
+ void audio (boost::shared_ptr<AudioBuffers>, DCPTime);
+ void subtitle (PlayerSubtitles, DCPTimePeriod);
+
+ boost::shared_ptr<Writer> _writer;
+ boost::shared_ptr<J2KEncoder> _j2k_encoder;
+ bool _finishing;
+ bool _non_burnt_subtitles;
+};
+++ /dev/null
-/*
- Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-/** @file src/dcp_transcoder.cc
- * @brief A class which takes a Film and some Options, then uses those to transcode the film into a DCP.
- *
- * A decoder is selected according to the content type, and the encoder can be specified
- * as a parameter to the constructor.
- */
-
-#include "dcp_transcoder.h"
-#include "encoder.h"
-#include "film.h"
-#include "video_decoder.h"
-#include "audio_decoder.h"
-#include "player.h"
-#include "job.h"
-#include "writer.h"
-#include "compose.hpp"
-#include "referenced_reel_asset.h"
-#include "subtitle_content.h"
-#include "player_video.h"
-#include <boost/signals2.hpp>
-#include <boost/foreach.hpp>
-#include <iostream>
-
-#include "i18n.h"
-
-using std::string;
-using std::cout;
-using std::list;
-using boost::shared_ptr;
-using boost::weak_ptr;
-using boost::dynamic_pointer_cast;
-
-/** Construct a DCP transcoder.
- * @param film Film that we are transcoding.
- * @param job Job that this transcoder is being used in.
- */
-DCPTranscoder::DCPTranscoder (shared_ptr<const Film> film, weak_ptr<Job> job)
- : Transcoder (film, job)
- , _writer (new Writer (film, job))
- , _encoder (new Encoder (film, _writer))
- , _finishing (false)
- , _non_burnt_subtitles (false)
-{
- BOOST_FOREACH (shared_ptr<const Content> c, _film->content ()) {
- if (c->subtitle && c->subtitle->use() && !c->subtitle->burn()) {
- _non_burnt_subtitles = true;
- }
- }
-}
-
-void
-DCPTranscoder::go ()
-{
- _writer->start ();
- _encoder->begin ();
-
- {
- shared_ptr<Job> job = _job.lock ();
- DCPOMATIC_ASSERT (job);
- job->sub (_("Encoding"));
- }
-
- if (_non_burnt_subtitles) {
- _writer->write (_player->get_subtitle_fonts ());
- }
-
- while (!_player->pass ()) {}
-
- BOOST_FOREACH (ReferencedReelAsset i, _player->get_reel_assets ()) {
- _writer->write (i);
- }
-
- _finishing = true;
- _encoder->end ();
- _writer->finish ();
-}
-
-void
-DCPTranscoder::video (shared_ptr<PlayerVideo> data, DCPTime time)
-{
- if (!_film->three_d() && data->eyes() == EYES_LEFT) {
- /* Use left-eye images for both eyes */
- data->set_eyes (EYES_BOTH);
- }
-
- _encoder->encode (data, time);
-}
-
-void
-DCPTranscoder::audio (shared_ptr<AudioBuffers> data, DCPTime time)
-{
- _writer->write (data);
-
- shared_ptr<Job> job = _job.lock ();
- DCPOMATIC_ASSERT (job);
- job->set_progress (float(time.get()) / _film->length().get());
-}
-
-void
-DCPTranscoder::subtitle (PlayerSubtitles data, DCPTimePeriod period)
-{
- if (_non_burnt_subtitles) {
- _writer->write (data, period);
- }
-}
-
-float
-DCPTranscoder::current_rate () const
-{
- return _encoder->current_encoding_rate ();
-}
-
-Frame
-DCPTranscoder::frames_done () const
-{
- return _encoder->video_frames_enqueued ();
-}
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "types.h"
-#include "player_subtitles.h"
-#include "transcoder.h"
-#include <boost/weak_ptr.hpp>
-
-class Film;
-class Encoder;
-class Player;
-class Writer;
-class Job;
-class PlayerVideo;
-class AudioBuffers;
-
-/** @class DCPTranscoder */
-class DCPTranscoder : public Transcoder
-{
-public:
- DCPTranscoder (boost::shared_ptr<const Film> film, boost::weak_ptr<Job> job);
-
- void go ();
-
- float current_rate () const;
- Frame frames_done () const;
-
- /** @return true if we are in the process of calling Encoder::process_end */
- bool finishing () const {
- return _finishing;
- }
-
-private:
-
- void video (boost::shared_ptr<PlayerVideo>, DCPTime);
- void audio (boost::shared_ptr<AudioBuffers>, DCPTime);
- void subtitle (PlayerSubtitles, DCPTimePeriod);
-
- boost::shared_ptr<Writer> _writer;
- boost::shared_ptr<Encoder> _encoder;
- bool _finishing;
- bool _non_burnt_subtitles;
-};
/*
- Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
-/** @file src/encoder.h
- * @brief Parent class for classes which can encode video and audio frames.
+/** @file src/encoder.cc
+ * @brief A class which takes a Film and some Options, then uses those to encode the film
+ * into some output format.
+ *
+ * A decoder is selected according to the content type, and the encoder can be specified
+ * as a parameter to the constructor.
*/
#include "encoder.h"
-#include "util.h"
-#include "film.h"
-#include "log.h"
-#include "config.h"
-#include "dcp_video.h"
-#include "cross.h"
-#include "writer.h"
-#include "encode_server_finder.h"
#include "player.h"
-#include "player_video.h"
-#include "encode_server_description.h"
-#include "compose.hpp"
-#include <libcxml/cxml.h>
-#include <boost/foreach.hpp>
-#include <iostream>
#include "i18n.h"
-#define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
-#define LOG_GENERAL_NC(...) _film->log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
-#define LOG_ERROR(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_ERROR);
-#define LOG_TIMING(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_TIMING);
-#define LOG_DEBUG_ENCODE(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_DEBUG_ENCODE);
-
-using std::list;
-using std::cout;
-using boost::shared_ptr;
using boost::weak_ptr;
-using boost::optional;
-using dcp::Data;
+using boost::shared_ptr;
-/** @param film Film that we are encoding.
- * @param writer Writer that we are using.
+/** Construct an encoder.
+ * @param film Film that we are encoding.
+ * @param job Job that this encoder is being used in.
*/
-Encoder::Encoder (shared_ptr<const Film> film, shared_ptr<Writer> writer)
+Encoder::Encoder (shared_ptr<const Film> film, weak_ptr<Job> job)
: _film (film)
- , _history (200)
- , _writer (writer)
-{
- servers_list_changed ();
-}
-
-Encoder::~Encoder ()
-{
- try {
- terminate_threads ();
- } catch (...) {
- /* Destructors must not throw exceptions; anything bad
- happening now is too late to worry about anyway,
- I think.
- */
- }
-}
-
-void
-Encoder::begin ()
-{
- weak_ptr<Encoder> wp = shared_from_this ();
- _server_found_connection = EncodeServerFinder::instance()->ServersListChanged.connect (
- boost::bind (&Encoder::call_servers_list_changed, wp)
- );
-}
-
-/* We don't want the servers-list-changed callback trying to do things
- during destruction of Encoder, and I think this is the neatest way
- to achieve that.
-*/
-void
-Encoder::call_servers_list_changed (weak_ptr<Encoder> encoder)
-{
- shared_ptr<Encoder> e = encoder.lock ();
- if (e) {
- e->servers_list_changed ();
- }
-}
-
-void
-Encoder::end ()
-{
- boost::mutex::scoped_lock lock (_queue_mutex);
-
- LOG_GENERAL (N_("Clearing queue of %1"), _queue.size ());
-
- /* Keep waking workers until the queue is empty */
- while (!_queue.empty ()) {
- rethrow ();
- _empty_condition.notify_all ();
- _full_condition.wait (lock);
- }
-
- lock.unlock ();
-
- LOG_GENERAL_NC (N_("Terminating encoder threads"));
-
- terminate_threads ();
-
- LOG_GENERAL (N_("Mopping up %1"), _queue.size());
-
- /* The following sequence of events can occur in the above code:
- 1. a remote worker takes the last image off the queue
- 2. the loop above terminates
- 3. the remote worker fails to encode the image and puts it back on the queue
- 4. the remote worker is then terminated by terminate_threads
-
- So just mop up anything left in the queue here.
- */
-
- for (list<shared_ptr<DCPVideo> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
- LOG_GENERAL (N_("Encode left-over frame %1"), (*i)->index ());
- try {
- _writer->write (
- (*i)->encode_locally (boost::bind (&Log::dcp_log, _film->log().get(), _1, _2)),
- (*i)->index (),
- (*i)->eyes ()
- );
- frame_done ();
- } catch (std::exception& e) {
- LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
- }
- }
-}
-
-/** @return an estimate of the current number of frames we are encoding per second,
- * or 0 if not known.
- */
-float
-Encoder::current_encoding_rate () const
-{
- return _history.rate ();
-}
-
-/** @return Number of video frames that have been queued for encoding */
-int
-Encoder::video_frames_enqueued () const
-{
- if (!_last_player_video_time) {
- return 0;
- }
-
- return _last_player_video_time->frames_floor (_film->video_frame_rate ());
-}
-
-/** Should be called when a frame has been encoded successfully */
-void
-Encoder::frame_done ()
-{
- _history.event ();
-}
-
-/** Called to request encoding of the next video frame in the DCP. This is called in order,
- * so each time the supplied frame is the one after the previous one.
- * pv represents one video frame, and could be empty if there is nothing to encode
- * for this DCP frame.
- *
- * @param pv PlayerVideo to encode.
- * @param time Time of \p pv within the DCP.
- */
-void
-Encoder::encode (shared_ptr<PlayerVideo> pv, DCPTime time)
-{
- _waker.nudge ();
-
- size_t threads = 0;
- {
- boost::mutex::scoped_lock threads_lock (_threads_mutex);
- threads = _threads.size ();
- }
-
- boost::mutex::scoped_lock queue_lock (_queue_mutex);
-
- /* Wait until the queue has gone down a bit. Allow one thing in the queue even
- when there are no threads.
- */
- while (_queue.size() >= (threads * 2) + 1) {
- LOG_TIMING ("decoder-sleep queue=%1 threads=%2", _queue.size(), threads);
- _full_condition.wait (queue_lock);
- LOG_TIMING ("decoder-wake queue=%1 threads=%2", _queue.size(), threads);
- }
-
- _writer->rethrow ();
- /* Re-throw any exception raised by one of our threads. If more
- than one has thrown an exception, only one will be rethrown, I think;
- but then, if that happens something has gone badly wrong.
- */
- rethrow ();
-
- Frame const position = time.frames_floor(_film->video_frame_rate());
-
- if (_writer->can_fake_write (position)) {
- /* We can fake-write this frame */
- LOG_DEBUG_ENCODE("Frame @ %1 FAKE", to_string(time));
- _writer->fake_write (position, pv->eyes ());
- frame_done ();
- } else if (pv->has_j2k ()) {
- LOG_DEBUG_ENCODE("Frame @ %1 J2K", to_string(time));
- /* This frame already has JPEG2000 data, so just write it */
- _writer->write (pv->j2k(), position, pv->eyes ());
- } else if (_last_player_video && _writer->can_repeat(position) && pv->same (_last_player_video)) {
- LOG_DEBUG_ENCODE("Frame @ %1 REPEAT", to_string(time));
- _writer->repeat (position, pv->eyes ());
- } else {
- LOG_DEBUG_ENCODE("Frame @ %1 ENCODE", to_string(time));
- /* Queue this new frame for encoding */
- LOG_TIMING ("add-frame-to-queue queue=%1", _queue.size ());
- _queue.push_back (shared_ptr<DCPVideo> (
- new DCPVideo (
- pv,
- position,
- _film->video_frame_rate(),
- _film->j2k_bandwidth(),
- _film->resolution(),
- _film->log()
- )
- ));
-
- /* The queue might not be empty any more, so notify anything which is
- waiting on that.
- */
- _empty_condition.notify_all ();
- }
-
- _last_player_video = pv;
- _last_player_video_time = time;
-}
-
-void
-Encoder::terminate_threads ()
-{
- boost::mutex::scoped_lock threads_lock (_threads_mutex);
-
- int n = 0;
- for (list<boost::thread *>::iterator i = _threads.begin(); i != _threads.end(); ++i) {
- LOG_GENERAL ("Terminating thread %1 of %2", n + 1, _threads.size ());
- (*i)->interrupt ();
- DCPOMATIC_ASSERT ((*i)->joinable ());
- try {
- (*i)->join ();
- } catch (boost::thread_interrupted& e) {
- /* This is to be expected */
- }
- delete *i;
- LOG_GENERAL_NC ("Thread terminated");
- ++n;
- }
-
- _threads.clear ();
-}
-
-void
-Encoder::encoder_thread (optional<EncodeServerDescription> server)
-try
+ , _job (job)
+ , _player (new Player (film, film->playlist ()))
{
- if (server) {
- LOG_TIMING ("start-encoder-thread thread=%1 server=%2", thread_id (), server->host_name ());
- } else {
- LOG_TIMING ("start-encoder-thread thread=%1 server=localhost", thread_id ());
- }
-
- /* Number of seconds that we currently wait between attempts
- to connect to the server; not relevant for localhost
- encodings.
- */
- int remote_backoff = 0;
-
- while (true) {
-
- LOG_TIMING ("encoder-sleep thread=%1", thread_id ());
- boost::mutex::scoped_lock lock (_queue_mutex);
- while (_queue.empty ()) {
- _empty_condition.wait (lock);
- }
-
- LOG_TIMING ("encoder-wake thread=%1 queue=%2", thread_id(), _queue.size());
- shared_ptr<DCPVideo> vf = _queue.front ();
-
- /* We're about to commit to either encoding this frame or putting it back onto the queue,
- so we must not be interrupted until one or other of these things have happened. This
- block has thread interruption disabled.
- */
- {
- boost::this_thread::disable_interruption dis;
-
- LOG_TIMING ("encoder-pop thread=%1 frame=%2 eyes=%3", thread_id(), vf->index(), (int) vf->eyes ());
- _queue.pop_front ();
-
- lock.unlock ();
-
- optional<Data> encoded;
-
- /* We need to encode this input */
- if (server) {
- try {
- encoded = vf->encode_remotely (server.get ());
-
- if (remote_backoff > 0) {
- LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
- }
-
- /* This job succeeded, so remove any backoff */
- remote_backoff = 0;
-
- } catch (std::exception& e) {
- if (remote_backoff < 60) {
- /* back off more */
- remote_backoff += 10;
- }
- LOG_ERROR (
- N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
- vf->index(), server->host_name(), e.what(), remote_backoff
- );
- }
-
- } else {
- try {
- LOG_TIMING ("start-local-encode thread=%1 frame=%2", thread_id(), vf->index());
- encoded = vf->encode_locally (boost::bind (&Log::dcp_log, _film->log().get(), _1, _2));
- LOG_TIMING ("finish-local-encode thread=%1 frame=%2", thread_id(), vf->index());
- } catch (std::exception& e) {
- /* This is very bad, so don't cope with it, just pass it on */
- LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
- throw;
- }
- }
-
- if (encoded) {
- _writer->write (encoded.get(), vf->index (), vf->eyes ());
- frame_done ();
- } else {
- lock.lock ();
- LOG_GENERAL (N_("[%1] Encoder thread pushes frame %2 back onto queue after failure"), thread_id(), vf->index());
- _queue.push_front (vf);
- lock.unlock ();
- }
- }
-
- if (remote_backoff > 0) {
- boost::this_thread::sleep (boost::posix_time::seconds (remote_backoff));
- }
-
- /* The queue might not be full any more, so notify anything that is waiting on that */
- lock.lock ();
- _full_condition.notify_all ();
- }
-}
-catch (boost::thread_interrupted& e) {
- /* Ignore these and just stop the thread */
- _full_condition.notify_all ();
-}
-catch (...)
-{
- store_current ();
- /* Wake anything waiting on _full_condition so it can see the exception */
- _full_condition.notify_all ();
-}
-
-void
-Encoder::servers_list_changed ()
-{
- terminate_threads ();
-
- /* XXX: could re-use threads */
-
- boost::mutex::scoped_lock lm (_threads_mutex);
-
-#ifdef BOOST_THREAD_PLATFORM_WIN32
- OSVERSIONINFO info;
- info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
- GetVersionEx (&info);
- bool const windows_xp = (info.dwMajorVersion == 5 && info.dwMinorVersion == 1);
- if (windows_xp) {
- LOG_GENERAL_NC (N_("Setting thread affinity for Windows XP"));
- }
-#endif
-
- if (!Config::instance()->only_servers_encode ()) {
- for (int i = 0; i < Config::instance()->master_encoding_threads (); ++i) {
- boost::thread* t = new boost::thread (boost::bind (&Encoder::encoder_thread, this, optional<EncodeServerDescription> ()));
- _threads.push_back (t);
-#ifdef BOOST_THREAD_PLATFORM_WIN32
- if (windows_xp) {
- SetThreadAffinityMask (t->native_handle(), 1 << i);
- }
-#endif
- }
- }
-
- BOOST_FOREACH (EncodeServerDescription i, EncodeServerFinder::instance()->servers ()) {
- LOG_GENERAL (N_("Adding %1 worker threads for remote %2"), i.threads(), i.host_name ());
- for (int j = 0; j < i.threads(); ++j) {
- _threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, i)));
- }
- }
-
- _writer->set_encoder_threads (_threads.size ());
+ _player->Video.connect (bind (&Encoder::video, this, _1, _2));
+ _player->Audio.connect (bind (&Encoder::audio, this, _1, _2));
+ _player->Subtitle.connect (bind (&Encoder::subtitle, this, _1, _2));
}
/*
- Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#ifndef DCPOMATIC_ENCODER_H
#define DCPOMATIC_ENCODER_H
-/** @file src/encoder.h
- * @brief Encoder class.
- */
-
-#include "util.h"
-#include "cross.h"
-#include "event_history.h"
-#include "exception_store.h"
-#include <boost/shared_ptr.hpp>
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/condition.hpp>
-#include <boost/thread.hpp>
-#include <boost/optional.hpp>
-#include <boost/signals2.hpp>
-#include <boost/enable_shared_from_this.hpp>
-#include <list>
-#include <stdint.h>
+#include "types.h"
+#include "player_subtitles.h"
+#include <boost/weak_ptr.hpp>
class Film;
-class EncodeServerDescription;
-class DCPVideo;
-class Writer;
+class Encoder;
+class Player;
class Job;
class PlayerVideo;
+class AudioBuffers;
-/** @class Encoder
- * @brief Class to manage encoding to JPEG2000.
- *
- * This class keeps a queue of frames to be encoded and distributes
- * the work around threads and encoding servers.
- */
-
-class Encoder : public boost::noncopyable, public ExceptionStore, public boost::enable_shared_from_this<Encoder>
+/** @class Encoder */
+class Encoder : public boost::noncopyable
{
public:
- Encoder (boost::shared_ptr<const Film> film, boost::shared_ptr<Writer> writer);
- ~Encoder ();
-
- /** Called to indicate that a processing run is about to begin */
- void begin ();
-
- /** Called to pass a bit of video to be encoded as the next DCP frame */
- void encode (boost::shared_ptr<PlayerVideo> pv, DCPTime time);
-
- /** Called when a processing run has finished */
- void end ();
-
- float current_encoding_rate () const;
- int video_frames_enqueued () const;
-
- void servers_list_changed ();
+ Encoder (boost::shared_ptr<const Film> film, boost::weak_ptr<Job> job);
+ virtual ~Encoder () {}
-private:
+ virtual void go () = 0;
- static void call_servers_list_changed (boost::weak_ptr<Encoder> encoder);
+ /** @return the current frame rate over the last short while */
+ virtual float current_rate () const = 0;
+ /** @return the number of frames that are done */
+ virtual Frame frames_done () const = 0;
+ virtual bool finishing () const = 0;
- void frame_done ();
+protected:
+ virtual void video (boost::shared_ptr<PlayerVideo>, DCPTime) = 0;
+ virtual void audio (boost::shared_ptr<AudioBuffers>, DCPTime) = 0;
+ virtual void subtitle (PlayerSubtitles, DCPTimePeriod) = 0;
- void encoder_thread (boost::optional<EncodeServerDescription>);
- void terminate_threads ();
-
- /** Film that we are encoding */
boost::shared_ptr<const Film> _film;
-
- EventHistory _history;
-
- /** Mutex for _threads */
- mutable boost::mutex _threads_mutex;
- std::list<boost::thread *> _threads;
- mutable boost::mutex _queue_mutex;
- std::list<boost::shared_ptr<DCPVideo> > _queue;
- /** condition to manage thread wakeups when we have nothing to do */
- boost::condition _empty_condition;
- /** condition to manage thread wakeups when we have too much to do */
- boost::condition _full_condition;
-
- boost::shared_ptr<Writer> _writer;
- Waker _waker;
-
- boost::shared_ptr<PlayerVideo> _last_player_video;
- boost::optional<DCPTime> _last_player_video_time;
-
- boost::signals2::scoped_connection _server_found_connection;
+ boost::weak_ptr<Job> _job;
+ boost::shared_ptr<Player> _player;
};
#endif
--- /dev/null
+/*
+ Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ffmpeg_encoder.h"
+#include "film.h"
+#include "job.h"
+#include "player.h"
+#include "player_video.h"
+#include "log.h"
+#include "image.h"
+#include "compose.hpp"
+#include <iostream>
+
+#include "i18n.h"
+
+using std::string;
+using std::runtime_error;
+using std::cout;
+using boost::shared_ptr;
+using boost::bind;
+using boost::weak_ptr;
+
+static AVPixelFormat
+force_pixel_format (AVPixelFormat, AVPixelFormat out)
+{
+ return out;
+}
+
+FFmpegEncoder::FFmpegEncoder (shared_ptr<const Film> film, weak_ptr<Job> job, boost::filesystem::path output, Format format)
+ : Encoder (film, job)
+ , _history (1000)
+ , _output (output)
+{
+ switch (format) {
+ case FORMAT_PRORES:
+ _pixel_format = AV_PIX_FMT_YUV422P10;
+ _codec_name = "prores_ks";
+ break;
+ case FORMAT_H264:
+ _pixel_format = AV_PIX_FMT_YUV420P;
+ _codec_name = "libx264";
+ break;
+ }
+}
+
+void
+FFmpegEncoder::go ()
+{
+ AVCodec* codec = avcodec_find_encoder_by_name (_codec_name.c_str());
+ if (!codec) {
+ throw runtime_error (String::compose ("could not find FFmpeg encoder %1", _codec_name));
+ }
+
+ _codec_context = avcodec_alloc_context3 (codec);
+ if (!_codec_context) {
+ throw runtime_error ("could not allocate FFmpeg context");
+ }
+
+ avcodec_get_context_defaults3 (_codec_context, codec);
+
+ /* Variable quantisation */
+ _codec_context->global_quality = 0;
+ _codec_context->width = _film->frame_size().width;
+ _codec_context->height = _film->frame_size().height;
+ _codec_context->time_base = (AVRational) { 1, _film->video_frame_rate() };
+ _codec_context->pix_fmt = _pixel_format;
+ _codec_context->flags |= CODEC_FLAG_QSCALE | CODEC_FLAG_GLOBAL_HEADER;
+
+ avformat_alloc_output_context2 (&_format_context, 0, 0, _output.string().c_str());
+ if (!_format_context) {
+ throw runtime_error ("could not allocate FFmpeg format context");
+ }
+
+ _video_stream = avformat_new_stream (_format_context, codec);
+ if (!_video_stream) {
+ throw runtime_error ("could not create FFmpeg output video stream");
+ }
+
+ /* Note: needs to increment with each stream */
+ _video_stream->id = 0;
+ _video_stream->codec = _codec_context;
+
+ AVDictionary* options = 0;
+ av_dict_set (&options, "profile", "3", 0);
+ av_dict_set (&options, "threads", "auto", 0);
+
+ if (avcodec_open2 (_codec_context, codec, &options) < 0) {
+ throw runtime_error ("could not open FFmpeg codec");
+ }
+
+ if (avio_open (&_format_context->pb, _output.c_str(), AVIO_FLAG_WRITE) < 0) {
+ throw runtime_error ("could not open FFmpeg output file");
+ }
+
+ if (avformat_write_header (_format_context, &options) < 0) {
+ throw runtime_error ("could not write header to FFmpeg output file");
+ }
+
+ {
+ shared_ptr<Job> job = _job.lock ();
+ DCPOMATIC_ASSERT (job);
+ job->sub (_("Encoding"));
+ }
+
+ while (!_player->pass ()) {}
+
+ while (true) {
+ AVPacket packet;
+ av_init_packet (&packet);
+ packet.data = 0;
+ packet.size = 0;
+
+ int got_packet;
+ avcodec_encode_video2 (_codec_context, &packet, 0, &got_packet);
+ if (!got_packet) {
+ break;
+ }
+
+ packet.stream_index = 0;
+ av_interleaved_write_frame (_format_context, &packet);
+ av_packet_unref (&packet);
+ }
+
+ av_write_trailer (_format_context);
+
+ avcodec_close (_codec_context);
+ avio_close (_format_context->pb);
+ avformat_free_context (_format_context);
+}
+
+void
+FFmpegEncoder::video (shared_ptr<PlayerVideo> video, DCPTime time)
+{
+ shared_ptr<Image> image = video->image (
+ bind (&Log::dcp_log, _film->log().get(), _1, _2),
+ bind (&force_pixel_format, _1, _pixel_format),
+ true,
+ false
+ );
+
+ AVFrame* frame = av_frame_alloc ();
+
+ for (int i = 0; i < 3; ++i) {
+ size_t const size = image->stride()[i] * image->size().height;
+ AVBufferRef* buffer = av_buffer_alloc (size);
+ /* XXX: inefficient */
+ memcpy (buffer->data, image->data()[i], size);
+ frame->buf[i] = av_buffer_ref (buffer);
+ frame->data[i] = buffer->data;
+ frame->linesize[i] = image->stride()[i];
+ av_buffer_unref (&buffer);
+ }
+
+ frame->width = image->size().width;
+ frame->height = image->size().height;
+ frame->format = _pixel_format;
+ frame->pts = time.seconds() / av_q2d (_video_stream->time_base);
+
+ AVPacket packet;
+ av_init_packet (&packet);
+ packet.data = 0;
+ packet.size = 0;
+
+ int got_packet;
+ if (avcodec_encode_video2 (_codec_context, &packet, frame, &got_packet) < 0) {
+ throw EncodeError ("FFmpeg video encode failed");
+ }
+
+ if (got_packet && packet.size) {
+ /* XXX: this should not be hard-wired */
+ packet.stream_index = 0;
+ av_interleaved_write_frame (_format_context, &packet);
+ av_packet_unref (&packet);
+ }
+
+ av_frame_free (&frame);
+
+ _history.event ();
+
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _last_time = time;
+ }
+
+ shared_ptr<Job> job = _job.lock ();
+ if (job) {
+ job->set_progress (float(time.get()) / _film->length().get());
+ }
+}
+
+void
+FFmpegEncoder::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
+{
+
+}
+
+void
+FFmpegEncoder::subtitle (PlayerSubtitles subs, DCPTimePeriod period)
+{
+
+}
+
+float
+FFmpegEncoder::current_rate () const
+{
+ return _history.rate ();
+}
+
+Frame
+FFmpegEncoder::frames_done () const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ return _last_time.frames_round (_film->video_frame_rate ());
+}
--- /dev/null
+/*
+ Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef DCPOMATIC_FFMPEG_ENCODER_H
+#define DCPOMATIC_FFMPEG_ENCODER_H
+
+#include "encoder.h"
+#include "event_history.h"
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+}
+
+class FFmpegEncoder : public Encoder
+{
+public:
+ enum Format
+ {
+ FORMAT_PRORES,
+ FORMAT_H264
+ };
+
+ FFmpegEncoder (boost::shared_ptr<const Film> film, boost::weak_ptr<Job> job, boost::filesystem::path output, Format format);
+
+ void go ();
+
+ float current_rate () const;
+ Frame frames_done () const;
+ bool finishing () const {
+ return false;
+ }
+
+private:
+ void video (boost::shared_ptr<PlayerVideo>, DCPTime);
+ void audio (boost::shared_ptr<AudioBuffers>, DCPTime);
+ void subtitle (PlayerSubtitles, DCPTimePeriod);
+
+ AVCodecContext* _codec_context;
+ AVFormatContext* _format_context;
+ AVStream* _video_stream;
+ AVPixelFormat _pixel_format;
+ std::string _codec_name;
+
+ mutable boost::mutex _mutex;
+ DCPTime _last_time;
+
+ EventHistory _history;
+
+ boost::filesystem::path _output;
+};
+
+#endif
+++ /dev/null
-/*
- Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "ffmpeg_transcoder.h"
-#include "film.h"
-#include "job.h"
-#include "player.h"
-#include "player_video.h"
-#include "log.h"
-#include "image.h"
-#include "compose.hpp"
-#include <iostream>
-
-#include "i18n.h"
-
-using std::string;
-using std::runtime_error;
-using std::cout;
-using boost::shared_ptr;
-using boost::bind;
-using boost::weak_ptr;
-
-static AVPixelFormat
-force_pixel_format (AVPixelFormat, AVPixelFormat out)
-{
- return out;
-}
-
-FFmpegTranscoder::FFmpegTranscoder (shared_ptr<const Film> film, weak_ptr<Job> job, boost::filesystem::path output, Format format)
- : Transcoder (film, job)
- , _history (1000)
- , _output (output)
-{
- switch (format) {
- case FORMAT_PRORES:
- _pixel_format = AV_PIX_FMT_YUV422P10;
- _codec_name = "prores_ks";
- break;
- case FORMAT_H264:
- _pixel_format = AV_PIX_FMT_YUV420P;
- _codec_name = "libx264";
- break;
- }
-}
-
-void
-FFmpegTranscoder::go ()
-{
- AVCodec* codec = avcodec_find_encoder_by_name (_codec_name.c_str());
- if (!codec) {
- throw runtime_error (String::compose ("could not find FFmpeg encoder %1", _codec_name));
- }
-
- _codec_context = avcodec_alloc_context3 (codec);
- if (!_codec_context) {
- throw runtime_error ("could not allocate FFmpeg context");
- }
-
- avcodec_get_context_defaults3 (_codec_context, codec);
-
- /* Variable quantisation */
- _codec_context->global_quality = 0;
- _codec_context->width = _film->frame_size().width;
- _codec_context->height = _film->frame_size().height;
- _codec_context->time_base = (AVRational) { 1, _film->video_frame_rate() };
- _codec_context->pix_fmt = _pixel_format;
- _codec_context->flags |= CODEC_FLAG_QSCALE | CODEC_FLAG_GLOBAL_HEADER;
-
- avformat_alloc_output_context2 (&_format_context, 0, 0, _output.string().c_str());
- if (!_format_context) {
- throw runtime_error ("could not allocate FFmpeg format context");
- }
-
- _video_stream = avformat_new_stream (_format_context, codec);
- if (!_video_stream) {
- throw runtime_error ("could not create FFmpeg output video stream");
- }
-
- /* Note: needs to increment with each stream */
- _video_stream->id = 0;
- _video_stream->codec = _codec_context;
-
- AVDictionary* options = 0;
- av_dict_set (&options, "profile", "3", 0);
- av_dict_set (&options, "threads", "auto", 0);
-
- if (avcodec_open2 (_codec_context, codec, &options) < 0) {
- throw runtime_error ("could not open FFmpeg codec");
- }
-
- if (avio_open (&_format_context->pb, _output.c_str(), AVIO_FLAG_WRITE) < 0) {
- throw runtime_error ("could not open FFmpeg output file");
- }
-
- if (avformat_write_header (_format_context, &options) < 0) {
- throw runtime_error ("could not write header to FFmpeg output file");
- }
-
- {
- shared_ptr<Job> job = _job.lock ();
- DCPOMATIC_ASSERT (job);
- job->sub (_("Encoding"));
- }
-
- while (!_player->pass ()) {}
-
- while (true) {
- AVPacket packet;
- av_init_packet (&packet);
- packet.data = 0;
- packet.size = 0;
-
- int got_packet;
- avcodec_encode_video2 (_codec_context, &packet, 0, &got_packet);
- if (!got_packet) {
- break;
- }
-
- packet.stream_index = 0;
- av_interleaved_write_frame (_format_context, &packet);
- av_packet_unref (&packet);
- }
-
- av_write_trailer (_format_context);
-
- avcodec_close (_codec_context);
- avio_close (_format_context->pb);
- avformat_free_context (_format_context);
-}
-
-void
-FFmpegTranscoder::video (shared_ptr<PlayerVideo> video, DCPTime time)
-{
- shared_ptr<Image> image = video->image (
- bind (&Log::dcp_log, _film->log().get(), _1, _2),
- bind (&force_pixel_format, _1, _pixel_format),
- true,
- false
- );
-
- AVFrame* frame = av_frame_alloc ();
-
- for (int i = 0; i < 3; ++i) {
- size_t const size = image->stride()[i] * image->size().height;
- AVBufferRef* buffer = av_buffer_alloc (size);
- /* XXX: inefficient */
- memcpy (buffer->data, image->data()[i], size);
- frame->buf[i] = av_buffer_ref (buffer);
- frame->data[i] = buffer->data;
- frame->linesize[i] = image->stride()[i];
- av_buffer_unref (&buffer);
- }
-
- frame->width = image->size().width;
- frame->height = image->size().height;
- frame->format = _pixel_format;
- frame->pts = time.seconds() / av_q2d (_video_stream->time_base);
-
- AVPacket packet;
- av_init_packet (&packet);
- packet.data = 0;
- packet.size = 0;
-
- int got_packet;
- if (avcodec_encode_video2 (_codec_context, &packet, frame, &got_packet) < 0) {
- throw EncodeError ("FFmpeg video encode failed");
- }
-
- if (got_packet && packet.size) {
- /* XXX: this should not be hard-wired */
- packet.stream_index = 0;
- av_interleaved_write_frame (_format_context, &packet);
- av_packet_unref (&packet);
- }
-
- av_frame_free (&frame);
-
- _history.event ();
-
- {
- boost::mutex::scoped_lock lm (_mutex);
- _last_time = time;
- }
-
- shared_ptr<Job> job = _job.lock ();
- if (job) {
- job->set_progress (float(time.get()) / _film->length().get());
- }
-}
-
-void
-FFmpegTranscoder::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
-{
-
-}
-
-void
-FFmpegTranscoder::subtitle (PlayerSubtitles subs, DCPTimePeriod period)
-{
-
-}
-
-float
-FFmpegTranscoder::current_rate () const
-{
- return _history.rate ();
-}
-
-Frame
-FFmpegTranscoder::frames_done () const
-{
- boost::mutex::scoped_lock lm (_mutex);
- return _last_time.frames_round (_film->video_frame_rate ());
-}
+++ /dev/null
-/*
- Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef DCPOMATIC_FFMPEG_TRANSCODER_H
-#define DCPOMATIC_FFMPEG_TRANSCODER_H
-
-#include "transcoder.h"
-#include "event_history.h"
-extern "C" {
-#include <libavcodec/avcodec.h>
-#include <libavformat/avformat.h>
-}
-
-class FFmpegTranscoder : public Transcoder
-{
-public:
- enum Format
- {
- FORMAT_PRORES,
- FORMAT_H264
- };
-
- FFmpegTranscoder (boost::shared_ptr<const Film> film, boost::weak_ptr<Job> job, boost::filesystem::path output, Format format);
-
- void go ();
-
- float current_rate () const;
- Frame frames_done () const;
- bool finishing () const {
- return false;
- }
-
-private:
- void video (boost::shared_ptr<PlayerVideo>, DCPTime);
- void audio (boost::shared_ptr<AudioBuffers>, DCPTime);
- void subtitle (PlayerSubtitles, DCPTimePeriod);
-
- AVCodecContext* _codec_context;
- AVFormatContext* _format_context;
- AVStream* _video_stream;
- AVPixelFormat _pixel_format;
- std::string _codec_name;
-
- mutable boost::mutex _mutex;
- DCPTime _last_time;
-
- EventHistory _history;
-
- boost::filesystem::path _output;
-};
-
-#endif
#include "job.h"
#include "util.h"
#include "job_manager.h"
-#include "dcp_transcoder.h"
+#include "dcp_encoder.h"
#include "transcode_job.h"
#include "upload_job.h"
#include "null_log.h"
LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
shared_ptr<TranscodeJob> tj (new TranscodeJob (shared_from_this()));
- tj->set_transcoder (shared_ptr<Transcoder> (new DCPTranscoder (shared_from_this(), tj)));
+ tj->set_encoder (shared_ptr<Encoder> (new DCPEncoder (shared_from_this(), tj)));
JobManager::instance()->add (tj);
}
--- /dev/null
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/** @file src/j2k_encoder.cc
+ * @brief J2K encoder class.
+ */
+
+#include "j2k_encoder.h"
+#include "util.h"
+#include "film.h"
+#include "log.h"
+#include "config.h"
+#include "dcp_video.h"
+#include "cross.h"
+#include "writer.h"
+#include "encode_server_finder.h"
+#include "player.h"
+#include "player_video.h"
+#include "encode_server_description.h"
+#include "compose.hpp"
+#include <libcxml/cxml.h>
+#include <boost/foreach.hpp>
+#include <iostream>
+
+#include "i18n.h"
+
+#define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
+#define LOG_GENERAL_NC(...) _film->log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
+#define LOG_ERROR(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_ERROR);
+#define LOG_TIMING(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_TIMING);
+#define LOG_DEBUG_ENCODE(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_DEBUG_ENCODE);
+
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::optional;
+using dcp::Data;
+
+/** @param film Film that we are encoding.
+ * @param writer Writer that we are using.
+ */
+J2KEncoder::J2KEncoder (shared_ptr<const Film> film, shared_ptr<Writer> writer)
+ : _film (film)
+ , _history (200)
+ , _writer (writer)
+{
+ servers_list_changed ();
+}
+
+J2KEncoder::~J2KEncoder ()
+{
+ try {
+ terminate_threads ();
+ } catch (...) {
+ /* Destructors must not throw exceptions; anything bad
+ happening now is too late to worry about anyway,
+ I think.
+ */
+ }
+}
+
+void
+J2KEncoder::begin ()
+{
+ weak_ptr<J2KEncoder> wp = shared_from_this ();
+ _server_found_connection = EncodeServerFinder::instance()->ServersListChanged.connect (
+ boost::bind (&J2KEncoder::call_servers_list_changed, wp)
+ );
+}
+
+/* We don't want the servers-list-changed callback trying to do things
+ during destruction of J2KEncoder, and I think this is the neatest way
+ to achieve that.
+*/
+void
+J2KEncoder::call_servers_list_changed (weak_ptr<J2KEncoder> encoder)
+{
+ shared_ptr<J2KEncoder> e = encoder.lock ();
+ if (e) {
+ e->servers_list_changed ();
+ }
+}
+
+void
+J2KEncoder::end ()
+{
+ boost::mutex::scoped_lock lock (_queue_mutex);
+
+ LOG_GENERAL (N_("Clearing queue of %1"), _queue.size ());
+
+ /* Keep waking workers until the queue is empty */
+ while (!_queue.empty ()) {
+ rethrow ();
+ _empty_condition.notify_all ();
+ _full_condition.wait (lock);
+ }
+
+ lock.unlock ();
+
+ LOG_GENERAL_NC (N_("Terminating encoder threads"));
+
+ terminate_threads ();
+
+ LOG_GENERAL (N_("Mopping up %1"), _queue.size());
+
+ /* The following sequence of events can occur in the above code:
+ 1. a remote worker takes the last image off the queue
+ 2. the loop above terminates
+ 3. the remote worker fails to encode the image and puts it back on the queue
+ 4. the remote worker is then terminated by terminate_threads
+
+ So just mop up anything left in the queue here.
+ */
+
+ for (list<shared_ptr<DCPVideo> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ LOG_GENERAL (N_("Encode left-over frame %1"), (*i)->index ());
+ try {
+ _writer->write (
+ (*i)->encode_locally (boost::bind (&Log::dcp_log, _film->log().get(), _1, _2)),
+ (*i)->index (),
+ (*i)->eyes ()
+ );
+ frame_done ();
+ } catch (std::exception& e) {
+ LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
+ }
+ }
+}
+
+/** @return an estimate of the current number of frames we are encoding per second,
+ * or 0 if not known.
+ */
+float
+J2KEncoder::current_encoding_rate () const
+{
+ return _history.rate ();
+}
+
+/** @return Number of video frames that have been queued for encoding */
+int
+J2KEncoder::video_frames_enqueued () const
+{
+ if (!_last_player_video_time) {
+ return 0;
+ }
+
+ return _last_player_video_time->frames_floor (_film->video_frame_rate ());
+}
+
+/** Should be called when a frame has been encoded successfully */
+void
+J2KEncoder::frame_done ()
+{
+ _history.event ();
+}
+
+/** Called to request encoding of the next video frame in the DCP. This is called in order,
+ * so each time the supplied frame is the one after the previous one.
+ * pv represents one video frame, and could be empty if there is nothing to encode
+ * for this DCP frame.
+ *
+ * @param pv PlayerVideo to encode.
+ * @param time Time of \p pv within the DCP.
+ */
+void
+J2KEncoder::encode (shared_ptr<PlayerVideo> pv, DCPTime time)
+{
+ _waker.nudge ();
+
+ size_t threads = 0;
+ {
+ boost::mutex::scoped_lock threads_lock (_threads_mutex);
+ threads = _threads.size ();
+ }
+
+ boost::mutex::scoped_lock queue_lock (_queue_mutex);
+
+ /* Wait until the queue has gone down a bit. Allow one thing in the queue even
+ when there are no threads.
+ */
+ while (_queue.size() >= (threads * 2) + 1) {
+ LOG_TIMING ("decoder-sleep queue=%1 threads=%2", _queue.size(), threads);
+ _full_condition.wait (queue_lock);
+ LOG_TIMING ("decoder-wake queue=%1 threads=%2", _queue.size(), threads);
+ }
+
+ _writer->rethrow ();
+ /* Re-throw any exception raised by one of our threads. If more
+ than one has thrown an exception, only one will be rethrown, I think;
+ but then, if that happens something has gone badly wrong.
+ */
+ rethrow ();
+
+ Frame const position = time.frames_floor(_film->video_frame_rate());
+
+ if (_writer->can_fake_write (position)) {
+ /* We can fake-write this frame */
+ LOG_DEBUG_ENCODE("Frame @ %1 FAKE", to_string(time));
+ _writer->fake_write (position, pv->eyes ());
+ frame_done ();
+ } else if (pv->has_j2k ()) {
+ LOG_DEBUG_ENCODE("Frame @ %1 J2K", to_string(time));
+ /* This frame already has J2K data, so just write it */
+ _writer->write (pv->j2k(), position, pv->eyes ());
+ } else if (_last_player_video && _writer->can_repeat(position) && pv->same (_last_player_video)) {
+ LOG_DEBUG_ENCODE("Frame @ %1 REPEAT", to_string(time));
+ _writer->repeat (position, pv->eyes ());
+ } else {
+ LOG_DEBUG_ENCODE("Frame @ %1 ENCODE", to_string(time));
+ /* Queue this new frame for encoding */
+ LOG_TIMING ("add-frame-to-queue queue=%1", _queue.size ());
+ _queue.push_back (shared_ptr<DCPVideo> (
+ new DCPVideo (
+ pv,
+ position,
+ _film->video_frame_rate(),
+ _film->j2k_bandwidth(),
+ _film->resolution(),
+ _film->log()
+ )
+ ));
+
+ /* The queue might not be empty any more, so notify anything which is
+ waiting on that.
+ */
+ _empty_condition.notify_all ();
+ }
+
+ _last_player_video = pv;
+ _last_player_video_time = time;
+}
+
+void
+J2KEncoder::terminate_threads ()
+{
+ boost::mutex::scoped_lock threads_lock (_threads_mutex);
+
+ int n = 0;
+ for (list<boost::thread *>::iterator i = _threads.begin(); i != _threads.end(); ++i) {
+ LOG_GENERAL ("Terminating thread %1 of %2", n + 1, _threads.size ());
+ (*i)->interrupt ();
+ DCPOMATIC_ASSERT ((*i)->joinable ());
+ try {
+ (*i)->join ();
+ } catch (boost::thread_interrupted& e) {
+ /* This is to be expected */
+ }
+ delete *i;
+ LOG_GENERAL_NC ("Thread terminated");
+ ++n;
+ }
+
+ _threads.clear ();
+}
+
+void
+J2KEncoder::encoder_thread (optional<EncodeServerDescription> server)
+try
+{
+ if (server) {
+ LOG_TIMING ("start-encoder-thread thread=%1 server=%2", thread_id (), server->host_name ());
+ } else {
+ LOG_TIMING ("start-encoder-thread thread=%1 server=localhost", thread_id ());
+ }
+
+ /* Number of seconds that we currently wait between attempts
+ to connect to the server; not relevant for localhost
+ encodings.
+ */
+ int remote_backoff = 0;
+
+ while (true) {
+
+ LOG_TIMING ("encoder-sleep thread=%1", thread_id ());
+ boost::mutex::scoped_lock lock (_queue_mutex);
+ while (_queue.empty ()) {
+ _empty_condition.wait (lock);
+ }
+
+ LOG_TIMING ("encoder-wake thread=%1 queue=%2", thread_id(), _queue.size());
+ shared_ptr<DCPVideo> vf = _queue.front ();
+
+ /* We're about to commit to either encoding this frame or putting it back onto the queue,
+ so we must not be interrupted until one or other of these things have happened. This
+ block has thread interruption disabled.
+ */
+ {
+ boost::this_thread::disable_interruption dis;
+
+ LOG_TIMING ("encoder-pop thread=%1 frame=%2 eyes=%3", thread_id(), vf->index(), (int) vf->eyes ());
+ _queue.pop_front ();
+
+ lock.unlock ();
+
+ optional<Data> encoded;
+
+ /* We need to encode this input */
+ if (server) {
+ try {
+ encoded = vf->encode_remotely (server.get ());
+
+ if (remote_backoff > 0) {
+ LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
+ }
+
+ /* This job succeeded, so remove any backoff */
+ remote_backoff = 0;
+
+ } catch (std::exception& e) {
+ if (remote_backoff < 60) {
+ /* back off more */
+ remote_backoff += 10;
+ }
+ LOG_ERROR (
+ N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
+ vf->index(), server->host_name(), e.what(), remote_backoff
+ );
+ }
+
+ } else {
+ try {
+ LOG_TIMING ("start-local-encode thread=%1 frame=%2", thread_id(), vf->index());
+ encoded = vf->encode_locally (boost::bind (&Log::dcp_log, _film->log().get(), _1, _2));
+ LOG_TIMING ("finish-local-encode thread=%1 frame=%2", thread_id(), vf->index());
+ } catch (std::exception& e) {
+ /* This is very bad, so don't cope with it, just pass it on */
+ LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
+ throw;
+ }
+ }
+
+ if (encoded) {
+ _writer->write (encoded.get(), vf->index (), vf->eyes ());
+ frame_done ();
+ } else {
+ lock.lock ();
+ LOG_GENERAL (N_("[%1] J2KEncoder thread pushes frame %2 back onto queue after failure"), thread_id(), vf->index());
+ _queue.push_front (vf);
+ lock.unlock ();
+ }
+ }
+
+ if (remote_backoff > 0) {
+ boost::this_thread::sleep (boost::posix_time::seconds (remote_backoff));
+ }
+
+ /* The queue might not be full any more, so notify anything that is waiting on that */
+ lock.lock ();
+ _full_condition.notify_all ();
+ }
+}
+catch (boost::thread_interrupted& e) {
+ /* Ignore these and just stop the thread */
+ _full_condition.notify_all ();
+}
+catch (...)
+{
+ store_current ();
+ /* Wake anything waiting on _full_condition so it can see the exception */
+ _full_condition.notify_all ();
+}
+
+void
+J2KEncoder::servers_list_changed ()
+{
+ terminate_threads ();
+
+ /* XXX: could re-use threads */
+
+ boost::mutex::scoped_lock lm (_threads_mutex);
+
+#ifdef BOOST_THREAD_PLATFORM_WIN32
+ OSVERSIONINFO info;
+ info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ GetVersionEx (&info);
+ bool const windows_xp = (info.dwMajorVersion == 5 && info.dwMinorVersion == 1);
+ if (windows_xp) {
+ LOG_GENERAL_NC (N_("Setting thread affinity for Windows XP"));
+ }
+#endif
+
+ if (!Config::instance()->only_servers_encode ()) {
+ for (int i = 0; i < Config::instance()->master_encoding_threads (); ++i) {
+ boost::thread* t = new boost::thread (boost::bind (&J2KEncoder::encoder_thread, this, optional<EncodeServerDescription> ()));
+ _threads.push_back (t);
+#ifdef BOOST_THREAD_PLATFORM_WIN32
+ if (windows_xp) {
+ SetThreadAffinityMask (t->native_handle(), 1 << i);
+ }
+#endif
+ }
+ }
+
+ BOOST_FOREACH (EncodeServerDescription i, EncodeServerFinder::instance()->servers ()) {
+ LOG_GENERAL (N_("Adding %1 worker threads for remote %2"), i.threads(), i.host_name ());
+ for (int j = 0; j < i.threads(); ++j) {
+ _threads.push_back (new boost::thread (boost::bind (&J2KEncoder::encoder_thread, this, i)));
+ }
+ }
+
+ _writer->set_encoder_threads (_threads.size ());
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef DCPOMATIC_J2K_ENCODER_H
+#define DCPOMATIC_J2K_ENCODER_H
+
+/** @file src/j2k_encoder.h
+ * @brief J2KEncoder class.
+ */
+
+#include "util.h"
+#include "cross.h"
+#include "event_history.h"
+#include "exception_store.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/thread.hpp>
+#include <boost/optional.hpp>
+#include <boost/signals2.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <list>
+#include <stdint.h>
+
+class Film;
+class EncodeServerDescription;
+class DCPVideo;
+class Writer;
+class Job;
+class PlayerVideo;
+
+/** @class J2KEncoder
+ * @brief Class to manage encoding to J2K.
+ *
+ * This class keeps a queue of frames to be encoded and distributes
+ * the work around threads and encoding servers.
+ */
+
+class J2KEncoder : public boost::noncopyable, public ExceptionStore, public boost::enable_shared_from_this<J2KEncoder>
+{
+public:
+ J2KEncoder (boost::shared_ptr<const Film> film, boost::shared_ptr<Writer> writer);
+ ~J2KEncoder ();
+
+ /** Called to indicate that a processing run is about to begin */
+ void begin ();
+
+ /** Called to pass a bit of video to be encoded as the next DCP frame */
+ void encode (boost::shared_ptr<PlayerVideo> pv, DCPTime time);
+
+ /** Called when a processing run has finished */
+ void end ();
+
+ float current_encoding_rate () const;
+ int video_frames_enqueued () const;
+
+ void servers_list_changed ();
+
+private:
+
+ static void call_servers_list_changed (boost::weak_ptr<J2KEncoder> encoder);
+
+ void frame_done ();
+
+ void encoder_thread (boost::optional<EncodeServerDescription>);
+ void terminate_threads ();
+
+ /** Film that we are encoding */
+ boost::shared_ptr<const Film> _film;
+
+ EventHistory _history;
+
+ /** Mutex for _threads */
+ mutable boost::mutex _threads_mutex;
+ std::list<boost::thread *> _threads;
+ mutable boost::mutex _queue_mutex;
+ std::list<boost::shared_ptr<DCPVideo> > _queue;
+ /** condition to manage thread wakeups when we have nothing to do */
+ boost::condition _empty_condition;
+ /** condition to manage thread wakeups when we have too much to do */
+ boost::condition _full_condition;
+
+ boost::shared_ptr<Writer> _writer;
+ Waker _waker;
+
+ boost::shared_ptr<PlayerVideo> _last_player_video;
+ boost::optional<DCPTime> _last_player_video_time;
+
+ boost::signals2::scoped_connection _server_found_connection;
+};
+
+#endif
*/
#include "transcode_job.h"
-#include "dcp_transcoder.h"
+#include "dcp_encoder.h"
#include "upload_job.h"
#include "job_manager.h"
#include "film.h"
-#include "transcoder.h"
+#include "encoder.h"
#include "log.h"
#include "compose.hpp"
#include <iostream>
}
void
-TranscodeJob::set_transcoder (shared_ptr<Transcoder> t)
+TranscodeJob::set_encoder (shared_ptr<Encoder> e)
{
- _transcoder = t;
+ _encoder = e;
}
void
gettimeofday (&start, 0);
LOG_GENERAL_NC (N_("Transcode job starting"));
- DCPOMATIC_ASSERT (_transcoder);
- _transcoder->go ();
+ DCPOMATIC_ASSERT (_encoder);
+ _encoder->go ();
set_progress (1);
set_state (FINISHED_OK);
float fps = 0;
if (finish.tv_sec != start.tv_sec) {
- fps = _transcoder->frames_done() / (finish.tv_sec - start.tv_sec);
+ fps = _encoder->frames_done() / (finish.tv_sec - start.tv_sec);
}
LOG_GENERAL (N_("Transcode job completed successfully: %1 fps"), fps);
- _transcoder.reset ();
+ _encoder.reset ();
/* XXX: this shouldn't be here */
if (_film->upload_after_make_dcp ()) {
}
} catch (...) {
- _transcoder.reset ();
+ _encoder.reset ();
throw;
}
}
string
TranscodeJob::status () const
{
- if (!_transcoder) {
+ if (!_encoder) {
return Job::status ();
}
char buffer[256];
- if (finished() || _transcoder->finishing()) {
+ if (finished() || _encoder->finishing()) {
strncpy (buffer, Job::status().c_str(), 256);
} else {
snprintf (
buffer, sizeof(buffer), "%s; %" PRId64 "/%" PRId64 " frames",
Job::status().c_str(),
- _transcoder->frames_done(),
+ _encoder->frames_done(),
_film->length().frames_round (_film->video_frame_rate ())
);
- float const fps = _transcoder->current_rate ();
+ float const fps = _encoder->current_rate ();
if (fps) {
char fps_buffer[64];
/// TRANSLATORS: fps here is an abbreviation for frames per second
int
TranscodeJob::remaining_time () const
{
- /* _transcoder might be destroyed by the job-runner thread */
- shared_ptr<Transcoder> t = _transcoder;
+ /* _encoder might be destroyed by the job-runner thread */
+ shared_ptr<Encoder> e = _encoder;
- if (!t || t->finishing()) {
+ if (!e || e->finishing()) {
/* We aren't doing any actual encoding so just use the job's guess */
return Job::remaining_time ();
}
/* We're encoding so guess based on the current encoding rate */
- float fps = t->current_rate ();
+ float fps = e->current_rate ();
if (fps == 0) {
return 0;
}
/* Compute approximate proposed length here, as it's only here that we need it */
- return (_film->length().frames_round (_film->video_frame_rate ()) - t->frames_done()) / fps;
+ return (_film->length().frames_round (_film->video_frame_rate ()) - e->frames_done()) / fps;
}
#include "job.h"
#include <boost/shared_ptr.hpp>
-class Transcoder;
+class Encoder;
/** @class TranscodeJob
- * @brief A job which transcodes from one format to another.
+ * @brief A job which transcodes a Film to another format.
*/
class TranscodeJob : public Job
{
void run ();
std::string status () const;
- void set_transcoder (boost::shared_ptr<Transcoder> t);
+ void set_encoder (boost::shared_ptr<Encoder> t);
private:
int remaining_time () const;
- boost::shared_ptr<Transcoder> _transcoder;
+ boost::shared_ptr<Encoder> _encoder;
};
+++ /dev/null
-/*
- Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-/** @file src/transcoder.cc
- * @brief A class which takes a Film and some Options, then uses those to transcode the film.
- *
- * A decoder is selected according to the content type, and the encoder can be specified
- * as a parameter to the constructor.
- */
-
-#include "transcoder.h"
-#include "player.h"
-
-#include "i18n.h"
-
-using boost::weak_ptr;
-using boost::shared_ptr;
-
-/** Construct a transcoder.
- * @param film Film that we are transcoding.
- * @param job Job that this transcoder is being used in.
- */
-Transcoder::Transcoder (shared_ptr<const Film> film, weak_ptr<Job> job)
- : _film (film)
- , _job (job)
- , _player (new Player (film, film->playlist ()))
-{
- _player->Video.connect (bind (&Transcoder::video, this, _1, _2));
- _player->Audio.connect (bind (&Transcoder::audio, this, _1, _2));
- _player->Subtitle.connect (bind (&Transcoder::subtitle, this, _1, _2));
-}
+++ /dev/null
-/*
- Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef DCPOMATIC_TRANSCODER_H
-#define DCPOMATIC_TRANSCODER_H
-
-#include "types.h"
-#include "player_subtitles.h"
-#include <boost/weak_ptr.hpp>
-
-class Film;
-class Encoder;
-class Player;
-class Job;
-class PlayerVideo;
-class AudioBuffers;
-
-/** @class Transcoder */
-class Transcoder : public boost::noncopyable
-{
-public:
- Transcoder (boost::shared_ptr<const Film> film, boost::weak_ptr<Job> job);
- virtual ~Transcoder () {}
-
- virtual void go () = 0;
-
- /** @return the current frame rate over the last short while */
- virtual float current_rate () const = 0;
- /** @return the number of frames that are done */
- virtual Frame frames_done () const = 0;
- virtual bool finishing () const = 0;
-
-protected:
- virtual void video (boost::shared_ptr<PlayerVideo>, DCPTime) = 0;
- virtual void audio (boost::shared_ptr<AudioBuffers>, DCPTime) = 0;
- virtual void subtitle (PlayerSubtitles, DCPTimePeriod) = 0;
-
- boost::shared_ptr<const Film> _film;
- boost::weak_ptr<Job> _job;
- boost::shared_ptr<Player> _player;
-};
-
-#endif
dcp_content.cc
dcp_content_type.cc
dcp_decoder.cc
+ dcp_encoder.cc
dcp_examiner.cc
dcp_subtitle.cc
dcp_subtitle_content.cc
dcp_subtitle_decoder.cc
- dcp_transcoder.cc
dcp_video.cc
dcpomatic_socket.cc
dcpomatic_time.cc
ffmpeg_audio_stream.cc
ffmpeg_content.cc
ffmpeg_decoder.cc
+ ffmpeg_encoder.cc
ffmpeg_examiner.cc
ffmpeg_stream.cc
ffmpeg_subtitle_stream.cc
- ffmpeg_transcoder.cc
film.cc
filter.cc
font.cc
j2k_image_proxy.cc
job.cc
job_manager.cc
+ j2k_encoder.cc
json_server.cc
log.cc
log_entry.cc
text_subtitle_decoder.cc
timer.cc
transcode_job.cc
- transcoder.cc
types.cc
signal_manager.cc
update_checker.cc
#include "lib/dcpomatic_socket.h"
#include "lib/hints.h"
#include "lib/dcp_content.h"
-#include "lib/ffmpeg_transcoder.h"
+#include "lib/ffmpeg_encoder.h"
#include "lib/transcode_job.h"
#include <dcp/exceptions.h>
#include <dcp/raw_convert.h>
ExportDialog* d = new ExportDialog (this);
if (d->ShowModal() == wxID_OK) {
shared_ptr<TranscodeJob> job (new TranscodeJob (_film));
- shared_ptr<FFmpegTranscoder> tx (new FFmpegTranscoder (_film, job, d->path(), d->format()));
- job->set_transcoder (tx);
+ job->set_encoder (shared_ptr<FFmpegEncoder> (new FFmpegEncoder (_film, job, d->path(), d->format())));
JobManager::instance()->add (job);
}
d->Destroy ();
_("MP4 files (*.mp4)|*.mp4"),
};
-FFmpegTranscoder::Format formats[] = {
- FFmpegTranscoder::FORMAT_PRORES,
- FFmpegTranscoder::FORMAT_H264,
+FFmpegEncoder::Format formats[] = {
+ FFmpegEncoder::FORMAT_PRORES,
+ FFmpegEncoder::FORMAT_H264,
};
ExportDialog::ExportDialog (wxWindow* parent)
return wx_to_std (_file->GetPath ());
}
-FFmpegTranscoder::Format
+FFmpegEncoder::Format
ExportDialog::format () const
{
DCPOMATIC_ASSERT (_format->GetSelection() >= 0 && _format->GetSelection() < FORMATS);
*/
#include "table_dialog.h"
-#include "lib/ffmpeg_transcoder.h"
+#include "lib/ffmpeg_encoder.h"
#include <wx/wx.h>
#include <boost/filesystem.hpp>
ExportDialog (wxWindow* parent);
boost::filesystem::path path () const;
- FFmpegTranscoder::Format format () const;
+ FFmpegEncoder::Format format () const;
private:
void format_changed ();
--- /dev/null
+/*
+ Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "lib/ffmpeg_encoder.h"
+#include "lib/film.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/ratio.h"
+#include "lib/transcode_job.h"
+#include "test.h"
+#include <boost/test/unit_test.hpp>
+
+using boost::shared_ptr;
+
+BOOST_AUTO_TEST_CASE (ffmpeg_transcoder_basic_test)
+{
+ shared_ptr<Film> film = new_test_film ("ffmpeg_transcoder_basic_test");
+ film->set_name ("ffmpeg_transcoder_basic_test");
+ shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/test.mp4"));
+ film->set_container (Ratio::from_id ("185"));
+ film->set_audio_channels (6);
+
+ film->examine_and_add_content (c);
+ wait_for_jobs ();
+
+ shared_ptr<Job> job (new TranscodeJob (film));
+ FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_basic_test/test.mov", FFmpegEncoder::FORMAT_PRORES);
+ encoder.go ();
+}
+++ /dev/null
-/*
- Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "lib/ffmpeg_transcoder.h"
-#include "lib/film.h"
-#include "lib/ffmpeg_content.h"
-#include "lib/ratio.h"
-#include "lib/transcode_job.h"
-#include "test.h"
-#include <boost/test/unit_test.hpp>
-
-using boost::shared_ptr;
-
-BOOST_AUTO_TEST_CASE (ffmpeg_transcoder_basic_test)
-{
- shared_ptr<Film> film = new_test_film ("ffmpeg_transcoder_basic_test");
- film->set_name ("ffmpeg_transcoder_basic_test");
- shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/test.mp4"));
- film->set_container (Ratio::from_id ("185"));
- film->set_audio_channels (6);
-
- film->examine_and_add_content (c);
- wait_for_jobs ();
-
- shared_ptr<Job> job (new TranscodeJob (film));
- FFmpegTranscoder transcoder (film, job, "build/test/ffmpeg_transcoder_basic_test/test.mov", FFmpegTranscoder::FORMAT_PRORES);
- transcoder.go ();
-}
ffmpeg_dcp_test.cc
ffmpeg_decoder_seek_test.cc
ffmpeg_decoder_sequential_test.cc
+ ffmpeg_encoder_test.cc
ffmpeg_examiner_test.cc
ffmpeg_pts_offset_test.cc
- ffmpeg_transcoder_test.cc
file_group_test.cc
file_log_test.cc
file_naming_test.cc