Fix the build for older macOS.
[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                                 shared_ptr<AudioBuffers> part (new AudioBuffers(audio, part_frames[0], 0));
307                                 _audio_reel->write (part);
308                         }
309
310                         if (part_frames[1]) {
311                                 audio.reset (new AudioBuffers(audio, part_frames[1], part_frames[0]));
312                         } else {
313                                 audio.reset ();
314                         }
315
316                         ++_audio_reel;
317                         t += part_lengths[0];
318                 }
319         }
320 }
321
322
323 void
324 Writer::write (shared_ptr<const dcp::AtmosFrame> atmos, DCPTime time, AtmosMetadata metadata)
325 {
326         if (_atmos_reel->period().to == time) {
327                 ++_atmos_reel;
328                 DCPOMATIC_ASSERT (_atmos_reel != _reels.end());
329         }
330
331         /* We assume that we get a video frame's worth of data here */
332         _atmos_reel->write (atmos, metadata);
333 }
334
335
336 /** Caller must hold a lock on _state_mutex */
337 bool
338 Writer::have_sequenced_image_at_queue_head ()
339 {
340         if (_queue.empty ()) {
341                 return false;
342         }
343
344         _queue.sort ();
345         auto const & f = _queue.front();
346         return _last_written[f.reel].next(f);
347 }
348
349
350 bool
351 Writer::LastWritten::next (QueueItem qi) const
352 {
353         if (qi.eyes == Eyes::BOTH) {
354                 /* 2D */
355                 return qi.frame == (_frame + 1);
356         }
357
358         /* 3D */
359
360         if (_eyes == Eyes::LEFT && qi.frame == _frame && qi.eyes == Eyes::RIGHT) {
361                 return true;
362         }
363
364         if (_eyes == Eyes::RIGHT && qi.frame == (_frame + 1) && qi.eyes == Eyes::LEFT) {
365                 return true;
366         }
367
368         return false;
369 }
370
371
372 void
373 Writer::LastWritten::update (QueueItem qi)
374 {
375         _frame = qi.frame;
376         _eyes = qi.eyes;
377 }
378
379
380 void
381 Writer::thread ()
382 try
383 {
384         start_of_thread ("Writer");
385
386         while (true)
387         {
388                 boost::mutex::scoped_lock lock (_state_mutex);
389
390                 while (true) {
391
392                         if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) {
393                                 /* We've got something to do: go and do it */
394                                 break;
395                         }
396
397                         /* Nothing to do: wait until something happens which may indicate that we do */
398                         LOG_TIMING (N_("writer-sleep queue=%1"), _queue.size());
399                         _empty_condition.wait (lock);
400                         LOG_TIMING (N_("writer-wake queue=%1"), _queue.size());
401                 }
402
403                 /* We stop here if we have been asked to finish, and if either the queue
404                    is empty or we do not have a sequenced image at its head (if this is the
405                    case we will never terminate as no new frames will be sent once
406                    _finish is true).
407                 */
408                 if (_finish && (!have_sequenced_image_at_queue_head() || _queue.empty())) {
409                         /* (Hopefully temporarily) log anything that was not written */
410                         if (!_queue.empty() && !have_sequenced_image_at_queue_head()) {
411                                 LOG_WARNING (N_("Finishing writer with a left-over queue of %1:"), _queue.size());
412                                 for (auto const& i: _queue) {
413                                         if (i.type == QueueItem::Type::FULL) {
414                                                 LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i.frame, (int) i.eyes);
415                                         } else {
416                                                 LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i.size, i.frame, (int) i.eyes);
417                                         }
418                                 }
419                         }
420                         return;
421                 }
422
423                 /* Write any frames that we can write; i.e. those that are in sequence. */
424                 while (have_sequenced_image_at_queue_head ()) {
425                         auto qi = _queue.front ();
426                         _last_written[qi.reel].update (qi);
427                         _queue.pop_front ();
428                         if (qi.type == QueueItem::Type::FULL && qi.encoded) {
429                                 --_queued_full_in_memory;
430                         }
431
432                         lock.unlock ();
433
434                         auto& reel = _reels[qi.reel];
435
436                         switch (qi.type) {
437                         case QueueItem::Type::FULL:
438                                 LOG_DEBUG_ENCODE (N_("Writer FULL-writes %1 (%2)"), qi.frame, (int) qi.eyes);
439                                 if (!qi.encoded) {
440                                         qi.encoded.reset (new ArrayData(film()->j2c_path(qi.reel, qi.frame, qi.eyes, false)));
441                                 }
442                                 reel.write (qi.encoded, qi.frame, qi.eyes);
443                                 ++_full_written;
444                                 break;
445                         case QueueItem::Type::FAKE:
446                                 LOG_DEBUG_ENCODE (N_("Writer FAKE-writes %1"), qi.frame);
447                                 reel.fake_write (qi.size);
448                                 ++_fake_written;
449                                 break;
450                         case QueueItem::Type::REPEAT:
451                                 LOG_DEBUG_ENCODE (N_("Writer REPEAT-writes %1"), qi.frame);
452                                 reel.repeat_write (qi.frame, qi.eyes);
453                                 ++_repeat_written;
454                                 break;
455                         }
456
457                         lock.lock ();
458                         _full_condition.notify_all ();
459                 }
460
461                 while (_queued_full_in_memory > _maximum_frames_in_memory) {
462                         /* Too many frames in memory which can't yet be written to the stream.
463                            Write some FULL frames to disk.
464                         */
465
466                         /* Find one from the back of the queue */
467                         _queue.sort ();
468                         auto i = _queue.rbegin ();
469                         while (i != _queue.rend() && (i->type != QueueItem::Type::FULL || !i->encoded)) {
470                                 ++i;
471                         }
472
473                         DCPOMATIC_ASSERT (i != _queue.rend());
474                         ++_pushed_to_disk;
475                         /* For the log message below */
476                         int const awaiting = _last_written[_queue.front().reel].frame() + 1;
477                         lock.unlock ();
478
479                         /* i is valid here, even though we don't hold a lock on the mutex,
480                            since list iterators are unaffected by insertion and only this
481                            thread could erase the last item in the list.
482                         */
483
484                         LOG_GENERAL ("Writer full; pushes %1 to disk while awaiting %2", i->frame, awaiting);
485
486                         i->encoded->write_via_temp (
487                                 film()->j2c_path(i->reel, i->frame, i->eyes, true),
488                                 film()->j2c_path(i->reel, i->frame, i->eyes, false)
489                                 );
490
491                         lock.lock ();
492                         i->encoded.reset ();
493                         --_queued_full_in_memory;
494                         _full_condition.notify_all ();
495                 }
496         }
497 }
498 catch (...)
499 {
500         store_current ();
501 }
502
503
504 void
505 Writer::terminate_thread (bool can_throw)
506 {
507         boost::this_thread::disable_interruption dis;
508
509         boost::mutex::scoped_lock lock (_state_mutex);
510
511         _finish = true;
512         _empty_condition.notify_all ();
513         _full_condition.notify_all ();
514         lock.unlock ();
515
516         try {
517                 _thread.join ();
518         } catch (...) {}
519
520         if (can_throw) {
521                 rethrow ();
522         }
523 }
524
525
526 void
527 Writer::calculate_digests ()
528 {
529         auto job = _job.lock ();
530         if (job) {
531                 job->sub (_("Computing digests"));
532         }
533
534         boost::asio::io_service service;
535         boost::thread_group pool;
536
537         auto work = make_shared<boost::asio::io_service::work>(service);
538
539         int const threads = max (1, Config::instance()->master_encoding_threads());
540
541         for (int i = 0; i < threads; ++i) {
542                 pool.create_thread (boost::bind (&boost::asio::io_service::run, &service));
543         }
544
545         std::function<void (float)> set_progress;
546         if (job) {
547                 set_progress = boost::bind (&Writer::set_digest_progress, this, job.get(), _1);
548         } else {
549                 set_progress = [](float) {
550                         boost::this_thread::interruption_point();
551                 };
552         }
553
554         for (auto& i: _reels) {
555                 service.post (boost::bind (&ReelWriter::calculate_digests, &i, set_progress));
556         }
557         service.post (boost::bind (&Writer::calculate_referenced_digests, this, set_progress));
558
559         work.reset ();
560
561         try {
562                 pool.join_all ();
563         } catch (boost::thread_interrupted) {
564                 /* join_all was interrupted, so we need to interrupt the threads
565                  * in our pool then try again to join them.
566                  */
567                 pool.interrupt_all ();
568                 pool.join_all ();
569         }
570
571         service.stop ();
572 }
573
574
575 /** @param output_dcp Path to DCP folder to write */
576 void
577 Writer::finish (boost::filesystem::path output_dcp)
578 {
579         if (_thread.joinable()) {
580                 LOG_GENERAL_NC ("Terminating writer thread");
581                 terminate_thread (true);
582         }
583
584         LOG_GENERAL_NC ("Finishing ReelWriters");
585
586         for (auto& i: _reels) {
587                 write_hanging_text (i);
588                 i.finish (output_dcp);
589         }
590
591         LOG_GENERAL_NC ("Writing XML");
592
593         dcp::DCP dcp (output_dcp);
594
595         auto cpl = make_shared<dcp::CPL>(
596                 film()->dcp_name(),
597                 film()->dcp_content_type()->libdcp_kind(),
598                 film()->interop() ? dcp::Standard::INTEROP : dcp::Standard::SMPTE
599                 );
600
601         dcp.add (cpl);
602
603         calculate_digests ();
604
605         /* Add reels */
606
607         for (auto& i: _reels) {
608                 cpl->add (i.create_reel(_reel_assets, _fonts, output_dcp, _have_subtitles, _have_closed_captions));
609         }
610
611         /* Add metadata */
612
613         auto creator = Config::instance()->dcp_creator();
614         if (creator.empty()) {
615                 creator = String::compose("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
616         }
617
618         auto issuer = Config::instance()->dcp_issuer();
619         if (issuer.empty()) {
620                 issuer = String::compose("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
621         }
622
623         cpl->set_creator (creator);
624         cpl->set_issuer (issuer);
625
626         cpl->set_ratings (film()->ratings());
627
628         vector<dcp::ContentVersion> cv;
629         for (auto i: film()->content_versions()) {
630                 cv.push_back (dcp::ContentVersion(i));
631         }
632         cpl->set_content_versions (cv);
633
634         cpl->set_full_content_title_text (film()->name());
635         cpl->set_full_content_title_text_language (film()->name_language());
636         if (film()->release_territory()) {
637                 cpl->set_release_territory (*film()->release_territory());
638         }
639         cpl->set_version_number (film()->version_number());
640         cpl->set_status (film()->status());
641         if (film()->chain()) {
642                 cpl->set_chain (*film()->chain());
643         }
644         if (film()->distributor()) {
645                 cpl->set_distributor (*film()->distributor());
646         }
647         if (film()->facility()) {
648                 cpl->set_facility (*film()->facility());
649         }
650         if (film()->luminance()) {
651                 cpl->set_luminance (*film()->luminance());
652         }
653         if (film()->sign_language_video_language()) {
654                 cpl->set_sign_language_video_language (*film()->sign_language_video_language());
655         }
656
657         auto ac = film()->mapped_audio_channels();
658         dcp::MCASoundField field = (
659                 find(ac.begin(), ac.end(), static_cast<int>(dcp::Channel::BSL)) != ac.end() ||
660                 find(ac.begin(), ac.end(), static_cast<int>(dcp::Channel::BSR)) != ac.end()
661                 ) ? dcp::MCASoundField::SEVEN_POINT_ONE : dcp::MCASoundField::FIVE_POINT_ONE;
662
663         dcp::MainSoundConfiguration msc (field, film()->audio_channels());
664         for (auto i: ac) {
665                 if (static_cast<int>(i) < film()->audio_channels()) {
666                         msc.set_mapping (i, static_cast<dcp::Channel>(i));
667                 }
668         }
669
670         cpl->set_main_sound_configuration (msc.to_string());
671         cpl->set_main_sound_sample_rate (film()->audio_frame_rate());
672         cpl->set_main_picture_stored_area (film()->frame_size());
673
674         auto active_area = film()->active_area();
675         if (active_area.width > 0 && active_area.height > 0) {
676                 /* It's not allowed to have a zero active area width or height */
677                 cpl->set_main_picture_active_area (active_area);
678         }
679
680         auto sl = film()->subtitle_languages().second;
681         if (!sl.empty()) {
682                 cpl->set_additional_subtitle_languages(sl);
683         }
684
685         auto signer = Config::instance()->signer_chain();
686         /* We did check earlier, but check again here to be on the safe side */
687         string reason;
688         if (!signer->valid (&reason)) {
689                 throw InvalidSignerError (reason);
690         }
691
692         dcp.write_xml (
693                 issuer,
694                 creator,
695                 dcp::LocalTime().as_string(),
696                 film()->dcp_name(),
697                 signer,
698                 Config::instance()->dcp_metadata_filename_format()
699                 );
700
701         LOG_GENERAL (
702                 N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT, %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk
703                 );
704
705         write_cover_sheet (output_dcp);
706 }
707
708
709 void
710 Writer::write_cover_sheet (boost::filesystem::path output_dcp)
711 {
712         auto const cover = film()->file("COVER_SHEET.txt");
713         auto f = fopen_boost (cover, "w");
714         if (!f) {
715                 throw OpenFileError (cover, errno, OpenFileError::WRITE);
716         }
717
718         auto text = Config::instance()->cover_sheet ();
719         boost::algorithm::replace_all (text, "$CPL_NAME", film()->name());
720         boost::algorithm::replace_all (text, "$TYPE", film()->dcp_content_type()->pretty_name());
721         boost::algorithm::replace_all (text, "$CONTAINER", film()->container()->container_nickname());
722
723         auto audio_language = film()->audio_language();
724         if (audio_language) {
725                 boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", audio_language->description());
726         } else {
727                 boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", _("None"));
728         }
729
730         auto subtitle_languages = film()->subtitle_languages();
731         if (subtitle_languages.first) {
732                 boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_languages.first->description());
733         } else {
734                 boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", _("None"));
735         }
736
737         boost::uintmax_t size = 0;
738         for (
739                 auto i = boost::filesystem::recursive_directory_iterator(output_dcp);
740                 i != boost::filesystem::recursive_directory_iterator();
741                 ++i) {
742                 if (boost::filesystem::is_regular_file (i->path())) {
743                         size += boost::filesystem::file_size (i->path());
744                 }
745         }
746
747         if (size > (1000000000L)) {
748                 boost::algorithm::replace_all (text, "$SIZE", String::compose("%1GB", dcp::locale_convert<string>(size / 1000000000.0, 1, true)));
749         } else {
750                 boost::algorithm::replace_all (text, "$SIZE", String::compose("%1MB", dcp::locale_convert<string>(size / 1000000.0, 1, true)));
751         }
752
753         auto ch = audio_channel_types (film()->mapped_audio_channels(), film()->audio_channels());
754         auto description = String::compose("%1.%2", ch.first, ch.second);
755
756         if (description == "0.0") {
757                 description = _("None");
758         } else if (description == "1.0") {
759                 description = _("Mono");
760         } else if (description == "2.0") {
761                 description = _("Stereo");
762         }
763         boost::algorithm::replace_all (text, "$AUDIO", description);
764
765         auto const hmsf = film()->length().split(film()->video_frame_rate());
766         string length;
767         if (hmsf.h == 0 && hmsf.m == 0) {
768                 length = String::compose("%1s", hmsf.s);
769         } else if (hmsf.h == 0 && hmsf.m > 0) {
770                 length = String::compose("%1m%2s", hmsf.m, hmsf.s);
771         } else if (hmsf.h > 0 && hmsf.m > 0) {
772                 length = String::compose("%1h%2m%3s", hmsf.h, hmsf.m, hmsf.s);
773         }
774
775         boost::algorithm::replace_all (text, "$LENGTH", length);
776
777         checked_fwrite (text.c_str(), text.length(), f, cover);
778         fclose (f);
779 }
780
781
782 /** @param frame Frame index within the whole DCP.
783  *  @return true if we can fake-write this frame.
784  */
785 bool
786 Writer::can_fake_write (Frame frame) const
787 {
788         if (film()->encrypted()) {
789                 /* We need to re-write the frame because the asset ID is embedded in the HMAC... I think... */
790                 return false;
791         }
792
793         /* We have to do a proper write of the first frame so that we can set up the JPEG2000
794            parameters in the asset writer.
795         */
796
797         auto const & reel = _reels[video_reel(frame)];
798
799         /* Make frame relative to the start of the reel */
800         frame -= reel.start ();
801         return (frame != 0 && frame < reel.first_nonexistant_frame());
802 }
803
804
805 /** @param track Closed caption track if type == TextType::CLOSED_CAPTION */
806 void
807 Writer::write (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
808 {
809         vector<ReelWriter>::iterator* reel = nullptr;
810
811         switch (type) {
812         case TextType::OPEN_SUBTITLE:
813                 reel = &_subtitle_reel;
814                 _have_subtitles = true;
815                 break;
816         case TextType::CLOSED_CAPTION:
817                 DCPOMATIC_ASSERT (track);
818                 DCPOMATIC_ASSERT (_caption_reels.find(*track) != _caption_reels.end());
819                 reel = &_caption_reels[*track];
820                 _have_closed_captions.insert (*track);
821                 break;
822         default:
823                 DCPOMATIC_ASSERT (false);
824         }
825
826         DCPOMATIC_ASSERT (*reel != _reels.end());
827         while ((*reel)->period().to <= period.from) {
828                 ++(*reel);
829                 DCPOMATIC_ASSERT (*reel != _reels.end());
830                 write_hanging_text (**reel);
831         }
832
833         auto back_off = [this](DCPTimePeriod period) {
834                 period.to -= DCPTime::from_frames(2, film()->video_frame_rate());
835                 return period;
836         };
837
838         if (period.to > (*reel)->period().to) {
839                 /* This text goes off the end of the reel.  Store parts of it that should go into
840                  * other reels.
841                  */
842                 for (auto i = std::next(*reel); i != _reels.end(); ++i) {
843                         auto overlap = i->period().overlap(period);
844                         if (overlap) {
845                                 _hanging_texts.push_back (HangingText{text, type, track, back_off(*overlap)});
846                         }
847                 }
848                 /* Back off from the reel boundary by a couple of frames to avoid tripping checks
849                  * for subtitles being too close together.
850                  */
851                 period.to = (*reel)->period().to;
852                 period = back_off(period);
853         }
854
855         (*reel)->write (text, type, track, period);
856 }
857
858
859 void
860 Writer::write (vector<FontData> fonts)
861 {
862         /* Just keep a list of unique fonts and we'll deal with them in ::finish */
863
864         for (auto const& i: fonts) {
865                 bool got = false;
866                 for (auto& j: _fonts) {
867                         if (i == j) {
868                                 got = true;
869                         }
870                 }
871
872                 if (!got) {
873                         _fonts.push_back (i);
874                 }
875         }
876 }
877
878
879 bool
880 operator< (QueueItem const & a, QueueItem const & b)
881 {
882         if (a.reel != b.reel) {
883                 return a.reel < b.reel;
884         }
885
886         if (a.frame != b.frame) {
887                 return a.frame < b.frame;
888         }
889
890         return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
891 }
892
893
894 bool
895 operator== (QueueItem const & a, QueueItem const & b)
896 {
897         return a.reel == b.reel && a.frame == b.frame && a.eyes == b.eyes;
898 }
899
900
901 void
902 Writer::set_encoder_threads (int threads)
903 {
904         boost::mutex::scoped_lock lm (_state_mutex);
905         _maximum_frames_in_memory = lrint (threads * Config::instance()->frames_in_memory_multiplier());
906         _maximum_queue_size = threads * 16;
907 }
908
909
910 void
911 Writer::write (ReferencedReelAsset asset)
912 {
913         _reel_assets.push_back (asset);
914 }
915
916
917 size_t
918 Writer::video_reel (int frame) const
919 {
920         auto t = DCPTime::from_frames (frame, film()->video_frame_rate());
921         size_t i = 0;
922         while (i < _reels.size() && !_reels[i].period().contains (t)) {
923                 ++i;
924         }
925
926         DCPOMATIC_ASSERT (i < _reels.size ());
927         return i;
928 }
929
930
931 void
932 Writer::set_digest_progress (Job* job, float progress)
933 {
934         boost::mutex::scoped_lock lm (_digest_progresses_mutex);
935
936         _digest_progresses[boost::this_thread::get_id()] = progress;
937         float min_progress = FLT_MAX;
938         for (auto const& i: _digest_progresses) {
939                 min_progress = min (min_progress, i.second);
940         }
941
942         job->set_progress (min_progress);
943
944         Waker waker;
945         waker.nudge ();
946
947         boost::this_thread::interruption_point();
948 }
949
950
951 /** Calculate hashes for any referenced MXF assets which do not already have one */
952 void
953 Writer::calculate_referenced_digests (std::function<void (float)> set_progress)
954 try
955 {
956         for (auto const& i: _reel_assets) {
957                 auto file = dynamic_pointer_cast<dcp::ReelFileAsset>(i.asset);
958                 if (file && !file->hash()) {
959                         file->asset_ref().asset()->hash (set_progress);
960                         file->set_hash (file->asset_ref().asset()->hash());
961                 }
962         }
963 } catch (boost::thread_interrupted) {
964         /* set_progress contains an interruption_point, so any of these methods
965          * may throw thread_interrupted, at which point we just give up.
966          */
967 }
968
969
970 void
971 Writer::write_hanging_text (ReelWriter& reel)
972 {
973         vector<HangingText> new_hanging_texts;
974         for (auto i: _hanging_texts) {
975                 if (i.period.from == reel.period().from) {
976                         reel.write (i.text, i.type, i.track, i.period);
977                 } else {
978                         new_hanging_texts.push_back (i);
979                 }
980         }
981         _hanging_texts = new_hanging_texts;
982 }