d775fde54f21ac1ee4b2d034f926800ae66b175d
[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 "audio_buffers.h"
23 #include "audio_mapping.h"
24 #include "compose.hpp"
25 #include "config.h"
26 #include "cross.h"
27 #include "dcp_content_type.h"
28 #include "dcp_video.h"
29 #include "dcpomatic_log.h"
30 #include "film.h"
31 #include "font_data.h"
32 #include "job.h"
33 #include "log.h"
34 #include "ratio.h"
35 #include "reel_writer.h"
36 #include "text_content.h"
37 #include "util.h"
38 #include "version.h"
39 #include "writer.h"
40 #include <dcp/cpl.h>
41 #include <dcp/locale_convert.h>
42 #include <dcp/reel_file_asset.h>
43 #include <cerrno>
44 #include <cfloat>
45 #include <fstream>
46 #include <iostream>
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::ArrayData;
69 using dcp::Data;
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         if (cv.empty()) {
632                 cv = { dcp::ContentVersion("1") };
633         }
634         cpl->set_content_versions (cv);
635
636         cpl->set_full_content_title_text (film()->name());
637         cpl->set_full_content_title_text_language (film()->name_language());
638         if (film()->release_territory()) {
639                 cpl->set_release_territory (*film()->release_territory());
640         }
641         cpl->set_version_number (film()->version_number());
642         cpl->set_status (film()->status());
643         if (film()->chain()) {
644                 cpl->set_chain (*film()->chain());
645         }
646         if (film()->distributor()) {
647                 cpl->set_distributor (*film()->distributor());
648         }
649         if (film()->facility()) {
650                 cpl->set_facility (*film()->facility());
651         }
652         if (film()->luminance()) {
653                 cpl->set_luminance (*film()->luminance());
654         }
655         if (film()->sign_language_video_language()) {
656                 cpl->set_sign_language_video_language (*film()->sign_language_video_language());
657         }
658
659         auto ac = film()->mapped_audio_channels();
660         dcp::MCASoundField field = (
661                 find(ac.begin(), ac.end(), static_cast<int>(dcp::Channel::BSL)) != ac.end() ||
662                 find(ac.begin(), ac.end(), static_cast<int>(dcp::Channel::BSR)) != ac.end()
663                 ) ? dcp::MCASoundField::SEVEN_POINT_ONE : dcp::MCASoundField::FIVE_POINT_ONE;
664
665         dcp::MainSoundConfiguration msc (field, film()->audio_channels());
666         for (auto i: ac) {
667                 if (static_cast<int>(i) < film()->audio_channels()) {
668                         msc.set_mapping (i, static_cast<dcp::Channel>(i));
669                 }
670         }
671
672         cpl->set_main_sound_configuration (msc.to_string());
673         cpl->set_main_sound_sample_rate (film()->audio_frame_rate());
674         cpl->set_main_picture_stored_area (film()->frame_size());
675
676         auto active_area = film()->active_area();
677         if (active_area.width > 0 && active_area.height > 0) {
678                 /* It's not allowed to have a zero active area width or height */
679                 cpl->set_main_picture_active_area (active_area);
680         }
681
682         auto sl = film()->subtitle_languages().second;
683         if (!sl.empty()) {
684                 cpl->set_additional_subtitle_languages(sl);
685         }
686
687         auto signer = Config::instance()->signer_chain();
688         /* We did check earlier, but check again here to be on the safe side */
689         string reason;
690         if (!signer->valid (&reason)) {
691                 throw InvalidSignerError (reason);
692         }
693
694         dcp.write_xml (
695                 issuer,
696                 creator,
697                 dcp::LocalTime().as_string(),
698                 film()->dcp_name(),
699                 signer,
700                 Config::instance()->dcp_metadata_filename_format()
701                 );
702
703         LOG_GENERAL (
704                 N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT, %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk
705                 );
706
707         write_cover_sheet (output_dcp);
708 }
709
710
711 void
712 Writer::write_cover_sheet (boost::filesystem::path output_dcp)
713 {
714         auto const cover = film()->file("COVER_SHEET.txt");
715         auto f = fopen_boost (cover, "w");
716         if (!f) {
717                 throw OpenFileError (cover, errno, OpenFileError::WRITE);
718         }
719
720         auto text = Config::instance()->cover_sheet ();
721         boost::algorithm::replace_all (text, "$CPL_NAME", film()->name());
722         auto cpls = film()->cpls();
723         if (!cpls.empty()) {
724                 boost::algorithm::replace_all (text, "$CPL_FILENAME", cpls[0].cpl_file.filename().string());
725         }
726         boost::algorithm::replace_all (text, "$TYPE", film()->dcp_content_type()->pretty_name());
727         boost::algorithm::replace_all (text, "$CONTAINER", film()->container()->container_nickname());
728
729         auto audio_language = film()->audio_language();
730         if (audio_language) {
731                 boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", audio_language->description());
732         } else {
733                 boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", _("None"));
734         }
735
736         auto subtitle_languages = film()->subtitle_languages();
737         if (subtitle_languages.first) {
738                 boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_languages.first->description());
739         } else {
740                 boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", _("None"));
741         }
742
743         boost::uintmax_t size = 0;
744         for (
745                 auto i = boost::filesystem::recursive_directory_iterator(output_dcp);
746                 i != boost::filesystem::recursive_directory_iterator();
747                 ++i) {
748                 if (boost::filesystem::is_regular_file (i->path())) {
749                         size += boost::filesystem::file_size (i->path());
750                 }
751         }
752
753         if (size > (1000000000L)) {
754                 boost::algorithm::replace_all (text, "$SIZE", String::compose("%1GB", dcp::locale_convert<string>(size / 1000000000.0, 1, true)));
755         } else {
756                 boost::algorithm::replace_all (text, "$SIZE", String::compose("%1MB", dcp::locale_convert<string>(size / 1000000.0, 1, true)));
757         }
758
759         auto ch = audio_channel_types (film()->mapped_audio_channels(), film()->audio_channels());
760         auto description = String::compose("%1.%2", ch.first, ch.second);
761
762         if (description == "0.0") {
763                 description = _("None");
764         } else if (description == "1.0") {
765                 description = _("Mono");
766         } else if (description == "2.0") {
767                 description = _("Stereo");
768         }
769         boost::algorithm::replace_all (text, "$AUDIO", description);
770
771         auto const hmsf = film()->length().split(film()->video_frame_rate());
772         string length;
773         if (hmsf.h == 0 && hmsf.m == 0) {
774                 length = String::compose("%1s", hmsf.s);
775         } else if (hmsf.h == 0 && hmsf.m > 0) {
776                 length = String::compose("%1m%2s", hmsf.m, hmsf.s);
777         } else if (hmsf.h > 0 && hmsf.m > 0) {
778                 length = String::compose("%1h%2m%3s", hmsf.h, hmsf.m, hmsf.s);
779         }
780
781         boost::algorithm::replace_all (text, "$LENGTH", length);
782
783         checked_fwrite (text.c_str(), text.length(), f, cover);
784         fclose (f);
785 }
786
787
788 /** @param frame Frame index within the whole DCP.
789  *  @return true if we can fake-write this frame.
790  */
791 bool
792 Writer::can_fake_write (Frame frame) const
793 {
794         if (film()->encrypted()) {
795                 /* We need to re-write the frame because the asset ID is embedded in the HMAC... I think... */
796                 return false;
797         }
798
799         /* We have to do a proper write of the first frame so that we can set up the JPEG2000
800            parameters in the asset writer.
801         */
802
803         auto const & reel = _reels[video_reel(frame)];
804
805         /* Make frame relative to the start of the reel */
806         frame -= reel.start ();
807         return (frame != 0 && frame < reel.first_nonexistant_frame());
808 }
809
810
811 /** @param track Closed caption track if type == TextType::CLOSED_CAPTION */
812 void
813 Writer::write (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
814 {
815         vector<ReelWriter>::iterator* reel = nullptr;
816
817         switch (type) {
818         case TextType::OPEN_SUBTITLE:
819                 reel = &_subtitle_reel;
820                 _have_subtitles = true;
821                 break;
822         case TextType::CLOSED_CAPTION:
823                 DCPOMATIC_ASSERT (track);
824                 DCPOMATIC_ASSERT (_caption_reels.find(*track) != _caption_reels.end());
825                 reel = &_caption_reels[*track];
826                 _have_closed_captions.insert (*track);
827                 break;
828         default:
829                 DCPOMATIC_ASSERT (false);
830         }
831
832         DCPOMATIC_ASSERT (*reel != _reels.end());
833         while ((*reel)->period().to <= period.from) {
834                 ++(*reel);
835                 DCPOMATIC_ASSERT (*reel != _reels.end());
836                 write_hanging_text (**reel);
837         }
838
839         auto back_off = [this](DCPTimePeriod period) {
840                 period.to -= DCPTime::from_frames(2, film()->video_frame_rate());
841                 return period;
842         };
843
844         if (period.to > (*reel)->period().to) {
845                 /* This text goes off the end of the reel.  Store parts of it that should go into
846                  * other reels.
847                  */
848                 for (auto i = std::next(*reel); i != _reels.end(); ++i) {
849                         auto overlap = i->period().overlap(period);
850                         if (overlap) {
851                                 _hanging_texts.push_back (HangingText{text, type, track, back_off(*overlap)});
852                         }
853                 }
854                 /* Back off from the reel boundary by a couple of frames to avoid tripping checks
855                  * for subtitles being too close together.
856                  */
857                 period.to = (*reel)->period().to;
858                 period = back_off(period);
859         }
860
861         (*reel)->write (text, type, track, period);
862 }
863
864
865 void
866 Writer::write (vector<FontData> fonts)
867 {
868         /* Just keep a list of unique fonts and we'll deal with them in ::finish */
869
870         for (auto const& i: fonts) {
871                 bool got = false;
872                 for (auto& j: _fonts) {
873                         if (i == j) {
874                                 got = true;
875                         }
876                 }
877
878                 if (!got) {
879                         _fonts.push_back (i);
880                 }
881         }
882 }
883
884
885 bool
886 operator< (QueueItem const & a, QueueItem const & b)
887 {
888         if (a.reel != b.reel) {
889                 return a.reel < b.reel;
890         }
891
892         if (a.frame != b.frame) {
893                 return a.frame < b.frame;
894         }
895
896         return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
897 }
898
899
900 bool
901 operator== (QueueItem const & a, QueueItem const & b)
902 {
903         return a.reel == b.reel && a.frame == b.frame && a.eyes == b.eyes;
904 }
905
906
907 void
908 Writer::set_encoder_threads (int threads)
909 {
910         boost::mutex::scoped_lock lm (_state_mutex);
911         _maximum_frames_in_memory = lrint (threads * Config::instance()->frames_in_memory_multiplier());
912         _maximum_queue_size = threads * 16;
913 }
914
915
916 void
917 Writer::write (ReferencedReelAsset asset)
918 {
919         _reel_assets.push_back (asset);
920 }
921
922
923 size_t
924 Writer::video_reel (int frame) const
925 {
926         auto t = DCPTime::from_frames (frame, film()->video_frame_rate());
927         size_t i = 0;
928         while (i < _reels.size() && !_reels[i].period().contains (t)) {
929                 ++i;
930         }
931
932         DCPOMATIC_ASSERT (i < _reels.size ());
933         return i;
934 }
935
936
937 void
938 Writer::set_digest_progress (Job* job, float progress)
939 {
940         boost::mutex::scoped_lock lm (_digest_progresses_mutex);
941
942         _digest_progresses[boost::this_thread::get_id()] = progress;
943         float min_progress = FLT_MAX;
944         for (auto const& i: _digest_progresses) {
945                 min_progress = min (min_progress, i.second);
946         }
947
948         job->set_progress (min_progress);
949
950         Waker waker;
951         waker.nudge ();
952
953         boost::this_thread::interruption_point();
954 }
955
956
957 /** Calculate hashes for any referenced MXF assets which do not already have one */
958 void
959 Writer::calculate_referenced_digests (std::function<void (float)> set_progress)
960 try
961 {
962         for (auto const& i: _reel_assets) {
963                 auto file = dynamic_pointer_cast<dcp::ReelFileAsset>(i.asset);
964                 if (file && !file->hash()) {
965                         file->asset_ref().asset()->hash (set_progress);
966                         file->set_hash (file->asset_ref().asset()->hash());
967                 }
968         }
969 } catch (boost::thread_interrupted) {
970         /* set_progress contains an interruption_point, so any of these methods
971          * may throw thread_interrupted, at which point we just give up.
972          */
973 }
974
975
976 void
977 Writer::write_hanging_text (ReelWriter& reel)
978 {
979         vector<HangingText> new_hanging_texts;
980         for (auto i: _hanging_texts) {
981                 if (i.period.from == reel.period().from) {
982                         reel.write (i.text, i.type, i.track, i.period);
983                 } else {
984                         new_hanging_texts.push_back (i);
985                 }
986         }
987         _hanging_texts = new_hanging_texts;
988 }