-/** @file src/encoder.h
- * @brief Parent class for classes which can encode video and audio frames.
- */
-
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <boost/lexical_cast.hpp>
-#include <libdcp/picture_asset.h>
-#include "encoder.h"
-#include "util.h"
-#include "options.h"
-#include "film.h"
-#include "log.h"
-#include "exceptions.h"
-#include "filter.h"
-#include "config.h"
-#include "dcp_video_frame.h"
-#include "server.h"
-#include "format.h"
-#include "cross.h"
-#include "writer.h"
-
-using std::pair;
-using std::string;
-using std::stringstream;
-using std::vector;
-using std::list;
-using std::cout;
-using std::make_pair;
-using namespace boost;
-
-int const Encoder::_history_size = 25;
-
-/** @param f Film that we are encoding */
-Encoder::Encoder (shared_ptr<Film> f)
- : _film (f)
- , _video_frames_in (0)
- , _video_frames_out (0)
-#ifdef HAVE_SWRESAMPLE
- , _swr_context (0)
-#endif
- , _have_a_real_frame (false)
- , _terminate (false)
-{
-
-}
-
-Encoder::~Encoder ()
-{
- terminate_threads ();
- if (_writer) {
- _writer->finish ();
- }
-}
-
-void
-Encoder::process_begin ()
-{
- if (_film->audio_stream() && _film->audio_stream()->sample_rate() != _film->target_audio_sample_rate()) {
-#ifdef HAVE_SWRESAMPLE
-
- stringstream s;
- s << "Will resample audio from " << _film->audio_stream()->sample_rate() << " to " << _film->target_audio_sample_rate();
- _film->log()->log (s.str ());
-
- /* We will be using planar float data when we call the resampler */
- _swr_context = swr_alloc_set_opts (
- 0,
- _film->audio_stream()->channel_layout(),
- AV_SAMPLE_FMT_FLTP,
- _film->target_audio_sample_rate(),
- _film->audio_stream()->channel_layout(),
- AV_SAMPLE_FMT_FLTP,
- _film->audio_stream()->sample_rate(),
- 0, 0
- );
-
- swr_init (_swr_context);
-#else
- throw EncodeError ("Cannot resample audio as libswresample is not present");
-#endif
- } else {
-#ifdef HAVE_SWRESAMPLE
- _swr_context = 0;
-#endif
- }
-
- for (int i = 0; i < Config::instance()->num_local_encoding_threads (); ++i) {
- _threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, (ServerDescription *) 0)));
- }
-
- vector<ServerDescription*> servers = Config::instance()->servers ();
-
- for (vector<ServerDescription*>::iterator i = servers.begin(); i != servers.end(); ++i) {
- for (int j = 0; j < (*i)->threads (); ++j) {
- _threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, *i)));
- }
- }
-
- _writer.reset (new Writer (_film));
-}
-
-
-void
-Encoder::process_end ()
-{
-#if HAVE_SWRESAMPLE
- if (_film->audio_stream() && _film->audio_stream()->channels() && _swr_context) {
-
- shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_stream()->channels(), 256));
-
- while (1) {
- int const frames = swr_convert (_swr_context, (uint8_t **) out->data(), 256, 0, 0);
-
- if (frames < 0) {
- throw EncodeError ("could not run sample-rate converter");
- }
-
- if (frames == 0) {
- break;
- }
-
- out->set_frames (frames);
- write_audio (out);
- }
-
- swr_free (&_swr_context);
- }
-#endif
-
- boost::mutex::scoped_lock lock (_mutex);
-
- _film->log()->log ("Clearing queue of " + lexical_cast<string> (_queue.size ()));
-
- /* Keep waking workers until the queue is empty */
- while (!_queue.empty ()) {
- _film->log()->log ("Waking with " + lexical_cast<string> (_queue.size ()), Log::VERBOSE);
- _condition.notify_all ();
- _condition.wait (lock);
- }
-
- lock.unlock ();
-
- terminate_threads ();
-
- _film->log()->log ("Mopping up " + lexical_cast<string> (_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<DCPVideoFrame> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
- _film->log()->log (String::compose ("Encode left-over frame %1", (*i)->frame ()));
- try {
- _writer->write ((*i)->encode_locally(), (*i)->frame ());
- frame_done ();
- } catch (std::exception& e) {
- _film->log()->log (String::compose ("Local encode failed (%1)", e.what ()));
- }
- }
-
- _writer->finish ();
- _writer.reset ();
-}
-
-/** @return an estimate of the current number of frames we are encoding per second,
- * or 0 if not known.
- */
-float
-Encoder::current_frames_per_second () const
-{
- boost::mutex::scoped_lock lock (_history_mutex);
- if (int (_time_history.size()) < _history_size) {
- return 0;
- }
-
- struct timeval now;
- gettimeofday (&now, 0);