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