/*
- Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
+
/** @file src/j2k_encoder.cc
* @brief J2K encoder class.
*/
+
#include "j2k_encoder.h"
#include "util.h"
#include "film.h"
#include "encode_server_description.h"
#include "compose.hpp"
#include <libcxml/cxml.h>
-#include <boost/foreach.hpp>
#include <iostream>
#include "i18n.h"
+
using std::list;
using std::cout;
using std::exception;
-using boost::shared_ptr;
-using boost::weak_ptr;
+using std::shared_ptr;
+using std::weak_ptr;
+using std::make_shared;
using boost::optional;
using dcp::Data;
using namespace dcpomatic;
+
/** @param film Film that we are encoding.
* @param writer Writer that we are using.
*/
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.
- */
- }
+ boost::mutex::scoped_lock lm (_threads_mutex);
+ terminate_threads ();
}
+
void
J2KEncoder::begin ()
{
- weak_ptr<J2KEncoder> wp = shared_from_this ();
+ auto 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 ();
+ auto e = encoder.lock ();
if (e) {
e->servers_list_changed ();
}
}
+
void
J2KEncoder::end ()
{
LOG_GENERAL_NC (N_("Terminating encoder threads"));
- terminate_threads ();
+ {
+ boost::mutex::scoped_lock lm (_threads_mutex);
+ terminate_threads ();
+ }
/* Something might have been thrown during terminate_threads */
rethrow ();
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 ());
+ for (auto i: _queue) {
+ LOG_GENERAL(N_("Encode left-over frame %1"), i->index());
try {
_writer->write (
- (*i)->encode_locally(),
- (*i)->index(),
- (*i)->eyes()
+ make_shared<dcp::ArrayData>(i->encode_locally()),
+ i->index(),
+ i->eyes()
);
frame_done ();
} catch (std::exception& e) {
}
}
+
/** @return an estimate of the current number of frames we are encoding per second,
* if known.
*/
return _history.rate ();
}
+
/** @return Number of video frames that have been queued for encoding */
int
J2KEncoder::video_frames_enqueued () const
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
{
_waker.nudge ();
- size_t threads = _threads->size();
+ size_t threads = 0;
+ {
+ boost::mutex::scoped_lock lm (_threads_mutex);
+ threads = _threads->size();
+ }
boost::mutex::scoped_lock queue_lock (_queue_mutex);
*/
rethrow ();
- Frame const position = time.frames_floor(_film->video_frame_rate());
+ auto 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 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[pv->eyes()] && _writer->can_repeat(position) && pv->same (_last_player_video[pv->eyes()])) {
+ frame_done ();
+ } else if (_last_player_video[static_cast<int>(pv->eyes())] && _writer->can_repeat(position) && pv->same (_last_player_video[static_cast<int>(pv->eyes())])) {
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()
- )
- ));
+ _queue.push_back (make_shared<DCPVideo>(
+ pv,
+ position,
+ _film->video_frame_rate(),
+ _film->j2k_bandwidth(),
+ _film->resolution()
+ ));
/* The queue might not be empty any more, so notify anything which is
waiting on that.
_empty_condition.notify_all ();
}
- _last_player_video[pv->eyes()] = pv;
+ _last_player_video[static_cast<int>(pv->eyes())] = pv;
_last_player_video_time = time;
}
+
+/** Caller must hold a lock on _threads_mutex */
void
J2KEncoder::terminate_threads ()
{
+ boost::this_thread::disable_interruption dis;
+
if (!_threads) {
return;
}
_threads.reset ();
}
+
void
J2KEncoder::encoder_thread (optional<EncodeServerDescription> server)
try
{
+ start_of_thread ("J2KEncoder");
+
if (server) {
LOG_TIMING ("start-encoder-thread thread=%1 server=%2", thread_id (), server->host_name ());
} else {
}
LOG_TIMING ("encoder-wake thread=%1 queue=%2", thread_id(), _queue.size());
- shared_ptr<DCPVideo> vf = _queue.front ();
+ auto 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
lock.unlock ();
- optional<Data> encoded;
+ shared_ptr<Data> encoded;
/* We need to encode this input */
if (server) {
try {
- encoded = vf->encode_remotely (server.get ());
+ encoded = make_shared<dcp::ArrayData>(vf->encode_remotely(server.get()));
if (remote_backoff > 0) {
LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
} else {
try {
LOG_TIMING ("start-local-encode thread=%1 frame=%2", thread_id(), vf->index());
- encoded = vf->encode_locally ();
+ encoded = make_shared<dcp::ArrayData>(vf->encode_locally());
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 */
}
if (encoded) {
- _writer->write (encoded.get(), vf->index (), vf->eyes ());
+ _writer->write (encoded, vf->index(), vf->eyes());
frame_done ();
} else {
lock.lock ();
_full_condition.notify_all ();
}
+
void
J2KEncoder::servers_list_changed ()
{
+ boost::mutex::scoped_lock lm (_threads_mutex);
+
terminate_threads ();
- _threads.reset (new boost::thread_group());
+ _threads = make_shared<boost::thread_group>();
/* XXX: could re-use threads */
-#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) {
#ifdef DCPOMATIC_LINUX
- boost::thread* t = _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, optional<EncodeServerDescription>()));
+ auto t = _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, optional<EncodeServerDescription>()));
pthread_setname_np (t->native_handle(), "encode-worker");
-#endif
-#ifdef DCPOMATIC_OSX
+#else
_threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, optional<EncodeServerDescription>()));
-#endif
-#ifdef DCPOMATIC_WINDOWS
- boost::thread* t = _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, optional<EncodeServerDescription>()));
- if (windows_xp) {
- SetThreadAffinityMask (t->native_handle(), 1 << i);
- }
#endif
}
}
- BOOST_FOREACH (EncodeServerDescription i, EncodeServerFinder::instance()->servers()) {
+ for (auto i: EncodeServerFinder::instance()->servers()) {
if (!i.current_link_version()) {
continue;
}