2 Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
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.
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.
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.
21 #include <libdcp/picture_asset.h>
22 #include <libdcp/sound_asset.h>
23 #include <libdcp/picture_frame.h>
24 #include <libdcp/reel.h>
26 #include "compose.hpp"
30 #include "dcp_video_frame.h"
37 using boost::shared_ptr;
39 unsigned int const Writer::_maximum_frames_in_memory = 8;
41 Writer::Writer (shared_ptr<Film> f)
43 , _first_nonexistant_frame (0)
46 , _last_written_frame (-1)
48 check_existing_picture_mxf ();
50 /* Create our picture asset in a subdirectory, named according to those
51 film's parameters which affect the video output. We will hard-link
52 it into the DCP later.
55 _picture_asset.reset (
56 new libdcp::MonoPictureAsset (
57 _film->video_mxf_dir (),
58 _film->video_mxf_filename (),
59 DCPFrameRate (_film->frames_per_second()).frames_per_second,
60 _film->format()->dcp_size()
64 _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
66 if (_film->audio_channels() > 0) {
68 new libdcp::SoundAsset (
69 _film->dir (_film->dcp_name()),
71 DCPFrameRate (_film->frames_per_second()).frames_per_second,
72 _film->audio_channels(),
73 dcp_audio_sample_rate (_film->audio_stream()->sample_rate())
77 _sound_asset_writer = _sound_asset->start_write ();
80 _thread = new boost::thread (boost::bind (&Writer::thread, this));
84 Writer::write (shared_ptr<const EncodedData> encoded, int frame)
86 boost::mutex::scoped_lock lock (_mutex);
89 qi.type = QueueItem::FULL;
92 _queue.push_back (qi);
94 _condition.notify_all ();
98 Writer::fake_write (int frame)
100 boost::mutex::scoped_lock lock (_mutex);
102 ifstream ifi (_film->info_path (frame).c_str());
103 libdcp::FrameInfo info (ifi);
106 qi.type = QueueItem::FAKE;
109 _queue.push_back (qi);
111 _condition.notify_all ();
114 /** This method is not thread safe */
116 Writer::write (shared_ptr<const AudioBuffers> audio)
118 _sound_asset_writer->write (audio->data(), audio->frames());
126 boost::mutex::scoped_lock lock (_mutex);
130 _queue.size() > _maximum_frames_in_memory ||
131 (!_queue.empty() && _queue.front().frame == (_last_written_frame + 1))) {
136 TIMING ("writer sleeps with a queue of %1; %2 pending", _queue.size(), _pending.size());
137 _condition.wait (lock);
138 TIMING ("writer wakes with a queue of %1", _queue.size());
143 if (_finish && _queue.empty() && _pending.empty()) {
147 /* Write any frames that we can write; i.e. those that are in sequence */
148 while (!_queue.empty() && _queue.front().frame == (_last_written_frame + 1)) {
149 QueueItem qi = _queue.front ();
154 case QueueItem::FULL:
156 _film->log()->log (String::compose ("Writer FULL-writes %1 to MXF", qi.frame));
157 libdcp::FrameInfo const fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
158 qi.encoded->write_info (_film, qi.frame, fin);
159 _last_written = qi.encoded;
162 case QueueItem::FAKE:
163 _film->log()->log (String::compose ("Writer FAKE-writes %1 to MXF", qi.frame));
164 _picture_asset_writer->fake_write (qi.size);
165 _last_written.reset ();
167 case QueueItem::REPEAT:
169 _film->log()->log (String::compose ("Writer REPEAT-writes %1 to MXF", qi.frame));
170 libdcp::FrameInfo const fin = _picture_asset_writer->write (_last_written->data(), _last_written->size());
171 _last_written->write_info (_film, qi.frame, fin);
177 ++_last_written_frame;
180 while (_queue.size() > _maximum_frames_in_memory) {
181 /* Too many frames in memory which can't yet be written to the stream.
182 Put some in our pending list (and write FULL queue items' data to disk)
185 QueueItem qi = _queue.back ();
188 if (qi.type == QueueItem::FULL) {
190 _film->log()->log (String::compose ("Writer full (awaiting %1); pushes %2 to disk", _last_written_frame + 1, qi.frame));
191 qi.encoded->write (_film, qi.frame);
196 _pending.push_back (qi);
199 while (_queue.size() < _maximum_frames_in_memory && !_pending.empty()) {
200 /* We have some space in memory. Fetch some frames back off disk. */
203 QueueItem qi = _pending.front ();
205 if (qi.type == QueueItem::FULL) {
207 _film->log()->log (String::compose ("Writer pulls %1 back from disk", qi.frame));
208 shared_ptr<const EncodedData> encoded;
209 qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, false)));
213 _queue.push_back (qi);
214 _pending.remove (qi);
227 boost::mutex::scoped_lock lock (_mutex);
229 _condition.notify_all ();
236 _picture_asset_writer->finalize ();
238 if (_sound_asset_writer) {
239 _sound_asset_writer->finalize ();
242 int const frames = _last_written_frame + 1;
243 int const duration = frames - _film->trim_start() - _film->trim_end();
245 _film->set_dcp_intrinsic_duration (frames);
247 _picture_asset->set_entry_point (_film->trim_start ());
248 _picture_asset->set_duration (duration);
250 /* Hard-link the video MXF into the DCP */
252 boost::filesystem::path from;
253 from /= _film->video_mxf_dir();
254 from /= _film->video_mxf_filename();
256 boost::filesystem::path to;
257 to /= _film->dir (_film->dcp_name());
260 boost::filesystem::create_hard_link (from, to);
262 /* And update the asset */
264 _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
265 _picture_asset->set_file_name ("video.mxf");
268 _sound_asset->set_entry_point (_film->trim_start ());
269 _sound_asset->set_duration (duration);
272 libdcp::DCP dcp (_film->dir (_film->dcp_name()));
273 DCPFrameRate dfr (_film->frames_per_second ());
275 shared_ptr<libdcp::CPL> cpl (
276 new libdcp::CPL (_film->dir (_film->dcp_name()), _film->dcp_name(), _film->dcp_content_type()->libdcp_kind (), frames, dfr.frames_per_second)
281 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
284 shared_ptr<libdcp::SubtitleAsset> ()
291 /** Tell the writer that frame `f' should be a repeat of the frame before it */
293 Writer::repeat (int f)
295 boost::mutex::scoped_lock lock (_mutex);
298 qi.type = QueueItem::REPEAT;
301 _queue.push_back (qi);
303 _condition.notify_all ();
308 Writer::check_existing_picture_mxf ()
310 /* Try to open the existing MXF */
311 boost::filesystem::path p;
312 p /= _film->video_mxf_dir ();
313 p /= _film->video_mxf_filename ();
314 FILE* mxf = fopen (p.string().c_str(), "rb");
321 /* Read the frame info as written */
322 ifstream ifi (_film->info_path (_first_nonexistant_frame).c_str());
323 libdcp::FrameInfo info (ifi);
325 /* Read the data from the MXF and hash it */
326 fseek (mxf, info.offset, SEEK_SET);
327 EncodedData data (info.size);
328 fread (data.data(), 1, data.size(), mxf);
329 string const existing_hash = md5_digest (data.data(), data.size());
331 if (existing_hash != info.hash) {
332 _film->log()->log (String::compose ("Existing frame %1 failed hash check", _first_nonexistant_frame));
336 _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
337 ++_first_nonexistant_frame;
343 /** @return true if the fake write succeeded, otherwise false */
345 Writer::can_fake_write (int frame) const
347 return (frame != 0 && frame < _first_nonexistant_frame);
352 operator< (QueueItem const & a, QueueItem const & b)
354 return a.frame < b.frame;
358 operator== (QueueItem const & a, QueueItem const & b)
360 return a.frame == b.frame;