diff options
| author | Carl Hetherington <cth@carlh.net> | 2020-08-13 14:55:20 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2020-09-13 20:23:29 +0200 |
| commit | 50e85cf64504b7fc38b4129aa750c2def28b95fd (patch) | |
| tree | 12d30de6149bc7fa39ab388397da128a7d426a44 /src/lib | |
| parent | dbb5577ff761cfd25f154fc54c2dc7e111a31c77 (diff) | |
Rearrange encoding so that the different methods / backends are not all crammed into DCPVideo.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/dcp_video.cc | 96 | ||||
| -rw-r--r-- | src/lib/dcp_video.h | 21 | ||||
| -rw-r--r-- | src/lib/encode_server.cc | 10 | ||||
| -rw-r--r-- | src/lib/encode_server.h | 3 | ||||
| -rw-r--r-- | src/lib/j2k_encoder.cc | 82 | ||||
| -rw-r--r-- | src/lib/j2k_encoder.h | 11 | ||||
| -rw-r--r-- | src/lib/j2k_encoder_backend.h | 19 | ||||
| -rw-r--r-- | src/lib/j2k_encoder_cpu_backend.cc | 56 | ||||
| -rw-r--r-- | src/lib/j2k_encoder_cpu_backend.h | 14 | ||||
| -rw-r--r-- | src/lib/j2k_encoder_remote_backend.cc | 108 | ||||
| -rw-r--r-- | src/lib/j2k_encoder_remote_backend.h | 16 | ||||
| -rw-r--r-- | src/lib/wscript | 4 |
12 files changed, 266 insertions, 174 deletions
diff --git a/src/lib/dcp_video.cc b/src/lib/dcp_video.cc index 440571b76..161f74c57 100644 --- a/src/lib/dcp_video.cc +++ b/src/lib/dcp_video.cc @@ -32,7 +32,6 @@ #include "config.h" #include "exceptions.h" #include "encode_server_description.h" -#include "dcpomatic_socket.h" #include "image.h" #include "log.h" #include "dcpomatic_log.h" @@ -48,7 +47,6 @@ DCPOMATIC_DISABLE_WARNINGS #include <libxml++/libxml++.h> DCPOMATIC_ENABLE_WARNINGS -#include <boost/asio.hpp> #include <boost/thread.hpp> #include <stdint.h> #include <iomanip> @@ -117,95 +115,6 @@ DCPVideo::convert_to_xyz (shared_ptr<const PlayerVideo> frame, dcp::NoteHandler return xyz; } -/** J2K-encode this frame on the local host. - * @return Encoded data. - */ -Data -DCPVideo::encode_locally () -{ - string const comment = Config::instance()->dcp_j2k_comment(); - - Data enc = dcp::compress_j2k ( - convert_to_xyz (_frame, boost::bind(&Log::dcp_log, dcpomatic_log.get(), _1, _2)), - _j2k_bandwidth, - _frames_per_second, - _frame->eyes() == EYES_LEFT || _frame->eyes() == EYES_RIGHT, - _resolution == RESOLUTION_4K, - comment.empty() ? "libdcp" : comment - ); - - switch (_frame->eyes()) { - case EYES_BOTH: - LOG_DEBUG_ENCODE (N_("Finished locally-encoded frame %1 for mono"), _index); - break; - case EYES_LEFT: - LOG_DEBUG_ENCODE (N_("Finished locally-encoded frame %1 for L"), _index); - break; - case EYES_RIGHT: - LOG_DEBUG_ENCODE (N_("Finished locally-encoded frame %1 for R"), _index); - break; - default: - break; - } - - return enc; -} - -/** Send this frame to a remote server for J2K encoding, then read the result. - * @param serv Server to send to. - * @param timeout timeout in seconds. - * @return Encoded data. - */ -Data -DCPVideo::encode_remotely (EncodeServerDescription serv, int timeout) -{ - boost::asio::io_service io_service; - boost::asio::ip::tcp::resolver resolver (io_service); - boost::asio::ip::tcp::resolver::query query (serv.host_name(), raw_convert<string> (ENCODE_FRAME_PORT)); - boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query); - - shared_ptr<Socket> socket (new Socket (timeout)); - - socket->connect (*endpoint_iterator); - - /* Collect all XML metadata */ - xmlpp::Document doc; - xmlpp::Element* root = doc.create_root_node ("EncodingRequest"); - root->add_child("Version")->add_child_text (raw_convert<string> (SERVER_LINK_VERSION)); - add_metadata (root); - - LOG_DEBUG_ENCODE (N_("Sending frame %1 to remote"), _index); - - { - Socket::WriteDigestScope ds (socket); - - /* Send XML metadata */ - string xml = doc.write_to_string ("UTF-8"); - socket->write (xml.length() + 1); - socket->write ((uint8_t *) xml.c_str(), xml.length() + 1); - - /* Send binary data */ - LOG_TIMING("start-remote-send thread=%1", thread_id ()); - _frame->write_to_socket (socket); - } - - /* Read the response (JPEG2000-encoded data); this blocks until the data - is ready and sent back. - */ - Socket::ReadDigestScope ds (socket); - LOG_TIMING("start-remote-encode thread=%1", thread_id ()); - Data e (socket->read_uint32 ()); - LOG_TIMING("start-remote-receive thread=%1", thread_id ()); - socket->read (e.data().get(), e.size()); - LOG_TIMING("finish-remote-receive thread=%1", thread_id ()); - if (!ds.check()) { - throw NetworkError ("Checksums do not match"); - } - - LOG_DEBUG_ENCODE (N_("Finished remotely-encoded frame %1"), _index); - - return e; -} void DCPVideo::add_metadata (xmlpp::Element* el) const @@ -217,11 +126,6 @@ DCPVideo::add_metadata (xmlpp::Element* el) const _frame->add_metadata (el); } -Eyes -DCPVideo::eyes () const -{ - return _frame->eyes (); -} /** @return true if this DCPVideo is definitely the same as another; * (apart from the frame index), false if it is probably not. diff --git a/src/lib/dcp_video.h b/src/lib/dcp_video.h index 81ddc4470..49a858733 100644 --- a/src/lib/dcp_video.h +++ b/src/lib/dcp_video.h @@ -45,23 +45,32 @@ public: DCPVideo (boost::shared_ptr<const PlayerVideo>, int, int, int, Resolution); DCPVideo (boost::shared_ptr<const PlayerVideo>, cxml::ConstNodePtr); - dcp::Data encode_locally (); - dcp::Data encode_remotely (EncodeServerDescription, int timeout = 30); + boost::shared_ptr<const PlayerVideo> frame () const { + return _frame; + } int index () const { return _index; } - Eyes eyes () const; + int frames_per_second () const { + return _frames_per_second; + } + + int j2k_bandwidth () const { + return _j2k_bandwidth; + } + + Resolution resolution () const { + return _resolution; + } bool same (boost::shared_ptr<const DCPVideo> other) const; + void add_metadata (xmlpp::Element *) const; static boost::shared_ptr<dcp::OpenJPEGImage> convert_to_xyz (boost::shared_ptr<const PlayerVideo> frame, dcp::NoteHandler note); private: - - void add_metadata (xmlpp::Element *) const; - boost::shared_ptr<const PlayerVideo> _frame; int _index; ///< frame index within the DCP's intrinsic duration int _frames_per_second; ///< Frames per second that we will use for the DCP diff --git a/src/lib/encode_server.cc b/src/lib/encode_server.cc index f4224798b..85f3ca7b0 100644 --- a/src/lib/encode_server.cc +++ b/src/lib/encode_server.cc @@ -141,11 +141,11 @@ EncodeServer::process (shared_ptr<Socket> socket, struct timeval& after_read, st throw NetworkError ("Checksums do not match"); } - DCPVideo dcp_video_frame (pvf, xml); + shared_ptr<DCPVideo> frame(new DCPVideo(pvf, xml)); gettimeofday (&after_read, 0); - Data encoded = dcp_video_frame.encode_locally (); + Data encoded = *_cpu_encoder.encode (frame); gettimeofday (&after_encode, 0); @@ -154,12 +154,12 @@ EncodeServer::process (shared_ptr<Socket> socket, struct timeval& after_read, st socket->write (encoded.size()); socket->write (encoded.data().get(), encoded.size()); } catch (std::exception& e) { - cerr << "Send failed; frame " << dcp_video_frame.index() << "\n"; - LOG_ERROR ("Send failed; frame %1", dcp_video_frame.index()); + cerr << "Send failed; frame " << frame->index() << "\n"; + LOG_ERROR ("Send failed; frame %1", frame->index()); throw; } - return dcp_video_frame.index (); + return frame->index (); } void diff --git a/src/lib/encode_server.h b/src/lib/encode_server.h index a43cea7ef..af1ead2bb 100644 --- a/src/lib/encode_server.h +++ b/src/lib/encode_server.h @@ -27,6 +27,7 @@ #include "server.h" #include "exception_store.h" +#include "j2k_encoder_cpu_backend.h" #include <boost/thread.hpp> #include <boost/asio.hpp> #include <boost/thread/condition.hpp> @@ -61,6 +62,8 @@ private: bool _verbose; int _num_threads; + J2KEncoderCPUBackend _cpu_encoder; + struct Broadcast { Broadcast () diff --git a/src/lib/j2k_encoder.cc b/src/lib/j2k_encoder.cc index 64cd38147..ef38aaa55 100644 --- a/src/lib/j2k_encoder.cc +++ b/src/lib/j2k_encoder.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -23,6 +23,9 @@ */ #include "j2k_encoder.h" +#include "j2k_encoder_backend.h" +#include "j2k_encoder_cpu_backend.h" +#include "j2k_encoder_remote_backend.h" #include "util.h" #include "film.h" #include "log.h" @@ -57,6 +60,7 @@ using namespace dcpomatic; J2KEncoder::J2KEncoder (shared_ptr<const Film> film, shared_ptr<Writer> writer) : _film (film) , _history (200) + , _cpu_backend (new J2KEncoderCPUBackend()) , _writer (writer) { servers_list_changed (); @@ -123,13 +127,13 @@ J2KEncoder::end () 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 ()); + BOOST_FOREACH (shared_ptr<DCPVideo> i, _queue) { + LOG_GENERAL (N_("Encode left-over frame %1"), i->index()); try { _writer->write ( - (*i)->encode_locally(), - (*i)->index(), - (*i)->eyes() + *_cpu_backend->encode(i), + i->index(), + i->frame()->eyes() ); frame_done (); } catch (std::exception& e) { @@ -258,20 +262,10 @@ J2KEncoder::terminate_threads () } void -J2KEncoder::encoder_thread (optional<EncodeServerDescription> server) +J2KEncoder::encoder_thread (shared_ptr<J2KEncoderBackend> backend) 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; + LOG_TIMING ("start-encoder-thread thread=%1", thread_id()); while (true) { @@ -291,50 +285,15 @@ try { boost::this_thread::disable_interruption dis; - LOG_TIMING ("encoder-pop thread=%1 frame=%2 eyes=%3", thread_id(), vf->index(), (int) vf->eyes ()); + LOG_TIMING ("encoder-pop thread=%1 frame=%2 eyes=%3", thread_id(), vf->index(), (int) vf->frame()->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 (); - 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; - } - } + optional<Data> encoded = backend->encode (vf); if (encoded) { - _writer->write (encoded.get(), vf->index (), vf->eyes ()); + _writer->write (encoded.get(), vf->index (), vf->frame()->eyes()); frame_done (); } else { lock.lock (); @@ -344,10 +303,6 @@ try } } - 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 (); @@ -375,10 +330,10 @@ J2KEncoder::servers_list_changed () 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>())); + boost::thread* t = _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, _cpu_backend)); pthread_setname_np (t->native_handle(), "encode-worker"); #else - _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, optional<EncodeServerDescription>())); + _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, _cpu_backend)); #endif } } @@ -389,8 +344,9 @@ J2KEncoder::servers_list_changed () } LOG_GENERAL (N_("Adding %1 worker threads for remote %2"), i.threads(), i.host_name ()); + shared_ptr<J2KEncoderRemoteBackend> backend (new J2KEncoderRemoteBackend(i)); for (int j = 0; j < i.threads(); ++j) { - _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, i)); + _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, backend)); } } diff --git a/src/lib/j2k_encoder.h b/src/lib/j2k_encoder.h index d56fc1aec..df23a5bae 100644 --- a/src/lib/j2k_encoder.h +++ b/src/lib/j2k_encoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -45,6 +45,10 @@ class DCPVideo; class Writer; class Job; class PlayerVideo; +class J2KEncoderBackend; +class J2KEncoderCPUBackend; +class J2KEncoderRemoteBackend; + /** @class J2KEncoder * @brief Class to manage encoding to J2K. @@ -79,16 +83,17 @@ private: void frame_done (); - void encoder_thread (boost::optional<EncodeServerDescription>); + void encoder_thread (boost::shared_ptr<J2KEncoderBackend> backend); void terminate_threads (); - /** Film that we are encoding */ boost::shared_ptr<const Film> _film; EventHistory _history; boost::shared_ptr<boost::thread_group> _threads; + boost::shared_ptr<J2KEncoderCPUBackend> _cpu_backend; + mutable boost::mutex _queue_mutex; std::list<boost::shared_ptr<DCPVideo> > _queue; /** condition to manage thread wakeups when we have nothing to do */ diff --git a/src/lib/j2k_encoder_backend.h b/src/lib/j2k_encoder_backend.h new file mode 100644 index 000000000..7688e5f15 --- /dev/null +++ b/src/lib/j2k_encoder_backend.h @@ -0,0 +1,19 @@ +#ifndef DCPOMATIC_J2K_ENCODER_BACKEND_H +#define DCPOMATIC_J2K_ENCODER_BACKEND_H + +#include <dcp/data.h> +#include <boost/noncopyable.hpp> +#include <boost/optional.hpp> +#include <boost/shared_ptr.hpp> + + +class DCPVideo; + + +class J2KEncoderBackend : public boost::noncopyable +{ +public: + virtual boost::optional<dcp::Data> encode (boost::shared_ptr<DCPVideo> video) = 0; +}; + +#endif diff --git a/src/lib/j2k_encoder_cpu_backend.cc b/src/lib/j2k_encoder_cpu_backend.cc new file mode 100644 index 000000000..732913c06 --- /dev/null +++ b/src/lib/j2k_encoder_cpu_backend.cc @@ -0,0 +1,56 @@ +#include "config.h" +#include "cross.h" +#include "dcp_video.h" +#include "dcpomatic_log.h" +#include "j2k_encoder_cpu_backend.h" +#include "player_video.h" +#include <dcp/j2k.h> + +#include "i18n.h" + + +using std::string; +using boost::optional; +using boost::shared_ptr; +using dcp::Data; + + +optional<Data> +J2KEncoderCPUBackend::encode (shared_ptr<DCPVideo> video) +{ + try { + LOG_TIMING ("start-local-encode thread=%1 frame=%2", thread_id(), video->index()); + string const comment = Config::instance()->dcp_j2k_comment(); + + Data enc = dcp::compress_j2k ( + DCPVideo::convert_to_xyz (video->frame(), boost::bind(&Log::dcp_log, dcpomatic_log.get(), _1, _2)), + video->j2k_bandwidth(), + video->frames_per_second(), + video->frame()->eyes() == EYES_LEFT || video->frame()->eyes() == EYES_RIGHT, + video->resolution() == RESOLUTION_4K, + comment.empty() ? "libdcp" : comment + ); + + switch (video->frame()->eyes()) { + case EYES_BOTH: + LOG_DEBUG_ENCODE (N_("Finished locally-encoded frame %1 for mono"), video->index()); + break; + case EYES_LEFT: + LOG_DEBUG_ENCODE (N_("Finished locally-encoded frame %1 for L"), video->index()); + break; + case EYES_RIGHT: + LOG_DEBUG_ENCODE (N_("Finished locally-encoded frame %1 for R"), video->index()); + break; + default: + break; + } + + LOG_TIMING ("finish-local-encode thread=%1 frame=%2", thread_id(), video->index()); + return enc; + } 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; + } +} + diff --git a/src/lib/j2k_encoder_cpu_backend.h b/src/lib/j2k_encoder_cpu_backend.h new file mode 100644 index 000000000..75d7c97a7 --- /dev/null +++ b/src/lib/j2k_encoder_cpu_backend.h @@ -0,0 +1,14 @@ +#ifndef DCPOMATIC_J2K_ENCODER_CPU_BACKEND_H +#define DCPOMATIC_J2K_ENCODER_CPU_BACKEND_H + + +#include "j2k_encoder_backend.h" + + +class J2KEncoderCPUBackend : public J2KEncoderBackend +{ +public: + boost::optional<dcp::Data> encode (boost::shared_ptr<DCPVideo> video); +}; + +#endif diff --git a/src/lib/j2k_encoder_remote_backend.cc b/src/lib/j2k_encoder_remote_backend.cc new file mode 100644 index 000000000..831134718 --- /dev/null +++ b/src/lib/j2k_encoder_remote_backend.cc @@ -0,0 +1,108 @@ +#include "config.h" +#include "cross.h" +#include "dcp_video.h" +#include "dcpomatic_log.h" +#include "dcpomatic_socket.h" +#include "encode_server_description.h" +#include "j2k_encoder_remote_backend.h" +#include "player_video.h" +#include "warnings.h" +#include <dcp/raw_convert.h> +DCPOMATIC_DISABLE_WARNINGS +#include <libxml++/libxml++.h> +DCPOMATIC_ENABLE_WARNINGS +#include <boost/asio.hpp> +#include <boost/thread.hpp> + +#include "i18n.h" + + +using std::string; +using boost::optional; +using boost::shared_ptr; +using dcp::Data; +using dcp::raw_convert; + + +J2KEncoderRemoteBackend::J2KEncoderRemoteBackend (EncodeServerDescription server) + : _server (server) + , _backoff (0) +{ + +} + + +optional<Data> +J2KEncoderRemoteBackend::encode (shared_ptr<DCPVideo> video) +{ + int const timeout = 30; + + try { + boost::asio::io_service io_service; + boost::asio::ip::tcp::resolver resolver (io_service); + boost::asio::ip::tcp::resolver::query query (_server.host_name(), raw_convert<string>(ENCODE_FRAME_PORT)); + boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query); + + shared_ptr<Socket> socket (new Socket(timeout)); + + socket->connect (*endpoint_iterator); + + /* Collect all XML metadata */ + xmlpp::Document doc; + xmlpp::Element* root = doc.create_root_node ("EncodingRequest"); + root->add_child("Version")->add_child_text(raw_convert<string> (SERVER_LINK_VERSION)); + video->add_metadata (root); + + LOG_DEBUG_ENCODE (N_("Sending frame %1 to remote"), video->index()); + + { + Socket::WriteDigestScope ds (socket); + + /* Send XML metadata */ + string xml = doc.write_to_string ("UTF-8"); + socket->write (xml.length() + 1); + socket->write ((uint8_t *) xml.c_str(), xml.length() + 1); + + /* Send binary data */ + LOG_TIMING("start-remote-send thread=%1", thread_id()); + video->frame()->write_to_socket(socket); + } + + /* Read the response (JPEG2000-encoded data); this blocks until the data + is ready and sent back. + */ + Socket::ReadDigestScope ds (socket); + LOG_TIMING("start-remote-encode thread=%1", thread_id()); + Data e (socket->read_uint32 ()); + LOG_TIMING("start-remote-receive thread=%1", thread_id()); + socket->read (e.data().get(), e.size()); + LOG_TIMING("finish-remote-receive thread=%1", thread_id()); + if (!ds.check()) { + throw NetworkError ("Checksums do not match"); + } + + LOG_DEBUG_ENCODE (N_("Finished remotely-encoded frame %1"), video->index()); + + if (_backoff > 0) { + LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", _server.host_name()); + } + + /* This job succeeded, so remove any backoff */ + _backoff = 0; + + return e; + + } catch (std::exception& e) { + if (_backoff < 60) { + _backoff += 10; + } + LOG_ERROR ( + N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"), + video->index(), _server.host_name(), e.what(), _backoff + ); + + boost::this_thread::sleep (boost::posix_time::seconds(_backoff)); + return optional<Data>(); + } +} + diff --git a/src/lib/j2k_encoder_remote_backend.h b/src/lib/j2k_encoder_remote_backend.h new file mode 100644 index 000000000..5e62cd9a9 --- /dev/null +++ b/src/lib/j2k_encoder_remote_backend.h @@ -0,0 +1,16 @@ +#include "encode_server_description.h" +#include "j2k_encoder_backend.h" + + +class J2KEncoderRemoteBackend : public J2KEncoderBackend +{ +public: + J2KEncoderRemoteBackend (EncodeServerDescription server); + boost::optional<dcp::Data> encode (boost::shared_ptr<DCPVideo> video); + +private: + EncodeServerDescription _server; + int _backoff; +}; + + diff --git a/src/lib/wscript b/src/lib/wscript index 3b14f8738..59cc145a2 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -125,9 +125,11 @@ sources = """ image_proxy.cc isdcf_metadata.cc j2k_image_proxy.cc + j2k_encoder.cc + j2k_encoder_cpu_backend.cc + j2k_encoder_remote_backend.cc job.cc job_manager.cc - j2k_encoder.cc json_server.cc kdm_with_metadata.cc kdm_recipient.cc |
