Patch from Aaron.
[dcpomatic.git] / src / lib / j2k_encoder.cc
index d2e840f85f2a87ce647789321525fb5a14782bad..11f403d9524fe98aa6179c11f957965514874f14 100644 (file)
@@ -53,6 +53,7 @@ using boost::optional;
 using dcp::Data;
 using namespace dcpomatic;
 
+static grk_plugin::GrokInitializer grokInitializer;
 
 /** @param film Film that we are encoding.
  *  @param writer Writer that we are using.
@@ -60,7 +61,9 @@ using namespace dcpomatic;
 J2KEncoder::J2KEncoder(shared_ptr<const Film> film, Writer& writer)
        : _film (film)
        , _history (200)
-       , _writer (writer)
+       , _writer (writer) ,
+       dcpomaticContext_(film,writer,_history, Config::instance()->gpu_binary_location ()),
+       context_(Config::instance()->enable_gpu () ? new grk_plugin::GrokContext(dcpomaticContext_) : nullptr)
 {
        servers_list_changed ();
 }
@@ -70,10 +73,13 @@ J2KEncoder::~J2KEncoder ()
 {
        _server_found_connection.disconnect();
 
+       {
        boost::mutex::scoped_lock lm (_threads_mutex);
        terminate_threads ();
-}
+       }
 
+       delete context_;
+}
 
 void
 J2KEncoder::begin ()
@@ -83,23 +89,35 @@ J2KEncoder::begin ()
                );
 }
 
+void J2KEncoder::pause(void){
+       if (Config::instance()->enable_gpu ())
+               end(false);
+}
+
+void J2KEncoder::resume(void){
+       if (Config::instance()->enable_gpu ()) {
+               context_ = new grk_plugin::GrokContext(dcpomaticContext_);
+               servers_list_changed ();
+       }
+}
 
 void
-J2KEncoder::end ()
+J2KEncoder::end (bool isFinal)
 {
-       boost::mutex::scoped_lock lock (_queue_mutex);
+       if (isFinal) {
+               boost::mutex::scoped_lock lock (_queue_mutex);
 
-       LOG_GENERAL (N_("Clearing queue of %1"), _queue.size ());
+               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);
+               /* Keep waking workers until the queue is empty */
+                       while (!_queue.empty ()) {
+                               rethrow ();
+                               _empty_condition.notify_all ();
+                               _full_condition.wait (lock);
+                       }
+               lock.unlock ();
        }
 
-       lock.unlock ();
-
        LOG_GENERAL_NC (N_("Terminating encoder threads"));
 
        {
@@ -113,27 +131,38 @@ J2KEncoder::end ()
        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
+                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.
+                So just mop up anything left in the queue here.
        */
-
-       for (auto const& i: _queue) {
-               LOG_GENERAL(N_("Encode left-over frame %1"), i.index());
-               try {
-                       _writer.write(
-                               make_shared<dcp::ArrayData>(i.encode_locally()),
-                               i.index(),
-                               i.eyes()
-                               );
-                       frame_done ();
-               } catch (std::exception& e) {
-                       LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
+       if (isFinal) {
+               for (auto & i: _queue) {
+                       if (Config::instance()->enable_gpu ()) {
+                               if (!context_->scheduleCompress(i)){
+                                       LOG_GENERAL (N_("[%1] J2KEncoder thread pushes frame %2 back onto queue after failure"), thread_id(), i.index());
+                                       // handle error
+                               }
+                       }
+                       else {
+                               LOG_GENERAL(N_("Encode left-over frame %1"), i.index());
+                               try {
+                                       _writer.write(
+                                                       make_shared<dcp::ArrayData>(i.encode_locally()),
+                                               i.index(),
+                                               i.eyes()
+                                               );
+                                       frame_done ();
+                               } catch (std::exception& e) {
+                                       LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
+                               }
+                       }
                }
        }
+       delete context_;
+       context_ = nullptr;
 }
 
 
@@ -183,7 +212,10 @@ J2KEncoder::encode (shared_ptr<PlayerVideo> pv, DCPTime time)
        size_t threads = 0;
        {
                boost::mutex::scoped_lock lm (_threads_mutex);
-               threads = _threads->size();
+               if (_threads)
+                       threads = _threads->size();
+               else
+                       threads = std::thread::hardware_concurrency();
        }
 
        boost::mutex::scoped_lock queue_lock (_queue_mutex);
@@ -223,13 +255,14 @@ J2KEncoder::encode (shared_ptr<PlayerVideo> pv, DCPTime time)
                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 (DCPVideo(
+               auto dcpv = DCPVideo(
                                pv,
                                position,
                                _film->video_frame_rate(),
                                _film->j2k_bandwidth(),
                                _film->resolution()
-                               ));
+                               );
+               _queue.push_back (dcpv);
 
                /* The queue might not be empty any more, so notify anything which is
                   waiting on that.
@@ -269,6 +302,8 @@ void
 J2KEncoder::encoder_thread (optional<EncodeServerDescription> server)
 try
 {
+       auto config = Config::instance ();
+
        start_of_thread ("J2KEncoder");
 
        if (server) {
@@ -332,14 +367,22 @@ try
                                }
 
                        } else {
-                               try {
-                                       LOG_TIMING ("start-local-encode thread=%1 frame=%2", thread_id(), vf.index());
-                                       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 */
-                                       LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
-                                       throw;
+                               if (context_) {
+                                       if (!context_->launch(vf, config->selected_gpu()) || !context_->scheduleCompress(vf)) {
+                                               LOG_GENERAL (N_("[%1] J2KEncoder thread pushes frame %2 back onto queue after failure"), thread_id(), vf.index());
+                                               _queue.push_front (vf);
+                                       }
+
+                               } else {
+                                       try {
+                                               LOG_TIMING ("start-local-encode thread=%1 frame=%2", thread_id(), vf.index());
+                                               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 */
+                                               LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
+                                               throw;
+                                       }
                                }
                        }
 
@@ -347,10 +390,12 @@ try
                                _writer.write(encoded, 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 (!Config::instance()->enable_gpu ()) {
+                                       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 ();
+                               }
                        }
                }