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