Various multi-reel fixes.
[dcpomatic.git] / src / lib / writer.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "writer.h"
21 #include "compose.hpp"
22 #include "film.h"
23 #include "ratio.h"
24 #include "log.h"
25 #include "dcp_video.h"
26 #include "dcp_content_type.h"
27 #include "audio_mapping.h"
28 #include "config.h"
29 #include "job.h"
30 #include "cross.h"
31 #include "audio_buffers.h"
32 #include "md5_digester.h"
33 #include "data.h"
34 #include "version.h"
35 #include "font.h"
36 #include "util.h"
37 #include "referenced_reel_asset.h"
38 #include <dcp/mono_picture_asset.h>
39 #include <dcp/stereo_picture_asset.h>
40 #include <dcp/sound_asset.h>
41 #include <dcp/sound_asset_writer.h>
42 #include <dcp/reel.h>
43 #include <dcp/reel_mono_picture_asset.h>
44 #include <dcp/reel_stereo_picture_asset.h>
45 #include <dcp/reel_sound_asset.h>
46 #include <dcp/reel_subtitle_asset.h>
47 #include <dcp/dcp.h>
48 #include <dcp/cpl.h>
49 #include <dcp/certificate_chain.h>
50 #include <dcp/interop_subtitle_asset.h>
51 #include <dcp/smpte_subtitle_asset.h>
52 #include <boost/foreach.hpp>
53 #include <fstream>
54 #include <cerrno>
55 #include <iostream>
56
57 #include "i18n.h"
58
59 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
60 #define LOG_GENERAL_NC(...) _film->log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
61 #define LOG_DEBUG_ENCODE(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_DEBUG_ENCODE);
62 #define LOG_TIMING(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_TIMING);
63 #define LOG_WARNING_NC(...) _film->log()->log (__VA_ARGS__, LogEntry::TYPE_WARNING);
64 #define LOG_WARNING(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_WARNING);
65 #define LOG_ERROR(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_ERROR);
66
67 /* OS X strikes again */
68 #undef set_key
69
70 using std::make_pair;
71 using std::pair;
72 using std::string;
73 using std::list;
74 using std::cout;
75 using boost::shared_ptr;
76 using boost::weak_ptr;
77 using boost::dynamic_pointer_cast;
78
79 int const Writer::_info_size = 48;
80
81 Writer::Writer (shared_ptr<const Film> film, weak_ptr<Job> j)
82         : _film (film)
83         , _job (j)
84         , _thread (0)
85         , _finish (false)
86         , _queued_full_in_memory (0)
87         , _last_written_frame (-1)
88         , _last_written_eyes (EYES_RIGHT)
89         , _maximum_frames_in_memory (0)
90         , _full_written (0)
91         , _fake_written (0)
92         , _repeat_written (0)
93         , _ref_written (0)
94         , _pushed_to_disk (0)
95 {
96         /* Remove any old DCP */
97         boost::filesystem::remove_all (_film->dir (_film->dcp_name ()));
98
99         shared_ptr<Job> job = _job.lock ();
100         DCPOMATIC_ASSERT (job);
101
102         BOOST_FOREACH (DCPTimePeriod p, _film->reels ()) {
103                 Reel reel;
104                 reel.period = p;
105
106                 /* Create our picture asset in a subdirectory, named according to those
107                    film's parameters which affect the video output.  We will hard-link
108                    it into the DCP later.
109                 */
110
111                 if (_film->three_d ()) {
112                         reel.picture_asset.reset (new dcp::StereoPictureAsset (dcp::Fraction (_film->video_frame_rate (), 1)));
113                 } else {
114                         reel.picture_asset.reset (new dcp::MonoPictureAsset (dcp::Fraction (_film->video_frame_rate (), 1)));
115                 }
116
117                 reel.picture_asset->set_size (_film->frame_size ());
118
119                 if (_film->encrypted ()) {
120                         reel.picture_asset->set_key (_film->key ());
121                 }
122
123                 reel.picture_asset->set_file (
124                         _film->internal_video_asset_dir() / _film->internal_video_asset_filename(p)
125                         );
126
127                 job->sub (_("Checking existing image data"));
128                 check_existing_picture_asset (reel);
129
130                 reel.picture_asset_writer = reel.picture_asset->start_write (
131                         _film->internal_video_asset_dir() / _film->internal_video_asset_filename(p),
132                         _film->interop() ? dcp::INTEROP : dcp::SMPTE,
133                         reel.first_nonexistant_frame > 0
134                         );
135
136                 if (_film->audio_channels ()) {
137                         reel.sound_asset.reset (
138                                 new dcp::SoundAsset (dcp::Fraction (_film->video_frame_rate(), 1), _film->audio_frame_rate (), _film->audio_channels ())
139                         );
140
141                         if (_film->encrypted ()) {
142                                 reel.sound_asset->set_key (_film->key ());
143                         }
144
145                         /* Write the sound asset into the film directory so that we leave the creation
146                            of the DCP directory until the last minute.
147                         */
148                         reel.sound_asset_writer = reel.sound_asset->start_write (
149                                 _film->directory() / audio_asset_filename (reel.sound_asset),
150                                 _film->interop() ? dcp::INTEROP : dcp::SMPTE
151                                 );
152                 }
153
154                 _reels.push_back (reel);
155         }
156
157         /* We can keep track of the current audio and subtitle reels easily because audio
158            and subs arrive to the Writer in sequence.  This is not so for video.
159         */
160         _audio_reel = _reels.begin ();
161         _subtitle_reel = _reels.begin ();
162
163         /* Check that the signer is OK if we need one */
164         if (_film->is_signed() && !Config::instance()->signer_chain()->valid ()) {
165                 throw InvalidSignerError ();
166         }
167
168         job->sub (_("Encoding image data"));
169 }
170
171 void
172 Writer::start ()
173 {
174         _thread = new boost::thread (boost::bind (&Writer::thread, this));
175 }
176
177 Writer::~Writer ()
178 {
179         terminate_thread (false);
180 }
181
182 /** @param frame Frame within the DCP */
183 void
184 Writer::write (Data encoded, Frame frame, Eyes eyes)
185 {
186         boost::mutex::scoped_lock lock (_state_mutex);
187
188         while (_queued_full_in_memory > _maximum_frames_in_memory) {
189                 /* The queue is too big; wait until that is sorted out */
190                 _full_condition.wait (lock);
191         }
192
193         QueueItem qi;
194         qi.type = QueueItem::FULL;
195         qi.encoded = encoded;
196         qi.frame = frame;
197
198         if (_film->three_d() && eyes == EYES_BOTH) {
199                 /* 2D material in a 3D DCP; fake the 3D */
200                 qi.eyes = EYES_LEFT;
201                 _queue.push_back (qi);
202                 ++_queued_full_in_memory;
203                 qi.eyes = EYES_RIGHT;
204                 _queue.push_back (qi);
205                 ++_queued_full_in_memory;
206         } else {
207                 qi.eyes = eyes;
208                 _queue.push_back (qi);
209                 ++_queued_full_in_memory;
210         }
211
212         /* Now there's something to do: wake anything wait()ing on _empty_condition */
213         _empty_condition.notify_all ();
214 }
215
216 void
217 Writer::repeat (Frame frame, Eyes eyes)
218 {
219         boost::mutex::scoped_lock lock (_state_mutex);
220
221         while (_queued_full_in_memory > _maximum_frames_in_memory) {
222                 /* The queue is too big; wait until that is sorted out */
223                 _full_condition.wait (lock);
224         }
225
226         QueueItem qi;
227         qi.type = QueueItem::REPEAT;
228         qi.frame = frame;
229         if (_film->three_d() && eyes == EYES_BOTH) {
230                 qi.eyes = EYES_LEFT;
231                 _queue.push_back (qi);
232                 qi.eyes = EYES_RIGHT;
233                 _queue.push_back (qi);
234         } else {
235                 qi.eyes = eyes;
236                 _queue.push_back (qi);
237         }
238
239         /* Now there's something to do: wake anything wait()ing on _empty_condition */
240         _empty_condition.notify_all ();
241 }
242
243 void
244 Writer::fake_write (Frame frame, Eyes eyes)
245 {
246         boost::mutex::scoped_lock lock (_state_mutex);
247
248         while (_queued_full_in_memory > _maximum_frames_in_memory) {
249                 /* The queue is too big; wait until that is sorted out */
250                 _full_condition.wait (lock);
251         }
252
253         Reel const & reel = video_reel (frame);
254
255         FILE* file = fopen_boost (_film->info_file(reel.period), "rb");
256         if (!file) {
257                 throw ReadFileError (_film->info_file(reel.period));
258         }
259         dcp::FrameInfo info = read_frame_info (file, frame, eyes);
260         fclose (file);
261
262         QueueItem qi;
263         qi.type = QueueItem::FAKE;
264         qi.size = info.size;
265         qi.frame = frame;
266         if (_film->three_d() && eyes == EYES_BOTH) {
267                 qi.eyes = EYES_LEFT;
268                 _queue.push_back (qi);
269                 qi.eyes = EYES_RIGHT;
270                 _queue.push_back (qi);
271         } else {
272                 qi.eyes = eyes;
273                 _queue.push_back (qi);
274         }
275
276         /* Now there's something to do: wake anything wait()ing on _empty_condition */
277         _empty_condition.notify_all ();
278 }
279
280 void
281 Writer::ref_write (Frame frame)
282 {
283         boost::mutex::scoped_lock lock (_state_mutex);
284
285         while (_queued_full_in_memory > _maximum_frames_in_memory) {
286                 /* The queue is too big; wait until that is sorted out */
287                 _full_condition.wait (lock);
288         }
289
290         QueueItem qi;
291         qi.type = QueueItem::REF;
292         qi.frame = frame;
293         qi.eyes = EYES_BOTH;
294         _queue.push_back (qi);
295
296         /* Now there's something to do: wake anything wait()ing on _empty_condition */
297         _empty_condition.notify_all ();
298 }
299
300 /** Write one video frame's worth of audio frames to the DCP.
301  *  This method is not thread safe.
302  */
303 void
304 Writer::write (shared_ptr<const AudioBuffers> audio)
305 {
306         if (!_audio_reel->sound_asset_writer) {
307                 return;
308         }
309
310         if (audio) {
311                 _audio_reel->sound_asset_writer->write (audio->data(), audio->frames());
312         }
313
314         ++_audio_reel->written;
315
316         cout << "(written " << _audio_reel->written << "); period is " << _audio_reel->period.duration() << "\n";
317
318         /* written is in video frames, not audio frames */
319         if (_audio_reel->written >= _audio_reel->period.duration().frames_round (_film->video_frame_rate())) {
320                 cout << "NEXT AUDIO REEL!\n";
321                 ++_audio_reel;
322         }
323 }
324
325 /** This must be called from Writer::thread() with an appropriate lock held */
326 bool
327 Writer::have_sequenced_image_at_queue_head ()
328 {
329         if (_queue.empty ()) {
330                 return false;
331         }
332
333         _queue.sort ();
334
335         /* The queue should contain only EYES_LEFT/EYES_RIGHT pairs or EYES_BOTH */
336
337         if (_queue.front().eyes == EYES_BOTH) {
338                 /* 2D */
339                 return _queue.front().frame == (_last_written_frame + 1);
340         }
341
342         /* 3D */
343
344         if (_last_written_eyes == EYES_LEFT && _queue.front().frame == _last_written_frame && _queue.front().eyes == EYES_RIGHT) {
345                 return true;
346         }
347
348         if (_last_written_eyes == EYES_RIGHT && _queue.front().frame == (_last_written_frame + 1) && _queue.front().eyes == EYES_LEFT) {
349                 return true;
350         }
351
352         return false;
353 }
354
355 void
356 Writer::write_frame_info (int frame, Eyes eyes, dcp::FrameInfo info) const
357 {
358         Reel const & reel = video_reel (frame);
359         FILE* file = 0;
360         boost::filesystem::path info_file = _film->info_file (reel.period);
361         if (boost::filesystem::exists (info_file)) {
362                 file = fopen_boost (info_file, "r+b");
363         } else {
364                 file = fopen_boost (info_file, "wb");
365         }
366         if (!file) {
367                 throw OpenFileError (info_file);
368         }
369         dcpomatic_fseek (file, frame_info_position (frame, eyes), SEEK_SET);
370         fwrite (&info.offset, sizeof (info.offset), 1, file);
371         fwrite (&info.size, sizeof (info.size), 1, file);
372         fwrite (info.hash.c_str(), 1, info.hash.size(), file);
373         fclose (file);
374 }
375
376 void
377 Writer::thread ()
378 try
379 {
380         while (true)
381         {
382                 boost::mutex::scoped_lock lock (_state_mutex);
383
384                 while (true) {
385
386                         if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) {
387                                 /* We've got something to do: go and do it */
388                                 break;
389                         }
390
391                         /* Nothing to do: wait until something happens which may indicate that we do */
392                         LOG_TIMING (N_("writer-sleep queue=%1"), _queue.size());
393                         _empty_condition.wait (lock);
394                         LOG_TIMING (N_("writer-wake queue=%1"), _queue.size());
395                 }
396
397                 if (_finish && _queue.empty()) {
398                         return;
399                 }
400
401                 /* We stop here if we have been asked to finish, and if either the queue
402                    is empty or we do not have a sequenced image at its head (if this is the
403                    case we will never terminate as no new frames will be sent once
404                    _finish is true).
405                 */
406                 if (_finish && (!have_sequenced_image_at_queue_head() || _queue.empty())) {
407                         /* (Hopefully temporarily) log anything that was not written */
408                         if (!_queue.empty() && !have_sequenced_image_at_queue_head()) {
409                                 LOG_WARNING (N_("Finishing writer with a left-over queue of %1:"), _queue.size());
410                                 for (list<QueueItem>::const_iterator i = _queue.begin(); i != _queue.end(); ++i) {
411                                         if (i->type == QueueItem::FULL) {
412                                                 LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i->frame, i->eyes);
413                                         } else {
414                                                 LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i->size, i->frame, i->eyes);
415                                         }
416                                 }
417                                 LOG_WARNING (N_("Last written frame %1, last written eyes %2"), _last_written_frame, _last_written_eyes);
418                         }
419                         return;
420                 }
421                 /* Write any frames that we can write; i.e. those that are in sequence. */
422                 while (have_sequenced_image_at_queue_head ()) {
423                         QueueItem qi = _queue.front ();
424                         _queue.pop_front ();
425                         if (qi.type == QueueItem::FULL && qi.encoded) {
426                                 --_queued_full_in_memory;
427                         }
428
429                         lock.unlock ();
430
431                         Reel const & reel = video_reel (qi.frame);
432
433                         switch (qi.type) {
434                         case QueueItem::FULL:
435                         {
436                                 LOG_DEBUG_ENCODE (N_("Writer FULL-writes %1 (%2)"), qi.frame, qi.eyes);
437                                 if (!qi.encoded) {
438                                         qi.encoded = Data (_film->j2c_path (qi.frame, qi.eyes, false));
439                                 }
440
441                                 dcp::FrameInfo fin = reel.picture_asset_writer->write (qi.encoded->data().get (), qi.encoded->size());
442                                 write_frame_info (qi.frame, qi.eyes, fin);
443                                 _last_written[qi.eyes] = qi.encoded;
444                                 ++_full_written;
445                                 break;
446                         }
447                         case QueueItem::FAKE:
448                                 LOG_DEBUG_ENCODE (N_("Writer FAKE-writes %1"), qi.frame);
449                                 reel.picture_asset_writer->fake_write (qi.size);
450                                 ++_fake_written;
451                                 break;
452                         case QueueItem::REPEAT:
453                         {
454                                 LOG_DEBUG_ENCODE (N_("Writer REPEAT-writes %1"), qi.frame);
455                                 dcp::FrameInfo fin = reel.picture_asset_writer->write (
456                                         _last_written[qi.eyes]->data().get(),
457                                         _last_written[qi.eyes]->size()
458                                         );
459                                 write_frame_info (qi.frame, qi.eyes, fin);
460                                 ++_repeat_written;
461                                 break;
462                         }
463                         case QueueItem::REF:
464                                 LOG_DEBUG_ENCODE (N_("Writer REF-writes %1"), qi.frame);
465                                 ++_ref_written;
466                                 break;
467                         }
468
469                         lock.lock ();
470
471                         _last_written_frame = qi.frame;
472                         _last_written_eyes = qi.eyes;
473
474                         shared_ptr<Job> job = _job.lock ();
475                         DCPOMATIC_ASSERT (job);
476                         int64_t total = _film->length().frames_round (_film->video_frame_rate ());
477                         if (_film->three_d ()) {
478                                 /* _full_written and so on are incremented for each eye, so we need to double the total
479                                    frames to get the correct progress.
480                                 */
481                                 total *= 2;
482                         }
483                         if (total) {
484                                 job->set_progress (float (_full_written + _fake_written + _repeat_written + _ref_written) / total);
485                         }
486                 }
487
488                 while (_queued_full_in_memory > _maximum_frames_in_memory) {
489                         /* Too many frames in memory which can't yet be written to the stream.
490                            Write some FULL frames to disk.
491                         */
492
493                         /* Find one from the back of the queue */
494                         _queue.sort ();
495                         list<QueueItem>::reverse_iterator i = _queue.rbegin ();
496                         while (i != _queue.rend() && (i->type != QueueItem::FULL || !i->encoded)) {
497                                 ++i;
498                         }
499
500                         DCPOMATIC_ASSERT (i != _queue.rend());
501                         ++_pushed_to_disk;
502                         lock.unlock ();
503
504                         /* i is valid here, even though we don't hold a lock on the mutex,
505                            since list iterators are unaffected by insertion and only this
506                            thread could erase the last item in the list.
507                         */
508
509                         LOG_GENERAL (
510                                 "Writer full (awaiting %1 [last eye was %2]); pushes %3 to disk",
511                                 _last_written_frame + 1,
512                                 _last_written_eyes, i->frame
513                                 );
514
515                         i->encoded->write_via_temp (_film->j2c_path (i->frame, i->eyes, true), _film->j2c_path (i->frame, i->eyes, false));
516
517                         lock.lock ();
518                         i->encoded.reset ();
519                         --_queued_full_in_memory;
520                 }
521
522                 /* The queue has probably just gone down a bit; notify anything wait()ing on _full_condition */
523                 _full_condition.notify_all ();
524         }
525 }
526 catch (...)
527 {
528         store_current ();
529 }
530
531 void
532 Writer::terminate_thread (bool can_throw)
533 {
534         boost::mutex::scoped_lock lock (_state_mutex);
535         if (_thread == 0) {
536                 return;
537         }
538
539         _finish = true;
540         _empty_condition.notify_all ();
541         _full_condition.notify_all ();
542         lock.unlock ();
543
544         DCPOMATIC_ASSERT (_thread->joinable ());
545         _thread->join ();
546         if (can_throw) {
547                 rethrow ();
548         }
549
550         delete _thread;
551         _thread = 0;
552 }
553
554 void
555 Writer::finish ()
556 {
557         if (!_thread) {
558                 return;
559         }
560
561         terminate_thread (true);
562
563         BOOST_FOREACH (Reel& i, _reels) {
564
565                 if (!i.picture_asset_writer->finalize ()) {
566                         /* Nothing was written to the picture asset */
567                         i.picture_asset.reset ();
568                 }
569
570                 if (i.sound_asset_writer && !i.sound_asset_writer->finalize ()) {
571                         /* Nothing was written to the sound asset */
572                         cout << "nothing written to reel @ " << i.period << "\n";
573                         i.sound_asset.reset ();
574                 } else {
575                         cout << "something written to reel @ " << i.period << "\n";
576                 }
577
578                 /* Hard-link any video asset file into the DCP */
579                 if (i.picture_asset) {
580                         boost::filesystem::path video_from = i.picture_asset->file ();
581                         boost::filesystem::path video_to;
582                         video_to /= _film->dir (_film->dcp_name());
583                         video_to /= video_asset_filename (i.picture_asset);
584
585                         boost::system::error_code ec;
586                         boost::filesystem::create_hard_link (video_from, video_to, ec);
587                         if (ec) {
588                                 LOG_WARNING_NC ("Hard-link failed; copying instead");
589                                 boost::filesystem::copy_file (video_from, video_to, ec);
590                                 if (ec) {
591                                         LOG_ERROR ("Failed to copy video file from %1 to %2 (%3)", video_from.string(), video_to.string(), ec.message ());
592                                         throw FileError (ec.message(), video_from);
593                                 }
594                         }
595
596                         i.picture_asset->set_file (video_to);
597                 }
598
599                 /* Move the audio asset into the DCP */
600                 if (i.sound_asset) {
601                         boost::filesystem::path audio_to;
602                         audio_to /= _film->dir (_film->dcp_name ());
603                         audio_to /= audio_asset_filename (i.sound_asset);
604
605                         boost::system::error_code ec;
606                         boost::filesystem::rename (_film->file (audio_asset_filename (i.sound_asset)), audio_to, ec);
607                         if (ec) {
608                                 throw FileError (
609                                         String::compose (_("could not move audio asset into the DCP (%1)"), ec.value ()), audio_asset_filename (i.sound_asset)
610                                         );
611                         }
612
613                         i.sound_asset->set_file (audio_to);
614                 }
615         }
616
617         dcp::DCP dcp (_film->dir (_film->dcp_name()));
618
619         shared_ptr<dcp::CPL> cpl (
620                 new dcp::CPL (
621                         _film->dcp_name(),
622                         _film->dcp_content_type()->libdcp_kind ()
623                         )
624                 );
625
626         dcp.add (cpl);
627
628         BOOST_FOREACH (Reel& i, _reels) {
629                 shared_ptr<dcp::Reel> reel (new dcp::Reel ());
630
631                 shared_ptr<dcp::ReelPictureAsset> reel_picture_asset;
632
633                 if (i.picture_asset) {
634                         /* We have made a picture asset of our own.  Put it into the reel */
635                         shared_ptr<dcp::MonoPictureAsset> mono = dynamic_pointer_cast<dcp::MonoPictureAsset> (i.picture_asset);
636                         if (mono) {
637                                 reel_picture_asset.reset (new dcp::ReelMonoPictureAsset (mono, 0));
638                         }
639
640                         shared_ptr<dcp::StereoPictureAsset> stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (i.picture_asset);
641                         if (stereo) {
642                                 reel_picture_asset.reset (new dcp::ReelStereoPictureAsset (stereo, 0));
643                         }
644                 } else {
645                         /* We don't have a picture asset of our own; hopefully we have one to reference */
646                         BOOST_FOREACH (ReferencedReelAsset j, _reel_assets) {
647                                 shared_ptr<dcp::ReelPictureAsset> k = dynamic_pointer_cast<dcp::ReelPictureAsset> (j.asset);
648                                 if (k && j.period == i.period) {
649                                         reel_picture_asset = k;
650                                 }
651                         }
652                 }
653
654                 reel->add (reel_picture_asset);
655
656                 if (i.sound_asset) {
657                         /* We have made a sound asset of our own.  Put it into the reel */
658                         reel->add (shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (i.sound_asset, 0)));
659                 } else {
660                         /* We don't have a sound asset of our own; hopefully we have one to reference */
661                         BOOST_FOREACH (ReferencedReelAsset j, _reel_assets) {
662                                 shared_ptr<dcp::ReelSoundAsset> k = dynamic_pointer_cast<dcp::ReelSoundAsset> (j.asset);
663                                 if (k && j.period == i.period) {
664                                         reel->add (k);
665                                 }
666                         }
667                 }
668
669                 if (i.subtitle_asset) {
670                         boost::filesystem::path liberation;
671                         try {
672                                 liberation = shared_path () / "LiberationSans-Regular.ttf";
673                         } catch (boost::filesystem::filesystem_error& e) {
674                                 /* Hack: try the debian/ubuntu location if getting the shared path failed */
675                                 liberation = "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf";
676                         }
677
678                         /* Add all the fonts to the subtitle content */
679                         BOOST_FOREACH (shared_ptr<Font> j, _fonts) {
680                                 i.subtitle_asset->add_font (j->id(), j->file().get_value_or (liberation));
681                         }
682
683                         if (dynamic_pointer_cast<dcp::InteropSubtitleAsset> (i.subtitle_asset)) {
684                                 boost::filesystem::path directory = _film->dir (_film->dcp_name ()) / i.subtitle_asset->id ();
685                                 boost::filesystem::create_directories (directory);
686                                 i.subtitle_asset->write (directory / ("sub_" + i.subtitle_asset->id() + ".xml"));
687                         } else {
688
689                                 /* All our assets should be the same length; use the picture asset length here
690                                    as a reference to set the subtitle one.
691                                 */
692                                 dynamic_pointer_cast<dcp::SMPTESubtitleAsset>(i.subtitle_asset)->set_intrinsic_duration (
693                                         reel_picture_asset->intrinsic_duration ()
694                                         );
695
696                                 i.subtitle_asset->write (
697                                         _film->dir (_film->dcp_name ()) / ("sub_" + i.subtitle_asset->id() + ".mxf")
698                                         );
699                         }
700
701                         reel->add (shared_ptr<dcp::ReelSubtitleAsset> (
702                                            new dcp::ReelSubtitleAsset (
703                                                    i.subtitle_asset,
704                                                    dcp::Fraction (_film->video_frame_rate(), 1),
705                                                    reel_picture_asset->intrinsic_duration (),
706                                                    0
707                                                    )
708                                            ));
709                 } else {
710                         /* We don't have a subtitle asset of our own; hopefully we have one to reference */
711                         BOOST_FOREACH (ReferencedReelAsset j, _reel_assets) {
712                                 shared_ptr<dcp::ReelSubtitleAsset> k = dynamic_pointer_cast<dcp::ReelSubtitleAsset> (j.asset);
713                                 if (k && j.period == i.period) {
714                                         reel->add (k);
715                                 }
716                         }
717                 }
718
719                 cpl->add (reel);
720
721                 shared_ptr<Job> job = _job.lock ();
722                 DCPOMATIC_ASSERT (job);
723
724                 job->sub (_("Computing image digest"));
725                 if (i.picture_asset) {
726                         i.picture_asset->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
727                 }
728
729                 if (i.sound_asset) {
730                         job->sub (_("Computing audio digest"));
731                         i.sound_asset->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
732                 }
733         }
734
735         dcp::XMLMetadata meta;
736         meta.creator = Config::instance()->dcp_creator ();
737         if (meta.creator.empty ()) {
738                 meta.creator = String::compose ("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
739         }
740         meta.issuer = Config::instance()->dcp_issuer ();
741         if (meta.issuer.empty ()) {
742                 meta.issuer = String::compose ("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
743         }
744         meta.set_issue_date_now ();
745
746         cpl->set_metadata (meta);
747
748         shared_ptr<const dcp::CertificateChain> signer;
749         if (_film->is_signed ()) {
750                 signer = Config::instance()->signer_chain ();
751                 /* We did check earlier, but check again here to be on the safe side */
752                 if (!signer->valid ()) {
753                         throw InvalidSignerError ();
754                 }
755         }
756
757         dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, signer);
758
759         LOG_GENERAL (
760                 N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT, %4 REF, %5 pushed to disk"), _full_written, _fake_written, _repeat_written, _ref_written, _pushed_to_disk
761                 );
762 }
763
764 void
765 Writer::check_existing_picture_asset (Reel& reel)
766 {
767         /* Try to open the existing asset */
768         FILE* asset_file = fopen_boost (reel.picture_asset->file(), "rb");
769         if (!asset_file) {
770                 LOG_GENERAL ("Could not open existing asset at %1 (errno=%2)", reel.picture_asset->file().string(), errno);
771                 return;
772         }
773
774         /* Offset of the last dcp::FrameInfo in the info file */
775         int const n = (boost::filesystem::file_size (_film->info_file(reel.period)) / _info_size) - 1;
776
777         FILE* info_file = fopen_boost (_film->info_file(reel.period), "rb");
778         if (!info_file) {
779                 LOG_GENERAL_NC ("Could not open film info file");
780                 fclose (asset_file);
781                 return;
782         }
783
784         if (_film->three_d ()) {
785                 /* Start looking at the last left frame */
786                 reel.first_nonexistant_frame = n / 2;
787         } else {
788                 reel.first_nonexistant_frame = n;
789         }
790
791         bool ok = false;
792
793         while (!ok) {
794                 /* Read the data from the info file; for 3D we just check the left
795                    frames until we find a good one.
796                 */
797                 dcp::FrameInfo info = read_frame_info (info_file, reel.first_nonexistant_frame, _film->three_d () ? EYES_LEFT : EYES_BOTH);
798
799                 ok = true;
800
801                 /* Read the data from the asset and hash it */
802                 dcpomatic_fseek (asset_file, info.offset, SEEK_SET);
803                 Data data (info.size);
804                 size_t const read = fread (data.data().get(), 1, data.size(), asset_file);
805                 if (read != static_cast<size_t> (data.size ())) {
806                         LOG_GENERAL ("Existing frame %1 is incomplete", reel.first_nonexistant_frame);
807                         ok = false;
808                 } else {
809                         MD5Digester digester;
810                         digester.add (data.data().get(), data.size());
811                         if (digester.get() != info.hash) {
812                                 LOG_GENERAL ("Existing frame %1 failed hash check", reel.first_nonexistant_frame);
813                                 ok = false;
814                         }
815                 }
816
817                 if (!ok) {
818                         --reel.first_nonexistant_frame;
819                 }
820         }
821
822         if (!_film->three_d ()) {
823                 /* If we are doing 3D we might have found a good L frame with no R, so only
824                    do this if we're in 2D and we've just found a good B(oth) frame.
825                 */
826                 ++reel.first_nonexistant_frame;
827         }
828
829         fclose (asset_file);
830         fclose (info_file);
831 }
832
833 /** @param frame Frame index within the whole DCP.
834  *  @return true if we can fake-write this frame.
835  */
836 bool
837 Writer::can_fake_write (Frame frame) const
838 {
839         /* We have to do a proper write of the first frame so that we can set up the JPEG2000
840            parameters in the asset writer.
841         */
842
843         Reel const & reel = video_reel (frame);
844
845         /* Make frame relative to the start of the reel */
846         frame -= reel.period.from.frames_floor (_film->video_frame_rate());
847         return (frame != 0 && frame < reel.first_nonexistant_frame);
848 }
849
850 void
851 Writer::write (PlayerSubtitles subs)
852 {
853         if (subs.text.empty ()) {
854                 return;
855         }
856
857         if (_subtitle_reel->period.to < subs.from) {
858                 ++_subtitle_reel;
859         }
860
861         if (!_subtitle_reel->subtitle_asset) {
862                 string lang = _film->subtitle_language ();
863                 if (lang.empty ()) {
864                         lang = "Unknown";
865                 }
866                 if (_film->interop ()) {
867                         shared_ptr<dcp::InteropSubtitleAsset> s (new dcp::InteropSubtitleAsset ());
868                         s->set_movie_title (_film->name ());
869                         s->set_language (lang);
870                         s->set_reel_number ("1");
871                         _subtitle_reel->subtitle_asset = s;
872                 } else {
873                         shared_ptr<dcp::SMPTESubtitleAsset> s (new dcp::SMPTESubtitleAsset ());
874                         s->set_content_title_text (_film->name ());
875                         s->set_language (lang);
876                         s->set_edit_rate (dcp::Fraction (_film->video_frame_rate (), 1));
877                         s->set_reel_number (1);
878                         s->set_time_code_rate (_film->video_frame_rate ());
879                         s->set_start_time (dcp::Time ());
880                         _subtitle_reel->subtitle_asset = s;
881                 }
882         }
883
884         for (list<dcp::SubtitleString>::const_iterator i = subs.text.begin(); i != subs.text.end(); ++i) {
885                 _subtitle_reel->subtitle_asset->add (*i);
886         }
887 }
888
889 void
890 Writer::write (list<shared_ptr<Font> > fonts)
891 {
892         /* Just keep a list of fonts and we'll deal with them in ::finish */
893         copy (fonts.begin (), fonts.end (), back_inserter (_fonts));
894 }
895
896 bool
897 operator< (QueueItem const & a, QueueItem const & b)
898 {
899         if (a.frame != b.frame) {
900                 return a.frame < b.frame;
901         }
902
903         return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
904 }
905
906 bool
907 operator== (QueueItem const & a, QueueItem const & b)
908 {
909         return a.frame == b.frame && a.eyes == b.eyes;
910 }
911
912 void
913 Writer::set_encoder_threads (int threads)
914 {
915         _maximum_frames_in_memory = lrint (threads * 1.1);
916 }
917
918 long
919 Writer::frame_info_position (int frame, Eyes eyes) const
920 {
921         switch (eyes) {
922         case EYES_BOTH:
923                 return frame * _info_size;
924         case EYES_LEFT:
925                 return frame * _info_size * 2;
926         case EYES_RIGHT:
927                 return frame * _info_size * 2 + _info_size;
928         default:
929                 DCPOMATIC_ASSERT (false);
930         }
931
932         DCPOMATIC_ASSERT (false);
933 }
934
935 dcp::FrameInfo
936 Writer::read_frame_info (FILE* file, int frame, Eyes eyes) const
937 {
938         dcp::FrameInfo info;
939         dcpomatic_fseek (file, frame_info_position (frame, eyes), SEEK_SET);
940         fread (&info.offset, sizeof (info.offset), 1, file);
941         fread (&info.size, sizeof (info.size), 1, file);
942
943         char hash_buffer[33];
944         fread (hash_buffer, 1, 32, file);
945         hash_buffer[32] = '\0';
946         info.hash = hash_buffer;
947
948         return info;
949 }
950
951 void
952 Writer::write (ReferencedReelAsset asset)
953 {
954         _reel_assets.push_back (asset);
955 }
956
957 Writer::Reel const &
958 Writer::video_reel (int frame) const
959 {
960         DCPTime t = DCPTime::from_frames (frame, _film->video_frame_rate ());
961         list<Reel>::const_iterator i = _reels.begin ();
962         while (i != _reels.end() && !i->period.contains (t)) {
963                 ++i;
964         }
965
966         DCPOMATIC_ASSERT (i != _reels.end ());
967         return *i;
968 }