X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fj2k_wav_encoder.cc;h=24163940078f0d36bd78302c271ddf4a9349690b;hb=cca887136613e3bf482fc520ed1b6d0a9ffbb6d5;hp=e6a1b285e1ba4d4e01af731f115b28bf043c7f60;hpb=bb767c7e338414beee132af3e96829c1448e214b;p=dcpomatic.git diff --git a/src/lib/j2k_wav_encoder.cc b/src/lib/j2k_wav_encoder.cc index e6a1b285e..241639400 100644 --- a/src/lib/j2k_wav_encoder.cc +++ b/src/lib/j2k_wav_encoder.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include "j2k_wav_encoder.h" @@ -38,12 +39,16 @@ #include "server.h" #include "filter.h" #include "log.h" +#include "cross.h" using namespace std; using namespace boost; J2KWAVEncoder::J2KWAVEncoder (shared_ptr s, shared_ptr o, Log* l) : Encoder (s, o, l) +#ifdef HAVE_SWRESAMPLE + , _swr_context (0) +#endif , _deinterleave_buffer_size (8192) , _deinterleave_buffer (0) , _process_end (false) @@ -53,7 +58,7 @@ J2KWAVEncoder::J2KWAVEncoder (shared_ptr s, shared_ptraudio_channels; ++i) { SF_INFO sf_info; - sf_info.samplerate = _fs->audio_sample_rate; + sf_info.samplerate = dcp_audio_sample_rate (_fs->audio_sample_rate); /* We write mono files */ sf_info.channels = 1; sf_info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24; @@ -125,11 +130,13 @@ J2KWAVEncoder::process_video (shared_ptr yuv, int frame) )); _worker_condition.notify_all (); + } else { + frame_skipped (); } } void -J2KWAVEncoder::encoder_thread (Server* server) +J2KWAVEncoder::encoder_thread (ServerDescription* server) { /* Number of seconds that we currently wait between attempts to connect to the server; not relevant for localhost @@ -173,7 +180,7 @@ J2KWAVEncoder::encoder_thread (Server* server) remote_backoff += 10; } stringstream s; - s << "Remote encode on " << server->host_name() << " failed (" << e.what() << "); thread sleeping for " << remote_backoff << "s."; + s << "Remote encode of " << vf->frame() << " on " << server->host_name() << " failed (" << e.what() << "); thread sleeping for " << remote_backoff << "s."; _log->log (s.str ()); } @@ -189,7 +196,7 @@ J2KWAVEncoder::encoder_thread (Server* server) if (encoded) { encoded->write (_opt, vf->frame ()); - frame_done (); + frame_done (vf->frame ()); } else { lock.lock (); _queue.push_front (vf); @@ -197,7 +204,7 @@ J2KWAVEncoder::encoder_thread (Server* server) } if (remote_backoff > 0) { - sleep (remote_backoff); + dvdomatic_sleep (remote_backoff); } lock.lock (); @@ -206,15 +213,43 @@ J2KWAVEncoder::encoder_thread (Server* server) } void -J2KWAVEncoder::process_begin () +J2KWAVEncoder::process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format) { + if ((_fs->audio_sample_rate != dcp_audio_sample_rate (_fs->audio_sample_rate)) || (rint (_fs->frames_per_second) != _fs->frames_per_second)) { +#ifdef HAVE_SWRESAMPLE + + stringstream s; + s << "Will resample audio from " << _fs->audio_sample_rate << " to " << target_sample_rate(); + _log->log (s.str ()); + + _swr_context = swr_alloc_set_opts ( + 0, + audio_channel_layout, + audio_sample_format, + target_sample_rate(), + audio_channel_layout, + audio_sample_format, + _fs->audio_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) { - _worker_threads.push_back (new boost::thread (boost::bind (&J2KWAVEncoder::encoder_thread, this, (Server *) 0))); + _worker_threads.push_back (new boost::thread (boost::bind (&J2KWAVEncoder::encoder_thread, this, (ServerDescription *) 0))); } - vector servers = Config::instance()->servers (); + vector servers = Config::instance()->servers (); - for (vector::iterator i = servers.begin(); i != servers.end(); ++i) { + for (vector::iterator i = servers.begin(); i != servers.end(); ++i) { for (int j = 0; j < (*i)->threads (); ++j) { _worker_threads.push_back (new boost::thread (boost::bind (&J2KWAVEncoder::encoder_thread, this, *i))); } @@ -226,8 +261,11 @@ J2KWAVEncoder::process_end () { boost::mutex::scoped_lock lock (_worker_mutex); + _log->log ("Clearing queue of " + lexical_cast (_queue.size ())); + /* Keep waking workers until the queue is empty */ while (!_queue.empty ()) { + _log->log ("Waking with " + lexical_cast (_queue.size ())); _worker_condition.notify_all (); _worker_condition.wait (lock); } @@ -235,6 +273,61 @@ J2KWAVEncoder::process_end () lock.unlock (); terminate_worker_threads (); + + _log->log ("Mopping up " + lexical_cast (_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_worker_threads + + So just mop up anything left in the queue here. + */ + + for (list >::iterator i = _queue.begin(); i != _queue.end(); ++i) { + stringstream s; + s << "Encode left-over frame " << (*i)->frame(); + _log->log (s.str ()); + try { + shared_ptr e = (*i)->encode_locally (); + e->write (_opt, (*i)->frame ()); + frame_done ((*i)->frame ()); + } catch (std::exception& e) { + stringstream s; + s << "Local encode failed " << e.what() << "."; + _log->log (s.str ()); + } + } + +#if HAVE_SWRESAMPLE + if (_swr_context) { + + int mop = 0; + while (1) { + uint8_t buffer[256 * _fs->bytes_per_sample() * _fs->audio_channels]; + uint8_t* out[1] = { + buffer + }; + + int const frames = swr_convert (_swr_context, out, 256, 0, 0); + + if (frames < 0) { + throw EncodeError ("could not run sample-rate converter"); + } + + if (frames == 0) { + break; + } + + mop += frames; + write_audio (buffer, frames); + } + + swr_free (&_swr_context); + } +#endif + close_sound_files (); /* Rename .wav.tmp files to .wav */ @@ -247,11 +340,64 @@ J2KWAVEncoder::process_end () } void -J2KWAVEncoder::process_audio (uint8_t* data, int data_size) +J2KWAVEncoder::process_audio (uint8_t* data, int size) +{ + /* This is a buffer we might use if we are sample-rate converting; + it will need freeing if so. + */ + uint8_t* out_buffer = 0; + + /* Maybe sample-rate convert */ +#if HAVE_SWRESAMPLE + if (_swr_context) { + + uint8_t const * in[2] = { + data, + 0 + }; + + /* Here's samples per channel */ + int const samples = size / _fs->bytes_per_sample(); + + /* And here's frames (where 1 frame is a collection of samples, 1 for each channel, + so for 5.1 a frame would be 6 samples) + */ + int const frames = samples / _fs->audio_channels; + + /* Compute the resampled frame count and add 32 for luck */ + int const out_buffer_size_frames = ceil (frames * target_sample_rate() / _fs->audio_sample_rate) + 32; + int const out_buffer_size_bytes = out_buffer_size_frames * _fs->audio_channels * _fs->bytes_per_sample(); + out_buffer = new uint8_t[out_buffer_size_bytes]; + + uint8_t* out[2] = { + out_buffer, + 0 + }; + + /* Resample audio */ + int out_frames = swr_convert (_swr_context, out, out_buffer_size_frames, in, size); + if (out_frames < 0) { + throw EncodeError ("could not run sample-rate converter"); + } + + /* And point our variables at the resampled audio */ + data = out_buffer; + size = out_frames * _fs->audio_channels * _fs->bytes_per_sample(); + } +#endif + + write_audio (data, size); + + /* Delete the sample-rate conversion buffer, if it exists */ + delete[] out_buffer; +} + +void +J2KWAVEncoder::write_audio (uint8_t* data, int size) { /* Size of a sample in bytes */ int const sample_size = 2; - + /* XXX: we are assuming that sample_size is right, the _deinterleave_buffer_size is a multiple of the sample size and that data_size is a multiple of _fs->audio_channels * sample_size. */ @@ -259,7 +405,7 @@ J2KWAVEncoder::process_audio (uint8_t* data, int data_size) /* XXX: this code is very tricksy and it must be possible to make it simpler ... */ /* Number of bytes left to read this time */ - int remaining = data_size; + int remaining = size; /* Our position in the output buffers, in bytes */ int position = 0; while (remaining > 0) { @@ -279,7 +425,7 @@ J2KWAVEncoder::process_audio (uint8_t* data, int data_size) sf_write_short (_sound_files[i], (const short *) _deinterleave_buffer, this_time / sample_size); break; default: - throw DecodeError ("unknown audio sample format"); + throw EncodeError ("unknown audio sample format"); } } @@ -287,3 +433,23 @@ J2KWAVEncoder::process_audio (uint8_t* data, int data_size) remaining -= this_time * _fs->audio_channels; } } + +int +J2KWAVEncoder::target_sample_rate () const +{ + double t = dcp_audio_sample_rate (_fs->audio_sample_rate); + if (rint (_fs->frames_per_second) != _fs->frames_per_second) { + if (_fs->frames_per_second == 23.976) { + /* 24fps drop-frame ie 24 * 1000 / 1001 frames per second; + hence we need to resample the audio to dcp_audio_sample_rate * 1000 / 1001 + so that when we play it back at dcp_audio_sample_rate it is sped up + by the same amount that the video is + */ + t *= double(1000) / 1001; + } else { + throw EncodeError ("unknown fractional frame rate"); + } + } + + return rint (t); +}