+
+ while (_queued_full_in_memory > _maximum_frames_in_memory) {
+ _full_condition.wait (lock);
+ }
+
+ QueueItem qi;
+ qi.type = QueueItem::REPEAT;
+ qi.frame = f;
+ if (_film->three_d() && e == EYES_BOTH) {
+ qi.eyes = EYES_LEFT;
+ _queue.push_back (qi);
+ qi.eyes = EYES_RIGHT;
+ _queue.push_back (qi);
+ } else {
+ qi.eyes = e;
+ _queue.push_back (qi);
+ }
+
+ _empty_condition.notify_all ();
+}
+
+bool
+Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
+{
+ /* Read the frame info as written */
+ FILE* ifi = fopen_boost (_film->info_path (f, eyes), "r");
+ if (!ifi) {
+ _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
+ return false;
+ }
+
+ dcp::FrameInfo info (ifi);
+ fclose (ifi);
+ if (info.size == 0) {
+ _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
+ return false;
+ }
+
+ /* Read the data from the MXF and hash it */
+ dcpomatic_fseek (mxf, info.offset, SEEK_SET);
+ EncodedData data (info.size);
+ size_t const read = fread (data.data(), 1, data.size(), mxf);
+ if (read != static_cast<size_t> (data.size ())) {
+ _film->log()->log (String::compose ("Existing frame %1 is incomplete", f));
+ return false;
+ }
+
+ string const existing_hash = md5_digest (data.data(), data.size());
+ if (existing_hash != info.hash) {
+ _film->log()->log (String::compose ("Existing frame %1 failed hash check", f));
+ return false;
+ }
+
+ return true;
+}
+
+void
+Writer::check_existing_picture_mxf ()
+{
+ /* Try to open the existing MXF */
+ boost::filesystem::path p;
+ p /= _film->internal_video_mxf_dir ();
+ p /= _film->internal_video_mxf_filename ();
+ FILE* mxf = fopen_boost (p, "rb");
+ if (!mxf) {
+ _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno));
+ return;
+ }
+
+ int N = 0;
+ for (boost::filesystem::directory_iterator i (_film->info_dir ()); i != boost::filesystem::directory_iterator (); ++i) {
+ ++N;
+ }
+
+ while (1) {
+
+ shared_ptr<Job> job = _job.lock ();
+ assert (job);
+
+ if (N > 0) {
+ job->set_progress (float (_first_nonexistant_frame) / N);
+ }
+
+ if (_film->three_d ()) {
+ if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_LEFT)) {
+ break;
+ }
+ if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_RIGHT)) {
+ break;
+ }
+ } else {
+ if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_BOTH)) {
+ break;
+ }
+ }
+
+ _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
+ ++_first_nonexistant_frame;
+ }
+
+ fclose (mxf);
+}
+
+/** @param frame Frame index.
+ * @return true if we can fake-write this frame.
+ */
+bool
+Writer::can_fake_write (int frame) const
+{
+ /* We have to do a proper write of the first frame so that we can set up the JPEG2000
+ parameters in the MXF writer.
+ */
+ return (frame != 0 && frame < _first_nonexistant_frame);
+}
+
+bool
+operator< (QueueItem const & a, QueueItem const & b)
+{
+ if (a.frame != b.frame) {
+ return a.frame < b.frame;
+ }
+
+ return static_cast<int> (a.eyes) < static_cast<int> (b.eyes);
+}
+
+bool
+operator== (QueueItem const & a, QueueItem const & b)
+{
+ return a.frame == b.frame && a.eyes == b.eyes;