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);
133 qi.type = QueueItem::FULL;
134 qi.encoded = encoded;
137 if (_film->three_d() && eyes == EYES_BOTH) {
138 /* 2D material in a 3D DCP; fake the 3D */
140 _queue.push_back (qi);
141 ++_queued_full_in_memory;
142 qi.eyes = EYES_RIGHT;
143 _queue.push_back (qi);
144 ++_queued_full_in_memory;
147 _queue.push_back (qi);
148 ++_queued_full_in_memory;
151 _condition.notify_all ();
155 Writer::fake_write (int frame, Eyes eyes)
157 boost::mutex::scoped_lock lock (_mutex);
159 FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
160 libdcp::FrameInfo info (ifi);
164 qi.type = QueueItem::FAKE;
167 if (_film->three_d() && eyes == EYES_BOTH) {
169 _queue.push_back (qi);
170 qi.eyes = EYES_RIGHT;
171 _queue.push_back (qi);
174 _queue.push_back (qi);
177 _condition.notify_all ();
180 /** This method is not thread safe */
182 Writer::write (shared_ptr<const AudioBuffers> audio)
184 _sound_asset_writer->write (audio->data(), audio->frames());
187 /** This must be called from Writer::thread() with an appropriate lock held */
189 Writer::have_sequenced_image_at_queue_head ()
191 if (_queue.empty ()) {
197 /* The queue should contain only EYES_LEFT/EYES_RIGHT pairs or EYES_BOTH */
199 if (_queue.front().eyes == EYES_BOTH) {
201 return _queue.front().frame == (_last_written_frame + 1);
206 if (_last_written_eyes == EYES_LEFT && _queue.front().frame == _last_written_frame && _queue.front().eyes == EYES_RIGHT) {
210 if (_last_written_eyes == EYES_RIGHT && _queue.front().frame == (_last_written_frame + 1) && _queue.front().eyes == EYES_LEFT) {
223 boost::mutex::scoped_lock lock (_mutex);
227 if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) {
231 TIMING (N_("writer sleeps with a queue of %1"), _queue.size());
232 _condition.wait (lock);
233 TIMING (N_("writer wakes with a queue of %1"), _queue.size());
236 if (_finish && _queue.empty()) {
240 /* Write any frames that we can write; i.e. those that are in sequence. */
241 while (have_sequenced_image_at_queue_head ()) {
242 QueueItem qi = _queue.front ();
244 if (qi.type == QueueItem::FULL && qi.encoded) {
245 --_queued_full_in_memory;
250 case QueueItem::FULL:
252 _film->log()->log (String::compose (N_("Writer FULL-writes %1 to MXF"), qi.frame));
254 qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false)));
257 libdcp::FrameInfo fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
258 qi.encoded->write_info (_film, qi.frame, qi.eyes, fin);
259 _last_written[qi.eyes] = qi.encoded;
263 case QueueItem::FAKE:
264 _film->log()->log (String::compose (N_("Writer FAKE-writes %1 to MXF"), qi.frame));
265 _picture_asset_writer->fake_write (qi.size);
266 _last_written[qi.eyes].reset ();
269 case QueueItem::REPEAT:
271 _film->log()->log (String::compose (N_("Writer REPEAT-writes %1 to MXF"), qi.frame));
272 libdcp::FrameInfo fin = _picture_asset_writer->write (
273 _last_written[qi.eyes]->data(),
274 _last_written[qi.eyes]->size()
277 _last_written[qi.eyes]->write_info (_film, qi.frame, qi.eyes, fin);
284 _last_written_frame = qi.frame;
285 _last_written_eyes = qi.eyes;
287 if (_film->length()) {
288 shared_ptr<Job> job = _job.lock ();
290 int total = _film->time_to_video_frames (_film->length ());
291 if (_film->three_d ()) {
292 /* _full_written and so on are incremented for each eye, so we need to double the total
293 frames to get the correct progress.
297 job->set_progress (float (_full_written + _fake_written + _repeat_written) / total);
301 while (_queued_full_in_memory > _maximum_frames_in_memory) {
302 /* Too many frames in memory which can't yet be written to the stream.
303 Write some FULL frames to disk.
306 /* Find one from the back of the queue */
308 list<QueueItem>::reverse_iterator i = _queue.rbegin ();
309 while (i != _queue.rend() && (i->type != QueueItem::FULL || !i->encoded)) {
313 assert (i != _queue.rend());
322 "Writer full (awaiting %1 [last eye was %2]); pushes %3 to disk",
323 _last_written_frame + 1,
324 _last_written_eyes, qi.frame)
327 qi.encoded->write (_film, qi.frame, qi.eyes);
330 --_queued_full_in_memory;
346 boost::mutex::scoped_lock lock (_mutex);
348 _condition.notify_all ();
357 _picture_asset_writer->finalize ();
358 _sound_asset_writer->finalize ();
360 int const frames = _last_written_frame + 1;
362 _picture_asset->set_duration (frames);
364 /* Hard-link the video MXF into the DCP */
365 boost::filesystem::path video_from;
366 video_from /= _film->internal_video_mxf_dir();
367 video_from /= _film->internal_video_mxf_filename();
369 boost::filesystem::path video_to;
370 video_to /= _film->dir (_film->dcp_name());
371 video_to /= _film->video_mxf_filename ();
373 boost::system::error_code ec;
374 boost::filesystem::create_hard_link (video_from, video_to, ec);
376 /* hard link failed; copy instead */
377 boost::filesystem::copy_file (video_from, video_to);
378 _film->log()->log ("Hard-link failed; fell back to copying");
381 /* And update the asset */
383 _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
384 _picture_asset->set_file_name (_film->video_mxf_filename ());
386 /* Move the audio MXF into the DCP */
388 boost::filesystem::path audio_to;
389 audio_to /= _film->dir (_film->dcp_name ());
390 audio_to /= _film->audio_mxf_filename ();
392 boost::filesystem::rename (_film->file (_film->audio_mxf_filename ()), audio_to, ec);
395 String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
399 _sound_asset->set_directory (_film->dir (_film->dcp_name ()));
400 _sound_asset->set_duration (frames);
402 libdcp::DCP dcp (_film->dir (_film->dcp_name()));
404 shared_ptr<libdcp::CPL> cpl (
406 _film->dir (_film->dcp_name()),
408 _film->dcp_content_type()->libdcp_kind (),
410 _film->video_frame_rate ()
416 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
419 shared_ptr<libdcp::SubtitleAsset> ()
423 shared_ptr<Job> job = _job.lock ();
426 job->sub (_("Computing image digest"));
427 _picture_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
429 job->sub (_("Computing audio digest"));
430 _sound_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
432 libdcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
433 meta.set_issue_date_now ();
434 dcp.write_xml (_film->interop (), meta, _film->is_signed() ? make_signer () : shared_ptr<const libdcp::Signer> ());
436 _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));
439 /** Tell the writer that frame `f' should be a repeat of the frame before it */
441 Writer::repeat (int f, Eyes e)
443 boost::mutex::scoped_lock lock (_mutex);
446 qi.type = QueueItem::REPEAT;
448 if (_film->three_d() && e == EYES_BOTH) {
450 _queue.push_back (qi);
451 qi.eyes = EYES_RIGHT;
452 _queue.push_back (qi);
455 _queue.push_back (qi);
458 _condition.notify_all ();
462 Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
464 /* Read the frame info as written */
465 FILE* ifi = fopen_boost (_film->info_path (f, eyes), "r");
467 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
471 libdcp::FrameInfo info (ifi);
473 if (info.size == 0) {
474 _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
478 /* Read the data from the MXF and hash it */
479 dcpomatic_fseek (mxf, info.offset, SEEK_SET);
480 EncodedData data (info.size);
481 size_t const read = fread (data.data(), 1, data.size(), mxf);
482 if (read != static_cast<size_t> (data.size ())) {
483 _film->log()->log (String::compose ("Existing frame %1 is incomplete", f));
487 string const existing_hash = md5_digest (data.data(), data.size());
488 if (existing_hash != info.hash) {
489 _film->log()->log (String::compose ("Existing frame %1 failed hash check", f));
497 Writer::check_existing_picture_mxf ()
499 /* Try to open the existing MXF */
500 boost::filesystem::path p;
501 p /= _film->internal_video_mxf_dir ();
502 p /= _film->internal_video_mxf_filename ();
503 FILE* mxf = fopen_boost (p, "rb");
505 _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno));
510 for (boost::filesystem::directory_iterator i (_film->info_dir ()); i != boost::filesystem::directory_iterator (); ++i) {
516 shared_ptr<Job> job = _job.lock ();
519 job->set_progress (float (_first_nonexistant_frame) / N);
521 if (_film->three_d ()) {
522 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_LEFT)) {
525 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_RIGHT)) {
529 if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_BOTH)) {
534 _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
535 ++_first_nonexistant_frame;
541 /** @param frame Frame index.
542 * @return true if we can fake-write this frame.
545 Writer::can_fake_write (int frame) const
547 /* We have to do a proper write of the first frame so that we can set up the JPEG2000
548 parameters in the MXF writer.
550 return (frame != 0 && frame < _first_nonexistant_frame);
554 operator< (QueueItem const & a, QueueItem const & b)
556 if (a.frame != b.frame) {
557 return a.frame < b.frame;
560 return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
564 operator== (QueueItem const & a, QueueItem const & b)
566 return a.frame == b.frame && a.eyes == b.eyes;