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.
22 #include <libdcp/mono_picture_asset.h>
23 #include <libdcp/stereo_picture_asset.h>
24 #include <libdcp/sound_asset.h>
25 #include <libdcp/reel.h>
26 #include <libdcp/dcp.h>
27 #include <libdcp/cpl.h>
29 #include "compose.hpp"
33 #include "dcp_video_frame.h"
34 #include "dcp_content_type.h"
36 #include "audio_mapping.h"
48 using boost::shared_ptr;
49 using boost::weak_ptr;
51 int const Writer::_maximum_frames_in_memory = Config::instance()->num_local_encoding_threads() + 4;
53 Writer::Writer (shared_ptr<const Film> f, weak_ptr<Job> j)
56 , _first_nonexistant_frame (0)
59 , _queued_full_in_memory (0)
60 , _last_written_frame (-1)
61 , _last_written_eyes (EYES_RIGHT)
67 /* Remove any old DCP */
68 boost::filesystem::remove_all (_film->dir (_film->dcp_name ()));
70 shared_ptr<Job> job = _job.lock ();
73 job->sub (_("Checking existing image data"));
74 check_existing_picture_mxf ();
76 /* Create our picture asset in a subdirectory, named according to those
77 film's parameters which affect the video output. We will hard-link
78 it into the DCP later.
81 if (_film->three_d ()) {
82 _picture_asset.reset (new libdcp::StereoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
84 _picture_asset.reset (new libdcp::MonoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
87 _picture_asset->set_edit_rate (_film->video_frame_rate ());
88 _picture_asset->set_size (fit_ratio_within (_film->container()->ratio(), _film->full_frame ()));
90 if (_film->encrypted ()) {
91 _picture_asset->set_key (_film->key ());
94 _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
96 /* Write the sound asset into the film directory so that we leave the creation
97 of the DCP directory until the last minute. Some versions of windows inexplicably
98 don't like overwriting existing files here, so try to remove it using boost.
100 boost::system::error_code ec;
101 boost::filesystem::remove (_film->file (_film->audio_mxf_filename ()), ec);
103 _film->log()->log (String::compose ("Could not remove existing audio MXF file (%1)", ec.value ()));
106 _sound_asset.reset (new libdcp::SoundAsset (_film->directory (), _film->audio_mxf_filename ()));
107 _sound_asset->set_edit_rate (_film->video_frame_rate ());
108 _sound_asset->set_channels (_film->audio_channels ());
109 _sound_asset->set_sampling_rate (_film->audio_frame_rate ());
111 if (_film->encrypted ()) {
112 _sound_asset->set_key (_film->key ());
115 _sound_asset_writer = _sound_asset->start_write ();
117 _thread = new boost::thread (boost::bind (&Writer::thread, this));
119 job->sub (_("Encoding image data"));
123 Writer::write (shared_ptr<const EncodedData> encoded, int frame, Eyes eyes)
125 boost::mutex::scoped_lock lock (_mutex);
128 qi.type = QueueItem::FULL;
129 qi.encoded = encoded;
132 if (_film->three_d() && eyes == EYES_BOTH) {
133 /* 2D material in a 3D DCP; fake the 3D */
135 _queue.push_back (qi);
136 ++_queued_full_in_memory;
137 qi.eyes = EYES_RIGHT;
138 _queue.push_back (qi);
139 ++_queued_full_in_memory;
142 _queue.push_back (qi);
143 ++_queued_full_in_memory;
146 _condition.notify_all ();
150 Writer::fake_write (int frame, Eyes eyes)
152 boost::mutex::scoped_lock lock (_mutex);
154 FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
155 libdcp::FrameInfo info (ifi);
159 qi.type = QueueItem::FAKE;
162 if (_film->three_d() && eyes == EYES_BOTH) {
164 _queue.push_back (qi);
165 qi.eyes = EYES_RIGHT;
166 _queue.push_back (qi);
169 _queue.push_back (qi);
172 _condition.notify_all ();
175 /** This method is not thread safe */
177 Writer::write (shared_ptr<const AudioBuffers> audio)
179 _sound_asset_writer->write (audio->data(), audio->frames());
182 /** This must be called from Writer::thread() with an appropriate lock held */
184 Writer::have_sequenced_image_at_queue_head ()
186 if (_queue.empty ()) {
192 /* The queue should contain only EYES_LEFT/EYES_RIGHT pairs or EYES_BOTH */
194 if (_queue.front().eyes == EYES_BOTH) {
196 return _queue.front().frame == (_last_written_frame + 1);
201 if (_last_written_eyes == EYES_LEFT && _queue.front().frame == _last_written_frame && _queue.front().eyes == EYES_RIGHT) {
205 if (_last_written_eyes == EYES_RIGHT && _queue.front().frame == (_last_written_frame + 1) && _queue.front().eyes == EYES_LEFT) {
218 boost::mutex::scoped_lock lock (_mutex);
222 if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) {
226 TIMING (N_("writer sleeps with a queue of %1"), _queue.size());
227 _condition.wait (lock);
228 TIMING (N_("writer wakes with a queue of %1"), _queue.size());
231 if (_finish && _queue.empty()) {
235 /* Write any frames that we can write; i.e. those that are in sequence. */
236 while (have_sequenced_image_at_queue_head ()) {
237 QueueItem qi = _queue.front ();
239 if (qi.type == QueueItem::FULL && qi.encoded) {
240 --_queued_full_in_memory;
245 case QueueItem::FULL:
247 _film->log()->log (String::compose (N_("Writer FULL-writes %1 to MXF"), qi.frame));
249 qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false)));
252 libdcp::FrameInfo fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
253 qi.encoded->write_info (_film, qi.frame, qi.eyes, fin);
254 _last_written[qi.eyes] = qi.encoded;
258 case QueueItem::FAKE:
259 _film->log()->log (String::compose (N_("Writer FAKE-writes %1 to MXF"), qi.frame));
260 _picture_asset_writer->fake_write (qi.size);
261 _last_written[qi.eyes].reset ();
264 case QueueItem::REPEAT:
266 _film->log()->log (String::compose (N_("Writer REPEAT-writes %1 to MXF"), qi.frame));
267 libdcp::FrameInfo fin = _picture_asset_writer->write (
268 _last_written[qi.eyes]->data(),
269 _last_written[qi.eyes]->size()
272 _last_written[qi.eyes]->write_info (_film, qi.frame, qi.eyes, fin);
279 _last_written_frame = qi.frame;
280 _last_written_eyes = qi.eyes;
282 if (_film->length()) {
283 shared_ptr<Job> job = _job.lock ();
285 int total = _film->time_to_video_frames (_film->length ());
286 if (_film->three_d ()) {
287 /* _full_written and so on are incremented for each eye, so we need to double the total
288 frames to get the correct progress.
292 job->set_progress (float (_full_written + _fake_written + _repeat_written) / total);
296 while (_queued_full_in_memory > _maximum_frames_in_memory) {
297 /* Too many frames in memory which can't yet be written to the stream.
298 Write some FULL frames to disk.
301 /* Find one from the back of the queue */
303 list<QueueItem>::reverse_iterator i = _queue.rbegin ();
304 while (i != _queue.rend() && (i->type != QueueItem::FULL || !i->encoded)) {
308 assert (i != _queue.rend());
317 "Writer full (awaiting %1 [last eye was %2]); pushes %3 to disk",
318 _last_written_frame + 1,
319 _last_written_eyes, qi.frame)
322 qi.encoded->write (_film, qi.frame, qi.eyes);
325 --_queued_full_in_memory;
341 boost::mutex::scoped_lock lock (_mutex);
343 _condition.notify_all ();
352 _picture_asset_writer->finalize ();
353 _sound_asset_writer->finalize ();
355 int const frames = _last_written_frame + 1;
357 _picture_asset->set_duration (frames);
359 /* Hard-link the video MXF into the DCP */
360 boost::filesystem::path video_from;
361 video_from /= _film->internal_video_mxf_dir();
362 video_from /= _film->internal_video_mxf_filename();
364 boost::filesystem::path video_to;
365 video_to /= _film->dir (_film->dcp_name());
366 video_to /= _film->video_mxf_filename ();
368 boost::system::error_code ec;
369 boost::filesystem::create_hard_link (video_from, video_to, ec);
371 /* hard link failed; copy instead */
372 boost::filesystem::copy_file (video_from, video_to);
373 _film->log()->log ("Hard-link failed; fell back to copying");
376 /* And update the asset */
378 _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
379 _picture_asset->set_file_name (_film->video_mxf_filename ());
381 /* Move the audio MXF into the DCP */
383 boost::filesystem::path audio_to;
384 audio_to /= _film->dir (_film->dcp_name ());
385 audio_to /= _film->audio_mxf_filename ();
387 boost::filesystem::rename (_film->file (_film->audio_mxf_filename ()), audio_to, ec);
390 String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
394 _sound_asset->set_directory (_film->dir (_film->dcp_name ()));
395 _sound_asset->set_duration (frames);
397 libdcp::DCP dcp (_film->dir (_film->dcp_name()));
399 shared_ptr<libdcp::CPL> cpl (
401 _film->dir (_film->dcp_name()),
403 _film->dcp_content_type()->libdcp_kind (),
405 _film->video_frame_rate ()
411 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
414 shared_ptr<libdcp::SubtitleAsset> ()
418 shared_ptr<Job> job = _job.lock ();
421 job->sub (_("Computing image digest"));
422 _picture_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
424 job->sub (_("Computing audio digest"));
425 _sound_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
427 libdcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
428 meta.set_issue_date_now ();
429 dcp.write_xml (_film->interop (), meta, _film->is_signed() ? make_signer () : shared_ptr<const libdcp::Signer> ());
431 _film->log()->log (String::compose (N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT; %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk));
434 /** Tell the writer that frame `f' should be a repeat of the frame before it */
436 Writer::repeat (int f, Eyes e)
438 boost::mutex::scoped_lock lock (_mutex);
441 qi.type = QueueItem::REPEAT;
443 if (_film->three_d() && e == EYES_BOTH) {
445 _queue.push_back (qi);
446 qi.eyes = EYES_RIGHT;
447 _queue.push_back (qi);
450 _queue.push_back (qi);
453 _condition.notify_all ();
457 Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
459 /* Read the frame info as written */
460 FILE* ifi = fopen_boost (_film->info_path (f, eyes), "r");
462 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
466 libdcp::FrameInfo info (ifi);
468 if (info.size == 0) {
469 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
473 /* Read the data from the MXF and hash it */
474 dcpomatic_fseek (mxf, info.offset, SEEK_SET);
475 EncodedData data (info.size);
476 size_t const read = fread (data.data(), 1, data.size(), mxf);
477 if (read != static_cast<size_t> (data.size ())) {
478 _film->log()->log (String::compose ("Existing frame %1 is incomplete", f));
482 string const existing_hash = md5_digest (data.data(), data.size());
483 if (existing_hash != info.hash) {
484 _film->log()->log (String::compose ("Existing frame %1 failed hash check", f));
492 Writer::check_existing_picture_mxf ()
494 /* Try to open the existing MXF */
495 boost::filesystem::path p;
496 p /= _film->internal_video_mxf_dir ();
497 p /= _film->internal_video_mxf_filename ();
498 FILE* mxf = fopen_boost (p, "rb");
500 _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno));
505 for (boost::filesystem::directory_iterator i (_film->info_dir ()); i != boost::filesystem::directory_iterator (); ++i) {
511 shared_ptr<Job> job = _job.lock ();
514 job->set_progress (float (_first_nonexistant_frame) / N);
516 if (_film->three_d ()) {
517 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_LEFT)) {
520 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_RIGHT)) {
524 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_BOTH)) {
529 _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
530 ++_first_nonexistant_frame;
536 /** @param frame Frame index.
537 * @return true if we can fake-write this frame.
540 Writer::can_fake_write (int frame) const
542 /* We have to do a proper write of the first frame so that we can set up the JPEG2000
543 parameters in the MXF writer.
545 return (frame != 0 && frame < _first_nonexistant_frame);
549 operator< (QueueItem const & a, QueueItem const & b)
551 if (a.frame != b.frame) {
552 return a.frame < b.frame;
555 return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
559 operator== (QueueItem const & a, QueueItem const & b)
561 return a.frame == b.frame && a.eyes == b.eyes;