2 Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/j2k_wav_encoder.cc
21 * @brief An encoder which writes JPEG2000 and WAV files.
28 #include <boost/thread.hpp>
29 #include <boost/filesystem.hpp>
32 #include "j2k_wav_encoder.h"
34 #include "film_state.h"
36 #include "exceptions.h"
37 #include "dcp_video_frame.h"
43 using namespace boost;
45 J2KWAVEncoder::J2KWAVEncoder (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
47 , _deinterleave_buffer_size (8192)
48 , _deinterleave_buffer (0)
49 , _process_end (false)
51 /* Create sound output files with .tmp suffixes; we will rename
52 them if and when we complete.
54 for (int i = 0; i < _fs->audio_channels; ++i) {
56 sf_info.samplerate = _fs->audio_sample_rate;
57 /* We write mono files */
59 sf_info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
60 SNDFILE* f = sf_open (_opt->multichannel_audio_out_path (i, true).c_str (), SFM_WRITE, &sf_info);
62 throw CreateFileError (_opt->multichannel_audio_out_path (i, true));
64 _sound_files.push_back (f);
67 /* Create buffer for deinterleaving audio */
68 _deinterleave_buffer = new uint8_t[_deinterleave_buffer_size];
71 J2KWAVEncoder::~J2KWAVEncoder ()
73 terminate_worker_threads ();
74 delete[] _deinterleave_buffer;
79 J2KWAVEncoder::terminate_worker_threads ()
81 boost::mutex::scoped_lock lock (_worker_mutex);
83 _worker_condition.notify_all ();
86 for (list<boost::thread *>::iterator i = _worker_threads.begin(); i != _worker_threads.end(); ++i) {
93 J2KWAVEncoder::close_sound_files ()
95 for (vector<SNDFILE*>::iterator i = _sound_files.begin(); i != _sound_files.end(); ++i) {
99 _sound_files.clear ();
103 J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame)
105 boost::mutex::scoped_lock lock (_worker_mutex);
107 /* Wait until the queue has gone down a bit */
108 while (_queue.size() >= _worker_threads.size() * 2 && !_process_end) {
109 _worker_condition.wait (lock);
116 /* Only do the processing if we don't already have a file for this frame */
117 if (!boost::filesystem::exists (_opt->frame_out_path (frame, false))) {
118 pair<string, string> const s = Filter::ffmpeg_strings (_fs->filters);
119 _queue.push_back (boost::shared_ptr<DCPVideoFrame> (
121 yuv, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second,
122 Config::instance()->colour_lut_index (), Config::instance()->j2k_bandwidth (),
127 _worker_condition.notify_all ();
132 J2KWAVEncoder::encoder_thread (Server* server)
134 /* Number of seconds that we currently wait between attempts
135 to connect to the server; not relevant for localhost
138 int remote_backoff = 0;
141 boost::mutex::scoped_lock lock (_worker_mutex);
142 while (_queue.empty () && !_process_end) {
143 _worker_condition.wait (lock);
150 boost::shared_ptr<DCPVideoFrame> vf = _queue.front ();
155 shared_ptr<EncodedData> encoded;
159 encoded = vf->encode_remotely (server);
161 if (remote_backoff > 0) {
163 s << server->host_name() << " was lost, but now she is found; removing backoff";
164 _log->log (s.str ());
167 /* This job succeeded, so remove any backoff */
170 } catch (std::exception& e) {
171 if (remote_backoff < 60) {
173 remote_backoff += 10;
176 s << "Remote encode on " << server->host_name() << " failed (" << e.what() << "); thread sleeping for " << remote_backoff << "s.";
177 _log->log (s.str ());
182 encoded = vf->encode_locally ();
183 } catch (std::exception& e) {
185 s << "Local encode failed " << e.what() << ".";
186 _log->log (s.str ());
191 encoded->write (_opt, vf->frame ());
195 _queue.push_front (vf);
199 if (remote_backoff > 0) {
200 sleep (remote_backoff);
204 _worker_condition.notify_all ();
209 J2KWAVEncoder::process_begin ()
211 for (int i = 0; i < Config::instance()->num_local_encoding_threads (); ++i) {
212 _worker_threads.push_back (new boost::thread (boost::bind (&J2KWAVEncoder::encoder_thread, this, (Server *) 0)));
215 vector<Server*> servers = Config::instance()->servers ();
217 for (vector<Server*>::iterator i = servers.begin(); i != servers.end(); ++i) {
218 for (int j = 0; j < (*i)->threads (); ++j) {
219 _worker_threads.push_back (new boost::thread (boost::bind (&J2KWAVEncoder::encoder_thread, this, *i)));
225 J2KWAVEncoder::process_end ()
227 boost::mutex::scoped_lock lock (_worker_mutex);
229 /* Keep waking workers until the queue is empty */
230 while (!_queue.empty ()) {
231 _worker_condition.notify_all ();
232 _worker_condition.wait (lock);
237 terminate_worker_threads ();
238 close_sound_files ();
240 /* Rename .wav.tmp files to .wav */
241 for (int i = 0; i < _fs->audio_channels; ++i) {
242 if (boost::filesystem::exists (_opt->multichannel_audio_out_path (i, false))) {
243 boost::filesystem::remove (_opt->multichannel_audio_out_path (i, false));
245 boost::filesystem::rename (_opt->multichannel_audio_out_path (i, true), _opt->multichannel_audio_out_path (i, false));
250 J2KWAVEncoder::process_audio (uint8_t* data, int data_size)
252 /* Size of a sample in bytes */
253 int const sample_size = 2;
255 /* XXX: we are assuming that sample_size is right, the _deinterleave_buffer_size is a multiple
256 of the sample size and that data_size is a multiple of _fs->audio_channels * sample_size.
259 /* XXX: this code is very tricksy and it must be possible to make it simpler ... */
261 /* Number of bytes left to read this time */
262 int remaining = data_size;
263 /* Our position in the output buffers, in bytes */
265 while (remaining > 0) {
266 /* How many bytes of the deinterleaved data to do this time */
267 int this_time = min (remaining / _fs->audio_channels, _deinterleave_buffer_size);
268 for (int i = 0; i < _fs->audio_channels; ++i) {
269 for (int j = 0; j < this_time; j += sample_size) {
270 for (int k = 0; k < sample_size; ++k) {
271 int const to = j + k;
272 int const from = position + (i * sample_size) + (j * _fs->audio_channels) + k;
273 _deinterleave_buffer[to] = data[from];
277 switch (_fs->audio_sample_format) {
278 case AV_SAMPLE_FMT_S16:
279 sf_write_short (_sound_files[i], (const short *) _deinterleave_buffer, this_time / sample_size);
282 throw DecodeError ("unknown audio sample format");
286 position += this_time;
287 remaining -= this_time * _fs->audio_channels;