DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
build=$DIR/../build
-export LD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH
export DYLD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:/Users/ci/osx-environment/x86_64/10.10/lib
export DCPOMATIC_GRAPHICS=$DIR/../graphics
binary=$build/src/tools/dcpomatic2
cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
-export LD_LIBRARY_PATH=build/src/lib:build/src:/home/c.hetherington/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=build/src/lib:build/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH
if [ "$1" == "--debug" ]; then
shift
gdb --args build/src/tools/dcpomatic2_cli "$@"
#!/bin/bash
-export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=build/src/lib:build/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH
export DYLD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:/Users/ci/osx-environment/x86_64/10.10/lib
export DCPOMATIC_GRAPHICS=graphics
if [ "$1" == "--debug" ]; then
# e.g. --run_tests=foo
if [ "$(uname)" == "Linux" ]; then
- export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH
+ export LD_LIBRARY_PATH=build/src/lib:build/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH
rm -f build/test/dcpomatic2_openssl
# This must be our patched openssl or tests will fail
if [ ! -f build/test/dcpomatic2_openssl ]; then
--- /dev/null
+/*
+ Copyright (C) 2021 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 "barrier.h"
+
+
+Barrier::Barrier (int count)
+ : _count(count)
+{
+
+}
+
+
+void
+Barrier::wait ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ ++_waiting;
+ if (_waiting >= _count) {
+ _condition.notify_all();
+ _waiting = 0;
+ } else {
+ _condition.wait(lm);
+ }
+}
+
+
+void
+Barrier::lower ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ _count = 0;
+ _condition.notify_all();
+}
+
--- /dev/null
+/*
+ Copyright (C) 2021 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 <boost/thread/condition.hpp>
+#include <boost/thread/mutex.hpp>
+
+
+class Barrier
+{
+public:
+ Barrier (int count);
+ Barrier (Barrier const&) = delete;
+ Barrier& operator= (Barrier const&) = delete;
+
+ void wait ();
+ void lower ();
+
+private:
+ boost::mutex _mutex;
+ boost::condition _condition;
+ int _count;
+ int _waiting = 0;
+};
+
+
void
Config::set_defaults ()
{
+ _encoding_backend = EncodingBackend::CPU;
_master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
_server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
_server_port_base = 6192;
backup ();
}
+ if (auto encoding_backend = f.optional_string_child("EncodingBackend")) {
+ _encoding_backend = *encoding_backend == "cpu" ? EncodingBackend::CPU : EncodingBackend::FASTVIDEO;
+ }
+
if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
_master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
} else {
/* [XML] Version The version number of the configuration file format. */
root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
+ root->add_child("EncodingBackend")->add_child_text(_encoding_backend == EncodingBackend::CPU ? "cpu" : "fastvideo");
/* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
/* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
class Config : public State
{
public:
+ enum class EncodingBackend {
+ CPU,
+ FASTVIDEO
+ };
+
+ EncodingBackend encoding_backend() const {
+ return _encoding_backend;
+ }
+
/** @return number of threads which a master DoM should use for J2K encoding on the local machine */
int master_encoding_threads () const {
return _master_encoding_threads;
/* SET (mostly) */
+ void set_encoding_backend (EncodingBackend backend) {
+ maybe_set (_encoding_backend, backend);
+ }
+
void set_master_encoding_threads (int n) {
maybe_set (_master_encoding_threads, n);
}
changed (prop);
}
+ EncodingBackend _encoding_backend;
/** number of threads which a master DoM should use for J2K encoding on the local machine */
int _master_encoding_threads;
/** number of threads which a server should use for J2K encoding on the local machine */
}
+
+#ifdef DCPOMATIC_FASTVIDEO
+FastvideoError::FastvideoError (string s, string message)
+ : runtime_error (String::compose("Fastvideo error in %1 (%2)", s, message))
+{
+
+}
+#endif
+
extern "C" {
#include <libavutil/pixfmt.h>
}
+#ifdef DCPOMATIC_FASTVIDEO
+#include <common/EnumToStringSdk.h>
+#endif
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>
#include <cstring>
};
+#ifdef DCPOMATIC_FASTVIDEO
+class FastvideoError : public std::runtime_error
+{
+public:
+ template <class T>
+ FastvideoError (std::string s, T code)
+ : std::runtime_error (String::compose("Fastvideo error in %1 (%2)", s, EnumToString(code)))
+ {}
+
+ FastvideoError (std::string s, std::string message);
+};
+#endif
+
+
#endif
#include "j2k_encoder_cpu_backend.h"
#include "j2k_encoder_remote_backend.h"
+#ifdef DCPOMATIC_FASTVIDEO
+#include "j2k_encoder_fastvideo_backend.h"
+#endif
#include "j2k_encoder.h"
#include "util.h"
#include "film.h"
*/
J2KEncoder::J2KEncoder (shared_ptr<const Film> film, shared_ptr<Writer> writer)
: _film (film)
+#ifdef DCPOMATIC_FASTVIDEO
+ , _gpu_barrier (Config::instance()->master_encoding_threads())
+#endif
, _history (200)
, _writer (writer)
{
LOG_GENERAL (N_("Clearing queue of %1"), _queue.size ());
+#ifdef DCPOMATIC_FASTVIDEO
+ _gpu_barrier.lower();
+#endif
+
/* Keep waking workers until the queue is empty */
while (!_queue.empty ()) {
rethrow ();
}
-void
-J2KEncoder::servers_list_changed ()
-try
+int
+J2KEncoder::create_cpu_threads ()
{
- boost::mutex::scoped_lock lm (_threads_mutex);
-
- terminate_threads ();
- _threads = make_shared<boost::thread_group>();
-
- _frames_in_parallel = 0;
-
- /* XXX: could re-use threads */
+ int parallel = 0;
- if (!Config::instance()->only_servers_encode ()) {
+ if (!Config::instance()->only_servers_encode()) {
auto backend = std::make_shared<J2KEncoderCPUBackend>();
for (int i = 0; i < Config::instance()->master_encoding_threads (); ++i) {
#ifdef DCPOMATIC_LINUX
#else
_threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, backend));
#endif
- _frames_in_parallel += backend->quantity();
+ parallel += backend->quantity();
}
}
LOG_GENERAL (N_("Adding %1 worker threads for remote %2"), i.threads(), i.host_name());
for (int j = 0; j < i.threads(); ++j) {
_threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, backend));
- _frames_in_parallel += backend->quantity();
+ parallel += backend->quantity();
}
}
- _writer->set_encoder_threads (_threads->size());
+ return parallel;
+}
+
+
+#ifdef DCPOMATIC_FASTVIDEO
+int
+J2KEncoder::create_fastvideo_threads ()
+{
+ int parallel = 0;
+ for (int i = 0; i < Config::instance()->master_encoding_threads(); ++i) {
+ auto backend = std::make_shared<J2KEncoderFastvideoBackend>(_gpu_barrier);
+ _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, backend));
+ parallel += backend->quantity();
+ }
+ return parallel;
+}
+#endif
+
+
+void
+J2KEncoder::servers_list_changed ()
+try
+{
+ boost::mutex::scoped_lock lm (_threads_mutex);
+
+ terminate_threads ();
+ _threads = make_shared<boost::thread_group>();
+
+ /* XXX: could re-use threads */
+
+#ifdef DCPOMATIC_FASTVIDEO
+ switch (Config::instance()->encoding_backend()) {
+ case Config::EncodingBackend::CPU:
+ _frames_in_parallel = create_cpu_threads();
+ break;
+ case Config::EncodingBackend::FASTVIDEO:
+ _frames_in_parallel = create_fastvideo_threads();
+ break;
+ }
+#else
+ _frames_in_parallel = create_cpu_threads();
+#endif
+
+ _writer->set_encoder_threads (_frames_in_parallel);
}
catch (...) {
terminate_threads ();
*/
+#include "barrier.h"
#include "cross.h"
#include "event_history.h"
#include "exception_store.h"
void frame_done ();
void encoder_thread (std::shared_ptr<J2KEncoderBackend> backend);
+ int create_cpu_threads ();
void terminate_threads ();
std::shared_ptr<const Film> _film;
+#ifdef DCPOMATIC_FASTVIDEO
+ int create_fastvideo_threads ();
+ Barrier _gpu_barrier;
+#endif
EventHistory _history;
class J2KEncoderCPUBackend : public J2KEncoderBackend
{
public:
- J2KEncoderCPUBackend () = default;
- J2KEncoderCPUBackend (J2KEncoderCPUBackend&& other) = default;
-
std::vector<dcp::ArrayData> encode (std::vector<DCPVideo> const& video) override;
};
--- /dev/null
+/*
+ Copyright (C) 2021 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 "barrier.h"
+#include "dcp_video.h"
+#include "dcpomatic_assert.h"
+#include "dcpomatic_log.h"
+#include "exceptions.h"
+#include "image.h"
+#include "j2k_encoder_fastvideo_backend.h"
+#include "player_video.h"
+#include <dcp/rgb_xyz.h>
+#include <fastvideo_encoder_j2k.h>
+#include <fastvideo_sdk.h>
+#include <common/EnumToStringSdk.h>
+
+
+using std::vector;
+using boost::optional;
+using dcp::ArrayData;
+#if BOOST_VERSION >= 106100
+using namespace boost::placeholders;
+#endif
+
+
+
+J2KEncoderFastvideoBackend::J2KEncoderFastvideoBackend (Barrier& barrier)
+ : _barrier (barrier)
+{
+ fastDeviceProperty* device_property;
+ int devices;
+ auto r = fastGetDevices(&device_property, &devices);
+ if (r != FAST_OK) {
+ throw FastvideoError ("GetDevices", r);
+ }
+
+ fastSdkParametersHandle_t sdk_parameters;
+ r = fastGetSdkParametersHandle(&sdk_parameters);
+ if (r != FAST_OK) {
+ throw FastvideoError ("GetSdkParametersHandle", r);
+ }
+ r = fastEncoderJ2kLibraryInit(sdk_parameters);
+ if (r != FAST_OK) {
+ throw FastvideoError ("DecoderJ2kLibraryInit", r);
+ }
+ fastTraceCreate("/home/carl/trace.log");
+}
+
+
+J2KEncoderFastvideoBackend::~J2KEncoderFastvideoBackend ()
+{
+ if (_setup_done) {
+ fastFree (_xyz_buffer);
+ fastEncoderJ2kDestroy (_encoder);
+ fastImportFromHostDestroy (_adapter);
+ }
+}
+
+
+void
+J2KEncoderFastvideoBackend::setup (dcp::Size size)
+{
+ auto r = fastImportFromHostCreate(
+ &_adapter, FAST_RGB12, size.width, size.height, &_src_buffer
+ );
+ if (r != FAST_OK) {
+ throw FastvideoError ("ImportFromHostCreate", r);
+ }
+
+ fastEncoderJ2kStaticParameters_t parameters;
+ parameters.lossless = false;
+ parameters.pcrdEnabled = true;
+ parameters.dwtLevels = 6;
+ parameters.codeblockSize = 32;
+ parameters.maxQuality = 1.5;
+ parameters.compressionRatio = 1;
+ parameters.info = false;
+ parameters.tier2Threads = 4;
+ parameters.tileWidth = size.width;
+ parameters.tileHeight = size.height;
+ parameters.noMCT = false;
+ parameters.ss1_x = 1;
+ parameters.ss1_y = 1;
+ parameters.ss2_x = 1;
+ parameters.ss2_y = 1;
+ parameters.ss3_x = 1;
+ parameters.ss3_y = 1;
+ parameters.yuvSubsampledFormat = false;
+
+ r = fastEncoderJ2kCreate(
+ &_encoder, ¶meters, FAST_RGB12, size.width, size.height, quantity(), _src_buffer
+ );
+ if (r != FAST_OK) {
+ throw FastvideoError ("EncoderJ2kCreate", r);
+ }
+
+ bool success = false;
+
+ r = fastEncoderJ2kIsInitialized(_encoder, &success);
+ if (r != FAST_OK || !success) {
+ throw FastvideoError ("EncoderJ2kIsInitialized", r);
+ }
+
+ _xyz_buffer_stride = size.width * 6;
+ _xyz_buffer_stride += 4 - (_xyz_buffer_stride % 4);
+ r = fastMalloc(reinterpret_cast<void**>(&_xyz_buffer), _xyz_buffer_stride * size.height);
+ if (r != FAST_OK) {
+ throw FastvideoError ("Malloc", r);
+ }
+}
+
+
+vector<ArrayData>
+J2KEncoderFastvideoBackend::encode (vector<DCPVideo> const& video)
+{
+ DCPOMATIC_ASSERT (static_cast<int>(video.size()) <= quantity());
+ _barrier.wait();
+
+ if (!_setup_done) {
+ setup (video.front().frame()->out_size());
+ _setup_done = true;
+ }
+
+ for (auto const& i: video) {
+ auto image = i.frame()->image(boost::bind(&PlayerVideo::keep_xyz_or_rgb, _1), VideoRange::FULL, false);
+ if (i.frame()->colour_conversion()) {
+ dcp::rgb_to_xyz (
+ image->data()[0],
+ image->size(),
+ image->stride()[0],
+ _xyz_buffer,
+ _xyz_buffer_stride,
+ i.frame()->colour_conversion().get()
+ );
+ } else {
+ /* XXX */
+ }
+
+ auto r = fastImportFromHostCopy(
+ _adapter,
+ _xyz_buffer,
+ image->size().width,
+ _xyz_buffer_stride,
+ image->size().height
+ );
+ if (r != FAST_OK) {
+ throw FastvideoError ("ImportFromHostCopy", r);
+ }
+
+ fastEncoderJ2kDynamicParameters_t dynamic_parameters;
+ dynamic_parameters.targetStreamSize = i.j2k_bandwidth() / (8 * i.frames_per_second());
+ dynamic_parameters.quality = 1.0;
+ dynamic_parameters.writeHeader = false;
+
+ r = fastEncoderJ2kAddImageToBatch(
+ _encoder,
+ &dynamic_parameters,
+ image->size().width,
+ image->size().height
+ );
+ if (r != FAST_OK) {
+ throw FastvideoError ("EncoderJ2kAddImageToBatch", r);
+ }
+ }
+
+
+ fastEncoderJ2kReport_t report;
+ fastEncoderJ2kOutput_t output;
+ int constexpr max_j2k_size = 1024 * 1024 * 2;
+ output.bufferSize = max_j2k_size;
+ dcp::ArrayData data(output.bufferSize);
+ output.byteStream = data.data();
+
+ auto r = fastEncoderJ2kTransformBatch(_encoder, &output, &report);
+ if (r != FAST_OK) {
+ fastTraceClose();
+ throw FastvideoError ("EncoderJ2KTransformBatch", r);
+ }
+
+ vector<dcp::ArrayData> encoded;
+ for (size_t i = 0; i < video.size(); ++i) {
+ data.set_size (output.streamSize);
+ encoded.push_back (data);
+ data = dcp::ArrayData(output.bufferSize);
+ output.byteStream = data.data();
+ int images_left = 0;
+ r = fastEncoderJ2kGetNextEncodedImage (_encoder, &output, &report, &images_left);
+ }
+
+ return encoded;
+}
+
+
--- /dev/null
+/*
+ Copyright (C) 2021 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_FASTVIDEO_BACKEND_H
+#define DCPOMATIC_J2K_ENCODER_FASTVIDEO_BACKEND_H
+
+
+#include "j2k_encoder_backend.h"
+#include <dcp/util.h>
+#include <fastvideo_encoder_j2k.h>
+#include <fastvideo_sdk.h>
+#include <boost/thread.hpp>
+
+
+class Barrier;
+
+
+class J2KEncoderFastvideoBackend : public J2KEncoderBackend
+{
+public:
+ J2KEncoderFastvideoBackend (Barrier& barrier);
+ ~J2KEncoderFastvideoBackend ();
+
+ std::vector<dcp::ArrayData> encode (std::vector<DCPVideo> const& video) override;
+
+ int quantity () const override {
+ return 1;
+ }
+
+private:
+ void setup (dcp::Size size);
+
+ Barrier& _barrier;
+ bool _setup_done = false;
+ fastImportFromHostHandle_t _adapter;
+ fastDeviceSurfaceBufferHandle_t _src_buffer;
+ fastEncoderJ2kHandle_t _encoder;
+ uint16_t* _xyz_buffer;
+ int _xyz_buffer_stride;
+};
+
+
+#endif
+
+
using std::make_shared;
-using std::shared_ptr;
using std::string;
-using std::unique_ptr;
using std::vector;
-using boost::optional;
using dcp::raw_convert;
-J2KEncoderRemoteBackend::J2KEncoderRemoteBackend (J2KEncoderRemoteBackend&& other)
- : _server (other._server)
- , _backoff (other._backoff)
-{
-
-}
-
-
vector<dcp::ArrayData>
J2KEncoderRemoteBackend::encode (vector<DCPVideo> const& all_video)
{
, _timeout (timeout)
{}
- J2KEncoderRemoteBackend (J2KEncoderRemoteBackend&& other);
-
std::vector<dcp::ArrayData> encode (std::vector<DCPVideo> const& video) override;
private:
audio_processor.cc
audio_ring_buffers.cc
audio_stream.cc
+ barrier.cc
butler.cc
text_content.cc
text_decoder.cc
obj.source += ' cross_linux.cc'
if bld.env.STATIC_DCPOMATIC:
obj.uselib += ' XMLPP'
+ if bld.env.ENABLE_FASTVIDEO:
+ obj.uselib += ' FASTVIDEO'
+ obj.source += ' j2k_encoder_fastvideo_backend.cc'
obj.target = 'dcpomatic2'
uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE WINSOCK2 OLE32 DSOUND WINMM KSUSER SETUPAPI '
if bld.env.TARGET_LINUX:
uselib += 'DL '
+ if bld.env.ENABLE_FASTVIDEO:
+ uselib += ' FASTVIDEO'
cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create']
if bld.env.ENABLE_DISK and not bld.env.DISABLE_GUI:
int r = 0;
add_language_controls (table, r);
+#ifdef DCPOMATIC_FASTVIDEO
+ add_label_to_sizer (table, _panel, _("Encode using"), true, wxGBPosition(r, 0));
+ _encoding_backend = new wxChoice (_panel, wxID_ANY);
+ table->Add (_encoding_backend, wxGBPosition(r, 1));
+ ++r;
+#endif
+
add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
_master_encoding_threads = new wxSpinCtrl (_panel);
table->Add (_master_encoding_threads, wxGBPosition (r, 1));
add_update_controls (table, r);
+#ifdef DCPOMATIC_FASTVIDEO
+ _encoding_backend->Append ("CPU");
+ _encoding_backend->Append ("GPU");
+ _encoding_backend->Bind (wxEVT_CHOICE, boost::bind(&FullGeneralPage::encoding_backend_changed, this));
+#endif
_config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this));
_cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
{
auto config = Config::instance ();
+#ifdef DCPOMATIC_FASTVIDEO
+ checked_set (_encoding_backend, Config::instance()->encoding_backend() == Config::EncodingBackend::CPU ? 0 : 1);
+#endif
+
checked_set (_master_encoding_threads, config->master_encoding_threads ());
checked_set (_server_encoding_threads, config->server_encoding_threads ());
#ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
GeneralPage::config_changed ();
}
+ void encoding_backend_changed ()
+ {
+ Config::instance()->set_encoding_backend(_encoding_backend->GetSelection() == 0 ? Config::EncodingBackend::CPU : Config::EncodingBackend::FASTVIDEO);
+ }
+
void export_cinemas_file ()
{
auto d = new wxFileDialog (
Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
}
+#ifdef DCPOMATIC_FASTVIDEO
+ wxChoice* _encoding_backend;
+#endif
wxSpinCtrl* _master_encoding_threads;
wxSpinCtrl* _server_encoding_threads;
FilePickerCtrl* _config_file;
obj.uselib += 'WINSOCK2 OLE32 DSOUND WINMM KSUSER GL GLU GLEW '
if bld.env.TARGET_OSX:
obj.framework = ['CoreAudio', 'OpenGL']
+ if bld.env.ENABLE_FASTVIDEO:
+ obj.uselib += ' FASTVIDEO'
obj.use = 'libdcpomatic2'
obj.source = sources
obj.target = 'dcpomatic2-wx'
--- /dev/null
+/*
+ Copyright (C) 2021 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/barrier.h"
+#include "lib/cross.h"
+#include <boost/test/unit_test.hpp>
+#include <boost/thread/thread.hpp>
+
+
+BOOST_AUTO_TEST_CASE (barrier_test)
+{
+ int constexpr num_threads = 128;
+
+ Barrier barrier (num_threads);
+
+ boost::mutex mutex;
+ int count = 0;
+
+ auto thread_body = [&barrier, &mutex, &count]() {
+ barrier.wait();
+ boost::mutex::scoped_lock lm(mutex);
+ ++count;
+ };
+
+ std::vector<boost::thread*> threads;
+ for (int i = 0; i < num_threads - 1; ++i) {
+ threads.push_back(new boost::thread(thread_body));
+ }
+
+ dcpomatic_sleep_seconds(5);
+
+ BOOST_CHECK_EQUAL (count, 0);
+
+ threads.push_back(new boost::thread(thread_body));
+ dcpomatic_sleep_seconds(5);
+
+ BOOST_CHECK_EQUAL (count, num_threads);
+
+ for (auto i: threads) {
+ i->join();
+ delete i;
+ }
+}
+
+
+BOOST_AUTO_TEST_CASE (barrier_test_lower)
+{
+ int constexpr num_threads = 128;
+
+ Barrier barrier (num_threads);
+
+ boost::mutex mutex;
+ int count = 0;
+
+ auto thread_body = [&barrier, &mutex, &count]() {
+ barrier.wait();
+ boost::mutex::scoped_lock lm(mutex);
+ ++count;
+ };
+
+ std::vector<boost::thread*> threads;
+ for (int i = 0; i < num_threads / 2; ++i) {
+ threads.push_back(new boost::thread(thread_body));
+ }
+
+ dcpomatic_sleep_seconds(5);
+
+ BOOST_CHECK_EQUAL (count, 0);
+
+ barrier.lower();
+ dcpomatic_sleep_seconds(5);
+
+ BOOST_CHECK_EQUAL (count, num_threads / 2);
+
+ for (auto i: threads) {
+ i->join();
+ delete i;
+ }
+}
+
--- /dev/null
+/*
+ Copyright (C) 2021 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/barrier.h"
+#include "lib/colour_conversion.h"
+#include "lib/dcp_video.h"
+#include "lib/ffmpeg_image_proxy.h"
+#include "lib/player_video.h"
+#include "lib/j2k_encoder_fastvideo_backend.h"
+#include "test.h"
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_AUTO_TEST_CASE (fastvideo_frame_size_test)
+{
+ auto test = [](int j2k_bandwidth) {
+ Barrier barrier (1);
+ J2KEncoderFastvideoBackend backend (barrier);
+
+ auto image_proxy = std::make_shared<FFmpegImageProxy>(TestPaths::private_data() / "prophet_frame.tiff");
+ auto player_video = std::make_shared<PlayerVideo> (
+ image_proxy,
+ Crop(),
+ boost::optional<double>(),
+ dcp::Size(1998, 1080),
+ dcp::Size(1998, 1080),
+ Eyes::BOTH,
+ Part::WHOLE,
+ PresetColourConversion::from_id("rec709").conversion,
+ VideoRange::FULL,
+ std::weak_ptr<Content>(),
+ boost::optional<Frame>(),
+ false
+ );
+
+ DCPVideo dcp_video (
+ player_video,
+ 0,
+ 24,
+ j2k_bandwidth,
+ Resolution::TWO_K
+ );
+
+ auto result = backend.encode ({dcp_video});
+ return result[0].size() * 8L * 24 / 1000000.0f;
+ };
+
+ auto bandwidth = std::vector<int>{ 50, 100, 150, 200, 250 };
+
+ for (auto b: bandwidth) {
+ auto mbps = test(b * 1000000.0f);
+ BOOST_CHECK_CLOSE (mbps, b, 5);
+ }
+}
+
obj.uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE '
if bld.env.TARGET_LINUX:
obj.uselib += 'DL '
+ if bld.env.ENABLE_FASTVIDEO:
+ obj.uselib += ' FASTVIDEO'
obj.use = 'libdcpomatic2'
obj.source = """
4k_test.cc
audio_processor_test.cc
audio_processor_delay_test.cc
audio_ring_buffers_test.cc
+ barrier_test.cc
butler_test.cc
cinema_sound_processor_test.cc
client_server_test.cc
obj.source += " disk_writer_test.cc"
obj.uselib += "LWEXT4 NANOMSG "
+ if bld.env.ENABLE_FASTVIDEO:
+ obj.source += " fastvideo_test.cc"
+
# Some difference in font rendering between the test machine and others...
# burnt_subtitle_test.cc
# This one doesn't check anything
opt.add_option('--enable-disk', action='store_true', default=False, help='build dcpomatic2_disk tool; requires Boost process, lwext4 and nanomsg libraries')
opt.add_option('--warnings-are-errors', action='store_true', default=False, help='build with -Werror')
opt.add_option('--wx-config', help='path to wx-config')
+ opt.add_option('--fastvideo-sdk', help='path to fastvideo SDK (containing fastvideo_sdk directory)')
def configure(conf):
conf.load('compiler_cxx')
conf.env.DEBUG = conf.options.enable_debug
conf.env.STATIC_DCPOMATIC = conf.options.static_dcpomatic
conf.env.ENABLE_DISK = conf.options.enable_disk
+ conf.env.ENABLE_FASTVIDEO = conf.options.fastvideo_sdk is not None
if conf.options.destdir == '':
conf.env.INSTALL_PREFIX = conf.options.prefix
else:
lib=deps,
uselib_store='BOOST_PROCESS')
+ # fastvideo
+ if conf.options.fastvideo_sdk is not None:
+ conf.env.INCLUDES_FASTVIDEO = [
+ os.path.join(conf.options.fastvideo_sdk),
+ os.path.join(conf.options.fastvideo_sdk, "core_samples"),
+ os.path.join(conf.options.fastvideo_sdk, "fastvideo_sdk", "inc")]
+ conf.env.LIBPATH_FASTVIDEO = [ os.path.join(conf.options.fastvideo_sdk, "fastvideo_sdk", "lib") ]
+ conf.env.LIB_FASTVIDEO = [ 'fastvideo_sdk', 'fastvideo_decoder_j2k', 'fastvideo_encoder_j2k', 'cuda', 'cudart', 'omp5' ]
+ conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_FASTVIDEO')
+
+
# Other stuff
conf.find_program('msgfmt', var='MSGFMT')