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_all (_film->file (_film->audio_mxf_filename ()), ec);
105 "Could not remove existing audio MXF file %1 (%2)",
106 _film->file (_film->audio_mxf_filename ()),
111 _sound_asset.reset (new libdcp::SoundAsset (_film->directory (), _film->audio_mxf_filename ()));
112 _sound_asset->set_edit_rate (_film->video_frame_rate ());
113 _sound_asset->set_channels (_film->audio_channels ());
114 _sound_asset->set_sampling_rate (_film->audio_frame_rate ());
116 if (_film->encrypted ()) {
117 _sound_asset->set_key (_film->key ());
120 _sound_asset_writer = _sound_asset->start_write ();
122 _thread = new boost::thread (boost::bind (&Writer::thread, this));
124 job->sub (_("Encoding image data"));
128 Writer::write (shared_ptr<const EncodedData> encoded, int frame, Eyes eyes)
130 boost::mutex::scoped_lock lock (_mutex);
132 while (_queued_full_in_memory > _maximum_frames_in_memory) {
133 _full_condition.wait (lock);
137 qi.type = QueueItem::FULL;
138 qi.encoded = encoded;
141 if (_film->three_d() && eyes == EYES_BOTH) {
142 /* 2D material in a 3D DCP; fake the 3D */
144 _queue.push_back (qi);
145 ++_queued_full_in_memory;
146 qi.eyes = EYES_RIGHT;
147 _queue.push_back (qi);
148 ++_queued_full_in_memory;
151 _queue.push_back (qi);
152 ++_queued_full_in_memory;
155 _empty_condition.notify_all ();
159 Writer::fake_write (int frame, Eyes eyes)
161 boost::mutex::scoped_lock lock (_mutex);
163 while (_queued_full_in_memory > _maximum_frames_in_memory) {
164 _full_condition.wait (lock);
167 FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
168 libdcp::FrameInfo info (ifi);
172 qi.type = QueueItem::FAKE;
175 if (_film->three_d() && eyes == EYES_BOTH) {
177 _queue.push_back (qi);
178 qi.eyes = EYES_RIGHT;
179 _queue.push_back (qi);
182 _queue.push_back (qi);
185 _empty_condition.notify_all ();
188 /** This method is not thread safe */
190 Writer::write (shared_ptr<const AudioBuffers> audio)
192 _sound_asset_writer->write (audio->data(), audio->frames());
195 /** This must be called from Writer::thread() with an appropriate lock held */
197 Writer::have_sequenced_image_at_queue_head ()
199 if (_queue.empty ()) {
205 /* The queue should contain only EYES_LEFT/EYES_RIGHT pairs or EYES_BOTH */
207 if (_queue.front().eyes == EYES_BOTH) {
209 return _queue.front().frame == (_last_written_frame + 1);
214 if (_last_written_eyes == EYES_LEFT && _queue.front().frame == _last_written_frame && _queue.front().eyes == EYES_RIGHT) {
218 if (_last_written_eyes == EYES_RIGHT && _queue.front().frame == (_last_written_frame + 1) && _queue.front().eyes == EYES_LEFT) {
231 boost::mutex::scoped_lock lock (_mutex);
235 if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) {
239 TIMING (N_("writer sleeps with a queue of %1"), _queue.size());
240 _empty_condition.wait (lock);
241 TIMING (N_("writer wakes with a queue of %1"), _queue.size());
244 if (_finish && _queue.empty()) {
248 /* Write any frames that we can write; i.e. those that are in sequence. */
249 while (have_sequenced_image_at_queue_head ()) {
250 QueueItem qi = _queue.front ();
252 if (qi.type == QueueItem::FULL && qi.encoded) {
253 --_queued_full_in_memory;
258 case QueueItem::FULL:
260 _film->log()->log (String::compose (N_("Writer FULL-writes %1 to MXF"), qi.frame));
262 qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false)));
265 libdcp::FrameInfo fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
266 qi.encoded->write_info (_film, qi.frame, qi.eyes, fin);
267 _last_written[qi.eyes] = qi.encoded;
271 case QueueItem::FAKE:
272 _film->log()->log (String::compose (N_("Writer FAKE-writes %1 to MXF"), qi.frame));
273 _picture_asset_writer->fake_write (qi.size);
274 _last_written[qi.eyes].reset ();
277 case QueueItem::REPEAT:
279 _film->log()->log (String::compose (N_("Writer REPEAT-writes %1 to MXF"), qi.frame));
280 libdcp::FrameInfo fin = _picture_asset_writer->write (
281 _last_written[qi.eyes]->data(),
282 _last_written[qi.eyes]->size()
285 _last_written[qi.eyes]->write_info (_film, qi.frame, qi.eyes, fin);
292 _last_written_frame = qi.frame;
293 _last_written_eyes = qi.eyes;
295 if (_film->length()) {
296 shared_ptr<Job> job = _job.lock ();
298 int total = _film->time_to_video_frames (_film->length ());
299 if (_film->three_d ()) {
300 /* _full_written and so on are incremented for each eye, so we need to double the total
301 frames to get the correct progress.
305 job->set_progress (float (_full_written + _fake_written + _repeat_written) / total);
309 while (_queued_full_in_memory > _maximum_frames_in_memory) {
310 /* Too many frames in memory which can't yet be written to the stream.
311 Write some FULL frames to disk.
314 /* Find one from the back of the queue */
316 list<QueueItem>::reverse_iterator i = _queue.rbegin ();
317 while (i != _queue.rend() && (i->type != QueueItem::FULL || !i->encoded)) {
321 assert (i != _queue.rend());
330 "Writer full (awaiting %1 [last eye was %2]); pushes %3 to disk",
331 _last_written_frame + 1,
332 _last_written_eyes, qi.frame)
335 qi.encoded->write (_film, qi.frame, qi.eyes);
338 --_queued_full_in_memory;
354 boost::mutex::scoped_lock lock (_mutex);
356 _empty_condition.notify_all ();
357 _full_condition.notify_all ();
366 _picture_asset_writer->finalize ();
367 _sound_asset_writer->finalize ();
369 int const frames = _last_written_frame + 1;
371 _picture_asset->set_duration (frames);
373 /* Hard-link the video MXF into the DCP */
374 boost::filesystem::path video_from;
375 video_from /= _film->internal_video_mxf_dir();
376 video_from /= _film->internal_video_mxf_filename();
378 boost::filesystem::path video_to;
379 video_to /= _film->dir (_film->dcp_name());
380 video_to /= _film->video_mxf_filename ();
382 boost::system::error_code ec;
383 boost::filesystem::create_hard_link (video_from, video_to, ec);
385 /* hard link failed; copy instead */
386 boost::filesystem::copy_file (video_from, video_to);
387 _film->log()->log ("Hard-link failed; fell back to copying");
390 /* And update the asset */
392 _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
393 _picture_asset->set_file_name (_film->video_mxf_filename ());
395 /* Move the audio MXF into the DCP */
397 boost::filesystem::path audio_to;
398 audio_to /= _film->dir (_film->dcp_name ());
399 audio_to /= _film->audio_mxf_filename ();
401 boost::filesystem::rename (_film->file (_film->audio_mxf_filename ()), audio_to, ec);
404 String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
408 _sound_asset->set_directory (_film->dir (_film->dcp_name ()));
409 _sound_asset->set_duration (frames);
411 libdcp::DCP dcp (_film->dir (_film->dcp_name()));
413 shared_ptr<libdcp::CPL> cpl (
415 _film->dir (_film->dcp_name()),
417 _film->dcp_content_type()->libdcp_kind (),
419 _film->video_frame_rate ()
425 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
428 shared_ptr<libdcp::SubtitleAsset> ()
432 shared_ptr<Job> job = _job.lock ();
435 job->sub (_("Computing image digest"));
436 _picture_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
438 job->sub (_("Computing audio digest"));
439 _sound_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
441 libdcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
442 meta.set_issue_date_now ();
443 dcp.write_xml (_film->interop (), meta, _film->is_signed() ? make_signer () : shared_ptr<const libdcp::Signer> ());
446 String::compose (N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT; %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk)
450 /** Tell the writer that frame `f' should be a repeat of the frame before it */
452 Writer::repeat (int f, Eyes e)
454 boost::mutex::scoped_lock lock (_mutex);
456 while (_queued_full_in_memory > _maximum_frames_in_memory) {
457 _full_condition.wait (lock);
461 qi.type = QueueItem::REPEAT;
463 if (_film->three_d() && e == EYES_BOTH) {
465 _queue.push_back (qi);
466 qi.eyes = EYES_RIGHT;
467 _queue.push_back (qi);
470 _queue.push_back (qi);
473 _empty_condition.notify_all ();
477 Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
479 /* Read the frame info as written */
480 FILE* ifi = fopen_boost (_film->info_path (f, eyes), "r");
482 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
486 libdcp::FrameInfo info (ifi);
488 if (info.size == 0) {
489 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
493 /* Read the data from the MXF and hash it */
494 dcpomatic_fseek (mxf, info.offset, SEEK_SET);
495 EncodedData data (info.size);
496 size_t const read = fread (data.data(), 1, data.size(), mxf);
497 if (read != static_cast<size_t> (data.size ())) {
498 _film->log()->log (String::compose ("Existing frame %1 is incomplete", f));
502 string const existing_hash = md5_digest (data.data(), data.size());
503 if (existing_hash != info.hash) {
504 _film->log()->log (String::compose ("Existing frame %1 failed hash check", f));
512 Writer::check_existing_picture_mxf ()
514 /* Try to open the existing MXF */
515 boost::filesystem::path p;
516 p /= _film->internal_video_mxf_dir ();
517 p /= _film->internal_video_mxf_filename ();
518 FILE* mxf = fopen_boost (p, "rb");
520 _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno));
525 for (boost::filesystem::directory_iterator i (_film->info_dir ()); i != boost::filesystem::directory_iterator (); ++i) {
531 shared_ptr<Job> job = _job.lock ();
534 job->set_progress (float (_first_nonexistant_frame) / N);
536 if (_film->three_d ()) {
537 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_LEFT)) {
540 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_RIGHT)) {
544 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_BOTH)) {
549 _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
550 ++_first_nonexistant_frame;
556 /** @param frame Frame index.
557 * @return true if we can fake-write this frame.
560 Writer::can_fake_write (int frame) const
562 /* We have to do a proper write of the first frame so that we can set up the JPEG2000
563 parameters in the MXF writer.
565 return (frame != 0 && frame < _first_nonexistant_frame);
569 operator< (QueueItem const & a, QueueItem const & b)
571 if (a.frame != b.frame) {
572 return a.frame < b.frame;
575 return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
579 operator== (QueueItem const & a, QueueItem const & b)
581 return a.frame == b.frame && a.eyes == b.eyes;