C++11 tidying.
[dcpomatic.git] / src / lib / writer.cc
1 /*
2     Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "writer.h"
23 #include "compose.hpp"
24 #include "film.h"
25 #include "ratio.h"
26 #include "log.h"
27 #include "dcpomatic_log.h"
28 #include "dcp_video.h"
29 #include "dcp_content_type.h"
30 #include "audio_mapping.h"
31 #include "config.h"
32 #include "job.h"
33 #include "cross.h"
34 #include "audio_buffers.h"
35 #include "version.h"
36 #include "font_data.h"
37 #include "util.h"
38 #include "reel_writer.h"
39 #include "text_content.h"
40 #include <dcp/cpl.h>
41 #include <dcp/locale_convert.h>
42 #include <dcp/reel_file_asset.h>
43 #include <fstream>
44 #include <cerrno>
45 #include <iostream>
46 #include <cfloat>
47
48 #include "i18n.h"
49
50
51 /* OS X strikes again */
52 #undef set_key
53
54
55 using std::cout;
56 using std::dynamic_pointer_cast;
57 using std::make_shared;
58 using std::max;
59 using std::min;
60 using std::shared_ptr;
61 using std::string;
62 using std::vector;
63 using std::weak_ptr;
64 using boost::optional;
65 #if BOOST_VERSION >= 106100
66 using namespace boost::placeholders;
67 #endif
68 using dcp::Data;
69 using dcp::ArrayData;
70 using namespace dcpomatic;
71
72
73 /** @param j Job to report progress to, or 0.
74  *  @param text_only true to enable only the text (subtitle/ccap) parts of the writer.
75  */
76 Writer::Writer (weak_ptr<const Film> weak_film, weak_ptr<Job> j, bool text_only)
77         : WeakConstFilm (weak_film)
78         , _job (j)
79         /* These will be reset to sensible values when J2KEncoder is created */
80         , _maximum_frames_in_memory (8)
81         , _maximum_queue_size (8)
82         , _text_only (text_only)
83 {
84         auto job = _job.lock ();
85
86         int reel_index = 0;
87         auto const reels = film()->reels();
88         for (auto p: reels) {
89                 _reels.push_back (ReelWriter(weak_film, p, job, reel_index++, reels.size(), text_only));
90         }
91
92         _last_written.resize (reels.size());
93
94         /* We can keep track of the current audio, subtitle and closed caption reels easily because audio
95            and captions arrive to the Writer in sequence.  This is not so for video.
96         */
97         _audio_reel = _reels.begin ();
98         _subtitle_reel = _reels.begin ();
99         for (auto i: film()->closed_caption_tracks()) {
100                 _caption_reels[i] = _reels.begin ();
101         }
102         _atmos_reel = _reels.begin ();
103
104         /* Check that the signer is OK */
105         string reason;
106         if (!Config::instance()->signer_chain()->valid(&reason)) {
107                 throw InvalidSignerError (reason);
108         }
109 }
110
111
112 void
113 Writer::start ()
114 {
115         if (!_text_only) {
116                 _thread = boost::thread (boost::bind(&Writer::thread, this));
117 #ifdef DCPOMATIC_LINUX
118                 pthread_setname_np (_thread.native_handle(), "writer");
119 #endif
120         }
121 }
122
123
124 Writer::~Writer ()
125 {
126         if (!_text_only) {
127                 terminate_thread (false);
128         }
129 }
130
131
132 /** Pass a video frame to the writer for writing to disk at some point.
133  *  This method can be called with frames out of order.
134  *  @param encoded JPEG2000-encoded data.
135  *  @param frame Frame index within the DCP.
136  *  @param eyes Eyes that this frame image is for.
137  */
138 void
139 Writer::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes)
140 {
141         boost::mutex::scoped_lock lock (_state_mutex);
142
143         while (_queued_full_in_memory > _maximum_frames_in_memory) {
144                 /* There are too many full frames in memory; wake the main writer thread and
145                    wait until it sorts everything out */
146                 _empty_condition.notify_all ();
147                 _full_condition.wait (lock);
148         }
149
150         QueueItem qi;
151         qi.type = QueueItem::Type::FULL;
152         qi.encoded = encoded;
153         qi.reel = video_reel (frame);
154         qi.frame = frame - _reels[qi.reel].start ();
155
156         if (film()->three_d() && eyes == Eyes::BOTH) {
157                 /* 2D material in a 3D DCP; fake the 3D */
158                 qi.eyes = Eyes::LEFT;
159                 _queue.push_back (qi);
160                 ++_queued_full_in_memory;
161                 qi.eyes = Eyes::RIGHT;
162                 _queue.push_back (qi);
163                 ++_queued_full_in_memory;
164         } else {
165                 qi.eyes = eyes;
166                 _queue.push_back (qi);
167                 ++_queued_full_in_memory;
168         }
169
170         /* Now there's something to do: wake anything wait()ing on _empty_condition */
171         _empty_condition.notify_all ();
172 }
173
174
175 bool
176 Writer::can_repeat (Frame frame) const
177 {
178         return frame > _reels[video_reel(frame)].start();
179 }
180
181
182 /** Repeat the last frame that was written to a reel as a new frame.
183  *  @param frame Frame index within the DCP of the new (repeated) frame.
184  *  @param eyes Eyes that this repeated frame image is for.
185  */
186 void
187 Writer::repeat (Frame frame, Eyes eyes)
188 {
189         boost::mutex::scoped_lock lock (_state_mutex);
190
191         while (_queue.size() > _maximum_queue_size && have_sequenced_image_at_queue_head()) {
192                 /* The queue is too big, and the main writer thread can run and fix it, so
193                    wake it and wait until it has done.
194                 */
195                 _empty_condition.notify_all ();
196                 _full_condition.wait (lock);
197         }
198
199         QueueItem qi;
200         qi.type = QueueItem::Type::REPEAT;
201         qi.reel = video_reel (frame);
202         qi.frame = frame - _reels[qi.reel].start ();
203         if (film()->three_d() && eyes == Eyes::BOTH) {
204                 qi.eyes = Eyes::LEFT;
205                 _queue.push_back (qi);
206                 qi.eyes = Eyes::RIGHT;
207                 _queue.push_back (qi);
208         } else {
209                 qi.eyes = eyes;
210                 _queue.push_back (qi);
211         }
212
213         /* Now there's something to do: wake anything wait()ing on _empty_condition */
214         _empty_condition.notify_all ();
215 }
216
217
218 void
219 Writer::fake_write (Frame frame, Eyes eyes)
220 {
221         boost::mutex::scoped_lock lock (_state_mutex);
222
223         while (_queue.size() > _maximum_queue_size && have_sequenced_image_at_queue_head()) {
224                 /* The queue is too big, and the main writer thread can run and fix it, so
225                    wake it and wait until it has done.
226                 */
227                 _empty_condition.notify_all ();
228                 _full_condition.wait (lock);
229         }
230
231         size_t const reel = video_reel (frame);
232         Frame const frame_in_reel = frame - _reels[reel].start ();
233
234         QueueItem qi;
235         qi.type = QueueItem::Type::FAKE;
236
237         {
238                 shared_ptr<InfoFileHandle> info_file = film()->info_file_handle(_reels[reel].period(), true);
239                 qi.size = _reels[reel].read_frame_info(info_file, frame_in_reel, eyes).size;
240         }
241
242         qi.reel = reel;
243         qi.frame = frame_in_reel;
244         if (film()->three_d() && eyes == Eyes::BOTH) {
245                 qi.eyes = Eyes::LEFT;
246                 _queue.push_back (qi);
247                 qi.eyes = Eyes::RIGHT;
248                 _queue.push_back (qi);
249         } else {
250                 qi.eyes = eyes;
251                 _queue.push_back (qi);
252         }
253
254         /* Now there's something to do: wake anything wait()ing on _empty_condition */
255         _empty_condition.notify_all ();
256 }
257
258
259 /** Write some audio frames to the DCP.
260  *  @param audio Audio data.
261  *  @param time Time of this data within the DCP.
262  *  This method is not thread safe.
263  */
264 void
265 Writer::write (shared_ptr<const AudioBuffers> audio, DCPTime const time)
266 {
267         DCPOMATIC_ASSERT (audio);
268
269         int const afr = film()->audio_frame_rate();
270
271         DCPTime const end = time + DCPTime::from_frames(audio->frames(), afr);
272
273         /* The audio we get might span a reel boundary, and if so we have to write it in bits */
274
275         DCPTime t = time;
276         while (t < end) {
277
278                 if (_audio_reel == _reels.end ()) {
279                         /* This audio is off the end of the last reel; ignore it */
280                         return;
281                 }
282
283                 if (end <= _audio_reel->period().to) {
284                         /* Easy case: we can write all the audio to this reel */
285                         _audio_reel->write (audio);
286                         t = end;
287                 } else if (_audio_reel->period().to <= t) {
288                         /* This reel is entirely before the start of our audio; just skip the reel */
289                         ++_audio_reel;
290                 } else {
291                         /* This audio is over a reel boundary; split the audio into two and write the first part */
292                         DCPTime part_lengths[2] = {
293                                 _audio_reel->period().to - t,
294                                 end - _audio_reel->period().to
295                         };
296
297                         /* Be careful that part_lengths[0] + part_lengths[1] can't be bigger than audio->frames() */
298                         Frame part_frames[2] = {
299                                 part_lengths[0].frames_ceil(afr),
300                                 part_lengths[1].frames_floor(afr)
301                         };
302
303                         DCPOMATIC_ASSERT ((part_frames[0] + part_frames[1]) <= audio->frames());
304
305                         if (part_frames[0]) {
306                                 _audio_reel->write (make_shared<AudioBuffers>(audio, part_frames[0], 0));
307                         }
308
309                         if (part_frames[1]) {
310                                 audio = make_shared<AudioBuffers>(audio, part_frames[1], part_frames[0]);
311                         } else {
312                                 audio.reset ();
313                         }
314
315                         ++_audio_reel;
316                         t += part_lengths[0];
317                 }
318         }
319 }
320
321
322 void
323 Writer::write (shared_ptr<const dcp::AtmosFrame> atmos, DCPTime time, AtmosMetadata metadata)
324 {
325         if (_atmos_reel->period().to == time) {
326                 ++_atmos_reel;
327                 DCPOMATIC_ASSERT (_atmos_reel != _reels.end());
328         }
329
330         /* We assume that we get a video frame's worth of data here */
331         _atmos_reel->write (atmos, metadata);
332 }
333
334
335 /** Caller must hold a lock on _state_mutex */
336 bool
337 Writer::have_sequenced_image_at_queue_head ()
338 {
339         if (_queue.empty ()) {
340                 return false;
341         }
342
343         _queue.sort ();
344         auto const & f = _queue.front();
345         return _last_written[f.reel].next(f);
346 }
347
348
349 bool
350 Writer::LastWritten::next (QueueItem qi) const
351 {
352         if (qi.eyes == Eyes::BOTH) {
353                 /* 2D */
354                 return qi.frame == (_frame + 1);
355         }
356
357         /* 3D */
358
359         if (_eyes == Eyes::LEFT && qi.frame == _frame && qi.eyes == Eyes::RIGHT) {
360                 return true;
361         }
362
363         if (_eyes == Eyes::RIGHT && qi.frame == (_frame + 1) && qi.eyes == Eyes::LEFT) {
364                 return true;
365         }
366
367         return false;
368 }
369
370
371 void
372 Writer::LastWritten::update (QueueItem qi)
373 {
374         _frame = qi.frame;
375         _eyes = qi.eyes;
376 }
377
378
379 void
380 Writer::thread ()
381 try
382 {
383         start_of_thread ("Writer");
384
385         while (true)
386         {
387                 boost::mutex::scoped_lock lock (_state_mutex);
388
389                 while (true) {
390
391                         if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) {
392                                 /* We've got something to do: go and do it */
393                                 break;
394                         }
395
396                         /* Nothing to do: wait until something happens which may indicate that we do */
397                         LOG_TIMING (N_("writer-sleep queue=%1"), _queue.size());
398                         _empty_condition.wait (lock);
399                         LOG_TIMING (N_("writer-wake queue=%1"), _queue.size());
400                 }
401
402                 /* We stop here if we have been asked to finish, and if either the queue
403                    is empty or we do not have a sequenced image at its head (if this is the
404                    case we will never terminate as no new frames will be sent once
405                    _finish is true).
406                 */
407                 if (_finish && (!have_sequenced_image_at_queue_head() || _queue.empty())) {
408                         /* (Hopefully temporarily) log anything that was not written */
409                         if (!_queue.empty() && !have_sequenced_image_at_queue_head()) {
410                                 LOG_WARNING (N_("Finishing writer with a left-over queue of %1:"), _queue.size());
411                                 for (auto const& i: _queue) {
412                                         if (i.type == QueueItem::Type::FULL) {
413                                                 LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i.frame, (int) i.eyes);
414                                         } else {
415                                                 LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i.size, i.frame, (int) i.eyes);
416                                         }
417                                 }
418                         }
419                         return;
420                 }
421
422                 /* Write any frames that we can write; i.e. those that are in sequence. */
423                 while (have_sequenced_image_at_queue_head ()) {
424                         auto qi = _queue.front ();
425                         _last_written[qi.reel].update (qi);
426                         _queue.pop_front ();
427                         if (qi.type == QueueItem::Type::FULL && qi.encoded) {
428                                 --_queued_full_in_memory;
429                         }
430
431                         lock.unlock ();
432
433                         auto& reel = _reels[qi.reel];
434
435                         switch (qi.type) {
436                         case QueueItem::Type::FULL:
437                                 LOG_DEBUG_ENCODE (N_("Writer FULL-writes %1 (%2)"), qi.frame, (int) qi.eyes);
438                                 if (!qi.encoded) {
439                                         qi.encoded.reset (new ArrayData(film()->j2c_path(qi.reel, qi.frame, qi.eyes, false)));
440                                 }
441                                 reel.write (qi.encoded, qi.frame, qi.eyes);
442                                 ++_full_written;
443                                 break;
444                         case QueueItem::Type::FAKE:
445                                 LOG_DEBUG_ENCODE (N_("Writer FAKE-writes %1"), qi.frame);
446                                 reel.fake_write (qi.size);
447                                 ++_fake_written;
448                                 break;
449                         case QueueItem::Type::REPEAT:
450                                 LOG_DEBUG_ENCODE (N_("Writer REPEAT-writes %1"), qi.frame);
451                                 reel.repeat_write (qi.frame, qi.eyes);
452                                 ++_repeat_written;
453                                 break;
454                         }
455
456                         lock.lock ();
457                         _full_condition.notify_all ();
458                 }
459
460                 while (_queued_full_in_memory > _maximum_frames_in_memory) {
461                         /* Too many frames in memory which can't yet be written to the stream.
462                            Write some FULL frames to disk.
463                         */
464
465                         /* Find one from the back of the queue */
466                         _queue.sort ();
467                         auto i = _queue.rbegin ();
468                         while (i != _queue.rend() && (i->type != QueueItem::Type::FULL || !i->encoded)) {
469                                 ++i;
470                         }
471
472                         DCPOMATIC_ASSERT (i != _queue.rend());
473                         ++_pushed_to_disk;
474                         /* For the log message below */
475                         int const awaiting = _last_written[_queue.front().reel].frame() + 1;
476                         lock.unlock ();
477
478                         /* i is valid here, even though we don't hold a lock on the mutex,
479                            since list iterators are unaffected by insertion and only this
480                            thread could erase the last item in the list.
481                         */
482
483                         LOG_GENERAL ("Writer full; pushes %1 to disk while awaiting %2", i->frame, awaiting);
484
485                         i->encoded->write_via_temp (
486                                 film()->j2c_path(i->reel, i->frame, i->eyes, true),
487                                 film()->j2c_path(i->reel, i->frame, i->eyes, false)
488                                 );
489
490                         lock.lock ();
491                         i->encoded.reset ();
492                         --_queued_full_in_memory;
493                         _full_condition.notify_all ();
494                 }
495         }
496 }
497 catch (...)
498 {
499         store_current ();
500 }
501
502
503 void
504 Writer::terminate_thread (bool can_throw)
505 {
506         boost::this_thread::disable_interruption dis;
507
508         boost::mutex::scoped_lock lock (_state_mutex);
509
510         _finish = true;
511         _empty_condition.notify_all ();
512         _full_condition.notify_all ();
513         lock.unlock ();
514
515         try {
516                 _thread.join ();
517         } catch (...) {}
518
519         if (can_throw) {
520                 rethrow ();
521         }
522 }
523
524
525 void
526 Writer::calculate_digests ()
527 {
528         auto job = _job.lock ();
529         if (job) {
530                 job->sub (_("Computing digests"));
531         }
532
533         boost::asio::io_service service;
534         boost::thread_group pool;
535
536         auto work = make_shared<boost::asio::io_service::work>(service);
537
538         int const threads = max (1, Config::instance()->master_encoding_threads());
539
540         for (int i = 0; i < threads; ++i) {
541                 pool.create_thread (boost::bind (&boost::asio::io_service::run, &service));
542         }
543
544         std::function<void (float)> set_progress;
545         if (job) {
546                 set_progress = boost::bind (&Writer::set_digest_progress, this, job.get(), _1);
547         } else {
548                 set_progress = [](float) {
549                         boost::this_thread::interruption_point();
550                 };
551         }
552
553         for (auto& i: _reels) {
554                 service.post (boost::bind (&ReelWriter::calculate_digests, &i, set_progress));
555         }
556         service.post (boost::bind (&Writer::calculate_referenced_digests, this, set_progress));
557
558         work.reset ();
559
560         try {
561                 pool.join_all ();
562         } catch (boost::thread_interrupted) {
563                 /* join_all was interrupted, so we need to interrupt the threads
564                  * in our pool then try again to join them.
565                  */
566                 pool.interrupt_all ();
567                 pool.join_all ();
568         }
569
570         service.stop ();
571 }
572
573
574 /** @param output_dcp Path to DCP folder to write */
575 void
576 Writer::finish (boost::filesystem::path output_dcp)
577 {
578         if (_thread.joinable()) {
579                 LOG_GENERAL_NC ("Terminating writer thread");
580                 terminate_thread (true);
581         }
582
583         LOG_GENERAL_NC ("Finishing ReelWriters");
584
585         for (auto& i: _reels) {
586                 write_hanging_text (i);
587                 i.finish (output_dcp);
588         }
589
590         LOG_GENERAL_NC ("Writing XML");
591
592         dcp::DCP dcp (output_dcp);
593
594         auto cpl = make_shared<dcp::CPL>(
595                 film()->dcp_name(),
596                 film()->dcp_content_type()->libdcp_kind(),
597                 film()->interop() ? dcp::Standard::INTEROP : dcp::Standard::SMPTE
598                 );
599
600         dcp.add (cpl);
601
602         calculate_digests ();
603
604         /* Add reels */
605
606         for (auto& i: _reels) {
607                 cpl->add (i.create_reel(_reel_assets, _fonts, output_dcp, _have_subtitles, _have_closed_captions));
608         }
609
610         /* Add metadata */
611
612         auto creator = Config::instance()->dcp_creator();
613         if (creator.empty()) {
614                 creator = String::compose("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
615         }
616
617         auto issuer = Config::instance()->dcp_issuer();
618         if (issuer.empty()) {
619                 issuer = String::compose("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
620         }
621
622         cpl->set_creator (creator);
623         cpl->set_issuer (issuer);
624
625         cpl->set_ratings (film()->ratings());
626
627         vector<dcp::ContentVersion> cv;
628         for (auto i: film()->content_versions()) {
629                 cv.push_back (dcp::ContentVersion(i));
630         }
631         cpl->set_content_versions (cv);
632
633         cpl->set_full_content_title_text (film()->name());
634         cpl->set_full_content_title_text_language (film()->name_language());
635         if (film()->release_territory()) {
636                 cpl->set_release_territory (*film()->release_territory());
637         }
638         cpl->set_version_number (film()->version_number());
639         cpl->set_status (film()->status());
640         if (film()->chain()) {
641                 cpl->set_chain (*film()->chain());
642         }
643         if (film()->distributor()) {
644                 cpl->set_distributor (*film()->distributor());
645         }
646         if (film()->facility()) {
647                 cpl->set_facility (*film()->facility());
648         }
649         if (film()->luminance()) {
650                 cpl->set_luminance (*film()->luminance());
651         }
652         if (film()->sign_language_video_language()) {
653                 cpl->set_sign_language_video_language (*film()->sign_language_video_language());
654         }
655
656         auto ac = film()->mapped_audio_channels();
657         dcp::MCASoundField field = (
658                 find(ac.begin(), ac.end(), static_cast<int>(dcp::Channel::BSL)) != ac.end() ||
659                 find(ac.begin(), ac.end(), static_cast<int>(dcp::Channel::BSR)) != ac.end()
660                 ) ? dcp::MCASoundField::SEVEN_POINT_ONE : dcp::MCASoundField::FIVE_POINT_ONE;
661
662         dcp::MainSoundConfiguration msc (field, film()->audio_channels());
663         for (auto i: ac) {
664                 if (static_cast<int>(i) < film()->audio_channels()) {
665                         msc.set_mapping (i, static_cast<dcp::Channel>(i));
666                 }
667         }
668
669         cpl->set_main_sound_configuration (msc.to_string());
670         cpl->set_main_sound_sample_rate (film()->audio_frame_rate());
671         cpl->set_main_picture_stored_area (film()->frame_size());
672
673         auto active_area = film()->active_area();
674         if (active_area.width > 0 && active_area.height > 0) {
675                 /* It's not allowed to have a zero active area width or height */
676                 cpl->set_main_picture_active_area (active_area);
677         }
678
679         auto sl = film()->subtitle_languages().second;
680         if (!sl.empty()) {
681                 cpl->set_additional_subtitle_languages(sl);
682         }
683
684         auto signer = Config::instance()->signer_chain();
685         /* We did check earlier, but check again here to be on the safe side */
686         string reason;
687         if (!signer->valid (&reason)) {
688                 throw InvalidSignerError (reason);
689         }
690
691         dcp.write_xml (
692                 issuer,
693                 creator,
694                 dcp::LocalTime().as_string(),
695                 film()->dcp_name(),
696                 signer,
697                 Config::instance()->dcp_metadata_filename_format()
698                 );
699
700         LOG_GENERAL (
701                 N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT, %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk
702                 );
703
704         write_cover_sheet (output_dcp);
705 }
706
707
708 void
709 Writer::write_cover_sheet (boost::filesystem::path output_dcp)
710 {
711         auto const cover = film()->file("COVER_SHEET.txt");
712         auto f = fopen_boost (cover, "w");
713         if (!f) {
714                 throw OpenFileError (cover, errno, OpenFileError::WRITE);
715         }
716
717         auto text = Config::instance()->cover_sheet ();
718         boost::algorithm::replace_all (text, "$CPL_NAME", film()->name());
719         auto cpls = film()->cpls();
720         if (!cpls.empty()) {
721                 boost::algorithm::replace_all (text, "$CPL_FILENAME", cpls[0].cpl_file.filename().string());
722         }
723         boost::algorithm::replace_all (text, "$TYPE", film()->dcp_content_type()->pretty_name());
724         boost::algorithm::replace_all (text, "$CONTAINER", film()->container()->container_nickname());
725
726         auto audio_language = film()->audio_language();
727         if (audio_language) {
728                 boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", audio_language->description());
729         } else {
730                 boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", _("None"));
731         }
732
733         auto subtitle_languages = film()->subtitle_languages();
734         if (subtitle_languages.first) {
735                 boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_languages.first->description());
736         } else {
737                 boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", _("None"));
738         }
739
740         boost::uintmax_t size = 0;
741         for (
742                 auto i = boost::filesystem::recursive_directory_iterator(output_dcp);
743                 i != boost::filesystem::recursive_directory_iterator();
744                 ++i) {
745                 if (boost::filesystem::is_regular_file (i->path())) {
746                         size += boost::filesystem::file_size (i->path());
747                 }
748         }
749
750         if (size > (1000000000L)) {
751                 boost::algorithm::replace_all (text, "$SIZE", String::compose("%1GB", dcp::locale_convert<string>(size / 1000000000.0, 1, true)));
752         } else {
753                 boost::algorithm::replace_all (text, "$SIZE", String::compose("%1MB", dcp::locale_convert<string>(size / 1000000.0, 1, true)));
754         }
755
756         auto ch = audio_channel_types (film()->mapped_audio_channels(), film()->audio_channels());
757         auto description = String::compose("%1.%2", ch.first, ch.second);
758
759         if (description == "0.0") {
760                 description = _("None");
761         } else if (description == "1.0") {
762                 description = _("Mono");
763         } else if (description == "2.0") {
764                 description = _("Stereo");
765         }
766         boost::algorithm::replace_all (text, "$AUDIO", description);
767
768         auto const hmsf = film()->length().split(film()->video_frame_rate());
769         string length;
770         if (hmsf.h == 0 && hmsf.m == 0) {
771                 length = String::compose("%1s", hmsf.s);
772         } else if (hmsf.h == 0 && hmsf.m > 0) {
773                 length = String::compose("%1m%2s", hmsf.m, hmsf.s);
774         } else if (hmsf.h > 0 && hmsf.m > 0) {
775                 length = String::compose("%1h%2m%3s", hmsf.h, hmsf.m, hmsf.s);
776         }
777
778         boost::algorithm::replace_all (text, "$LENGTH", length);
779
780         checked_fwrite (text.c_str(), text.length(), f, cover);
781         fclose (f);
782 }
783
784
785 /** @param frame Frame index within the whole DCP.
786  *  @return true if we can fake-write this frame.
787  */
788 bool
789 Writer::can_fake_write (Frame frame) const
790 {
791         if (film()->encrypted()) {
792                 /* We need to re-write the frame because the asset ID is embedded in the HMAC... I think... */
793                 return false;
794         }
795
796         /* We have to do a proper write of the first frame so that we can set up the JPEG2000
797            parameters in the asset writer.
798         */
799
800         auto const & reel = _reels[video_reel(frame)];
801
802         /* Make frame relative to the start of the reel */
803         frame -= reel.start ();
804         return (frame != 0 && frame < reel.first_nonexistant_frame());
805 }
806
807
808 /** @param track Closed caption track if type == TextType::CLOSED_CAPTION */
809 void
810 Writer::write (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
811 {
812         vector<ReelWriter>::iterator* reel = nullptr;
813
814         switch (type) {
815         case TextType::OPEN_SUBTITLE:
816                 reel = &_subtitle_reel;
817                 _have_subtitles = true;
818                 break;
819         case TextType::CLOSED_CAPTION:
820                 DCPOMATIC_ASSERT (track);
821                 DCPOMATIC_ASSERT (_caption_reels.find(*track) != _caption_reels.end());
822                 reel = &_caption_reels[*track];
823                 _have_closed_captions.insert (*track);
824                 break;
825         default:
826                 DCPOMATIC_ASSERT (false);
827         }
828
829         DCPOMATIC_ASSERT (*reel != _reels.end());
830         while ((*reel)->period().to <= period.from) {
831                 ++(*reel);
832                 DCPOMATIC_ASSERT (*reel != _reels.end());
833                 write_hanging_text (**reel);
834         }
835
836         auto back_off = [this](DCPTimePeriod period) {
837                 period.to -= DCPTime::from_frames(2, film()->video_frame_rate());
838                 return period;
839         };
840
841         if (period.to > (*reel)->period().to) {
842                 /* This text goes off the end of the reel.  Store parts of it that should go into
843                  * other reels.
844                  */
845                 for (auto i = std::next(*reel); i != _reels.end(); ++i) {
846                         auto overlap = i->period().overlap(period);
847                         if (overlap) {
848                                 _hanging_texts.push_back (HangingText{text, type, track, back_off(*overlap)});
849                         }
850                 }
851                 /* Back off from the reel boundary by a couple of frames to avoid tripping checks
852                  * for subtitles being too close together.
853                  */
854                 period.to = (*reel)->period().to;
855                 period = back_off(period);
856         }
857
858         (*reel)->write (text, type, track, period);
859 }
860
861
862 void
863 Writer::write (vector<FontData> fonts)
864 {
865         /* Just keep a list of unique fonts and we'll deal with them in ::finish */
866
867         for (auto const& i: fonts) {
868                 bool got = false;
869                 for (auto& j: _fonts) {
870                         if (i == j) {
871                                 got = true;
872                         }
873                 }
874
875                 if (!got) {
876                         _fonts.push_back (i);
877                 }
878         }
879 }
880
881
882 bool
883 operator< (QueueItem const & a, QueueItem const & b)
884 {
885         if (a.reel != b.reel) {
886                 return a.reel < b.reel;
887         }
888
889         if (a.frame != b.frame) {
890                 return a.frame < b.frame;
891         }
892
893         return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
894 }
895
896
897 bool
898 operator== (QueueItem const & a, QueueItem const & b)
899 {
900         return a.reel == b.reel && a.frame == b.frame && a.eyes == b.eyes;
901 }
902
903
904 void
905 Writer::set_encoder_threads (int threads)
906 {
907         boost::mutex::scoped_lock lm (_state_mutex);
908         _maximum_frames_in_memory = lrint (threads * Config::instance()->frames_in_memory_multiplier());
909         _maximum_queue_size = threads * 16;
910 }
911
912
913 void
914 Writer::write (ReferencedReelAsset asset)
915 {
916         _reel_assets.push_back (asset);
917 }
918
919
920 size_t
921 Writer::video_reel (int frame) const
922 {
923         auto t = DCPTime::from_frames (frame, film()->video_frame_rate());
924         size_t i = 0;
925         while (i < _reels.size() && !_reels[i].period().contains (t)) {
926                 ++i;
927         }
928
929         DCPOMATIC_ASSERT (i < _reels.size ());
930         return i;
931 }
932
933
934 void
935 Writer::set_digest_progress (Job* job, float progress)
936 {
937         boost::mutex::scoped_lock lm (_digest_progresses_mutex);
938
939         _digest_progresses[boost::this_thread::get_id()] = progress;
940         float min_progress = FLT_MAX;
941         for (auto const& i: _digest_progresses) {
942                 min_progress = min (min_progress, i.second);
943         }
944
945         job->set_progress (min_progress);
946
947         Waker waker;
948         waker.nudge ();
949
950         boost::this_thread::interruption_point();
951 }
952
953
954 /** Calculate hashes for any referenced MXF assets which do not already have one */
955 void
956 Writer::calculate_referenced_digests (std::function<void (float)> set_progress)
957 try
958 {
959         for (auto const& i: _reel_assets) {
960                 auto file = dynamic_pointer_cast<dcp::ReelFileAsset>(i.asset);
961                 if (file && !file->hash()) {
962                         file->asset_ref().asset()->hash (set_progress);
963                         file->set_hash (file->asset_ref().asset()->hash());
964                 }
965         }
966 } catch (boost::thread_interrupted) {
967         /* set_progress contains an interruption_point, so any of these methods
968          * may throw thread_interrupted, at which point we just give up.
969          */
970 }
971
972
973 void
974 Writer::write_hanging_text (ReelWriter& reel)
975 {
976         vector<HangingText> new_hanging_texts;
977         for (auto i: _hanging_texts) {
978                 if (i.period.from == reel.period().from) {
979                         reel.write (i.text, i.type, i.track, i.period);
980                 } else {
981                         new_hanging_texts.push_back (i);
982                 }
983         }
984         _hanging_texts = new_hanging_texts;
985 }