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 /* Write the sound asset into the film directory so that we leave the creation
95 of the DCP directory until the last minute. Some versions of windows inexplicably
96 don't like overwriting existing files here, so try to remove it using boost.
98 boost::system::error_code ec;
99 boost::filesystem::remove_all (_film->file (_film->audio_mxf_filename ()), ec);
103 "Could not remove existing audio MXF file %1 (%2)",
104 _film->file (_film->audio_mxf_filename ()),
109 _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
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;
341 _full_condition.notify_all ();
356 boost::mutex::scoped_lock lock (_mutex);
358 _empty_condition.notify_all ();
359 _full_condition.notify_all ();
368 _picture_asset_writer->finalize ();
369 _sound_asset_writer->finalize ();
371 int const frames = _last_written_frame + 1;
373 _picture_asset->set_duration (frames);
375 /* Hard-link the video MXF into the DCP */
376 boost::filesystem::path video_from;
377 video_from /= _film->internal_video_mxf_dir();
378 video_from /= _film->internal_video_mxf_filename();
380 boost::filesystem::path video_to;
381 video_to /= _film->dir (_film->dcp_name());
382 video_to /= _film->video_mxf_filename ();
384 boost::system::error_code ec;
385 boost::filesystem::create_hard_link (video_from, video_to, ec);
387 /* hard link failed; copy instead */
388 boost::filesystem::copy_file (video_from, video_to);
389 _film->log()->log ("Hard-link failed; fell back to copying");
392 /* And update the asset */
394 _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
395 _picture_asset->set_file_name (_film->video_mxf_filename ());
397 /* Move the audio MXF into the DCP */
399 boost::filesystem::path audio_to;
400 audio_to /= _film->dir (_film->dcp_name ());
401 audio_to /= _film->audio_mxf_filename ();
403 boost::filesystem::rename (_film->file (_film->audio_mxf_filename ()), audio_to, ec);
406 String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
410 _sound_asset->set_directory (_film->dir (_film->dcp_name ()));
411 _sound_asset->set_duration (frames);
413 libdcp::DCP dcp (_film->dir (_film->dcp_name()));
415 shared_ptr<libdcp::CPL> cpl (
417 _film->dir (_film->dcp_name()),
419 _film->dcp_content_type()->libdcp_kind (),
421 _film->video_frame_rate ()
427 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
430 shared_ptr<libdcp::SubtitleAsset> ()
434 shared_ptr<Job> job = _job.lock ();
437 job->sub (_("Computing image digest"));
438 _picture_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
440 job->sub (_("Computing audio digest"));
441 _sound_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
443 libdcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
444 meta.set_issue_date_now ();
445 dcp.write_xml (_film->interop (), meta, _film->is_signed() ? make_signer () : shared_ptr<const libdcp::Signer> ());
448 String::compose (N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT; %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk)
452 /** Tell the writer that frame `f' should be a repeat of the frame before it */
454 Writer::repeat (int f, Eyes e)
456 boost::mutex::scoped_lock lock (_mutex);
458 while (_queued_full_in_memory > _maximum_frames_in_memory) {
459 _full_condition.wait (lock);
463 qi.type = QueueItem::REPEAT;
465 if (_film->three_d() && e == EYES_BOTH) {
467 _queue.push_back (qi);
468 qi.eyes = EYES_RIGHT;
469 _queue.push_back (qi);
472 _queue.push_back (qi);
475 _empty_condition.notify_all ();
479 Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
481 /* Read the frame info as written */
482 FILE* ifi = fopen_boost (_film->info_path (f, eyes), "r");
484 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
488 libdcp::FrameInfo info (ifi);
490 if (info.size == 0) {
491 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
495 /* Read the data from the MXF and hash it */
496 dcpomatic_fseek (mxf, info.offset, SEEK_SET);
497 EncodedData data (info.size);
498 size_t const read = fread (data.data(), 1, data.size(), mxf);
499 if (read != static_cast<size_t> (data.size ())) {
500 _film->log()->log (String::compose ("Existing frame %1 is incomplete", f));
504 string const existing_hash = md5_digest (data.data(), data.size());
505 if (existing_hash != info.hash) {
506 _film->log()->log (String::compose ("Existing frame %1 failed hash check", f));
514 Writer::check_existing_picture_mxf ()
516 /* Try to open the existing MXF */
517 boost::filesystem::path p;
518 p /= _film->internal_video_mxf_dir ();
519 p /= _film->internal_video_mxf_filename ();
520 FILE* mxf = fopen_boost (p, "rb");
522 _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno));
527 for (boost::filesystem::directory_iterator i (_film->info_dir ()); i != boost::filesystem::directory_iterator (); ++i) {
533 shared_ptr<Job> job = _job.lock ();
536 job->set_progress (float (_first_nonexistant_frame) / N);
538 if (_film->three_d ()) {
539 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_LEFT)) {
542 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_RIGHT)) {
546 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_BOTH)) {
551 _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
552 ++_first_nonexistant_frame;
558 /** @param frame Frame index.
559 * @return true if we can fake-write this frame.
562 Writer::can_fake_write (int frame) const
564 /* We have to do a proper write of the first frame so that we can set up the JPEG2000
565 parameters in the MXF writer.
567 return (frame != 0 && frame < _first_nonexistant_frame);
571 operator< (QueueItem const & a, QueueItem const & b)
573 if (a.frame != b.frame) {
574 return a.frame < b.frame;
577 return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
581 operator== (QueueItem const & a, QueueItem const & b)
583 return a.frame == b.frame && a.eyes == b.eyes;