Try to fix up paths for video MXFs, hashes and temporarily-stored frames.
[dcpomatic.git] / src / lib / writer.cc
1 /*
2     Copyright (C) 2012 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 <libdcp/picture_asset.h>
21 #include <libdcp/sound_asset.h>
22 #include <libdcp/reel.h>
23 #include "writer.h"
24 #include "compose.hpp"
25 #include "film.h"
26 #include "format.h"
27 #include "log.h"
28 #include "dcp_video_frame.h"
29
30 using std::make_pair;
31 using std::pair;
32 using boost::shared_ptr;
33
34 unsigned int const Writer::_maximum_frames_in_memory = 8;
35
36 Writer::Writer (shared_ptr<Film> f)
37         : _film (f)
38         , _thread (0)
39         , _finish (false)
40         , _last_written_frame (-1)
41 {
42         _picture_asset.reset (
43                 new libdcp::MonoPictureAsset (
44                         _film->dir (_film->dcp_name()),
45                         _film->video_mxf_path(),
46                         DCPFrameRate (_film->frames_per_second()).frames_per_second,
47                         _film->format()->dcp_size()
48                         )
49                 );
50         
51         _picture_asset_writer = _picture_asset->start_write ();
52
53         if (_film->audio_channels() > 0) {
54                 _sound_asset.reset (
55                         new libdcp::SoundAsset (
56                                 _film->dir (_film->dcp_name()),
57                                 "audio.mxf",
58                                 DCPFrameRate (_film->frames_per_second()).frames_per_second,
59                                 _film->audio_channels(),
60                                 dcp_audio_sample_rate (_film->audio_stream()->sample_rate())
61                                 )
62                         );
63
64                 _sound_asset_writer = _sound_asset->start_write ();
65         }
66         
67         _thread = new boost::thread (boost::bind (&Writer::thread, this));
68 }
69
70 void
71 Writer::write (shared_ptr<const EncodedData> encoded, int frame)
72 {
73         boost::mutex::scoped_lock lock (_mutex);
74         _queue.push_back (make_pair (encoded, frame));
75         _condition.notify_all ();
76 }
77
78 /** This method is not thread safe */
79 void
80 Writer::write (shared_ptr<const AudioBuffers> audio)
81 {
82         _sound_asset_writer->write (audio->data(), audio->frames());
83 }
84
85 struct QueueSorter
86 {
87         bool operator() (pair<shared_ptr<const EncodedData>, int> const & a, pair<shared_ptr<const EncodedData>, int> const & b) {
88                 return a.second < b.second;
89         }
90 };
91
92 void
93 Writer::thread ()
94 {
95         while (1)
96         {
97                 boost::mutex::scoped_lock lock (_mutex);
98
99                 while (1) {
100                         if (_finish ||
101                             _queue.size() > _maximum_frames_in_memory ||
102                             (!_queue.empty() && _queue.front().second == (_last_written_frame + 1))) {
103                                     
104                                     break;
105                             }
106
107                             TIMING ("writer sleeps with a queue of %1; %2 pending", _queue.size(), _pending.size());
108                             _condition.wait (lock);
109                             TIMING ("writer wakes with a queue of %1", _queue.size());
110
111                             _queue.sort (QueueSorter ());
112                 }
113
114                 if (_finish && _queue.empty() && _pending.empty()) {
115                         return;
116                 }
117
118                 /* Write any frames that we can write; i.e. those that are in sequence */
119                 while (!_queue.empty() && _queue.front().second == (_last_written_frame + 1)) {
120                         pair<boost::shared_ptr<const EncodedData>, int> encoded = _queue.front ();
121                         _queue.pop_front ();
122
123                         lock.unlock ();
124                         _film->log()->log (String::compose ("Writer writes %1 to MXF", encoded.second));
125                         if (encoded.first) {
126                                 _picture_asset_writer->write (encoded.first->data(), encoded.first->size());
127                                 encoded.first->write_hash (_film, encoded.second);
128                                 _last_written = encoded.first;
129                         } else {
130                                 _picture_asset_writer->write (_last_written->data(), _last_written->size());
131                                 _last_written->write_hash (_film, encoded.second);
132                         }
133                         lock.lock ();
134
135                         ++_last_written_frame;
136                 }
137
138                 while (_queue.size() > _maximum_frames_in_memory) {
139                         /* Too many frames in memory which can't yet be written to the stream.
140                            Put some to disk.
141                         */
142
143                         pair<boost::shared_ptr<const EncodedData>, int> encoded = _queue.back ();
144                         _queue.pop_back ();
145                         if (!encoded.first) {
146                                 /* This is a `repeat-last' frame, so no need to write it to disk */
147                                 continue;
148                         }
149
150                         lock.unlock ();
151                         _film->log()->log (String::compose ("Writer full (awaiting %1); pushes %2 to disk", _last_written_frame + 1, encoded.second));
152                         encoded.first->write (_film, encoded.second);
153                         lock.lock ();
154
155                         _pending.push_back (encoded.second);
156                 }
157
158                 while (_queue.size() < _maximum_frames_in_memory && !_pending.empty()) {
159                         /* We have some space in memory.  Fetch some frames back off disk. */
160
161                         _pending.sort ();
162                         int const fetch = _pending.front ();
163
164                         lock.unlock ();
165                         _film->log()->log (String::compose ("Writer pulls %1 back from disk", fetch));
166                         shared_ptr<const EncodedData> encoded;
167                         if (boost::filesystem::exists (_film->j2c_path (fetch, false))) {
168                                 /* It's an actual frame (not a repeat-last); load it in */
169                                 encoded.reset (new EncodedData (_film->j2c_path (fetch, false)));
170                         }
171                         lock.lock ();
172
173                         _queue.push_back (make_pair (encoded, fetch));
174                         _pending.remove (fetch);
175                 }
176         }
177
178 }
179
180 void
181 Writer::finish ()
182 {
183         if (!_thread) {
184                 return;
185         }
186         
187         boost::mutex::scoped_lock lock (_mutex);
188         _finish = true;
189         _condition.notify_all ();
190         lock.unlock ();
191
192         _thread->join ();
193         delete _thread;
194         _thread = 0;
195
196         _picture_asset_writer->finalize ();
197
198         if (_sound_asset_writer) {
199                 _sound_asset_writer->finalize ();
200         }
201
202         int const frames = _last_written_frame + 1;
203         int const duration = frames - _film->trim_start() - _film->trim_end();
204         
205         _film->set_dcp_intrinsic_duration (frames);
206         
207         _picture_asset->set_entry_point (_film->trim_start ());
208         _picture_asset->set_duration (duration);
209
210         if (_sound_asset) {
211                 _sound_asset->set_entry_point (_film->trim_start ());
212                 _sound_asset->set_duration (duration);
213         }
214         
215         libdcp::DCP dcp (_film->dir (_film->dcp_name()));
216         DCPFrameRate dfr (_film->frames_per_second ());
217
218         shared_ptr<libdcp::CPL> cpl (
219                 new libdcp::CPL (_film->dir (_film->dcp_name()), _film->dcp_name(), _film->dcp_content_type()->libdcp_kind (), frames, dfr.frames_per_second)
220                 );
221         
222         dcp.add_cpl (cpl);
223
224         cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
225                                                          _picture_asset,
226                                                          _sound_asset,
227                                                          shared_ptr<libdcp::SubtitleAsset> ()
228                                                          )
229                                ));
230
231         dcp.write_xml ();
232 }
233
234 /** Tell the writer that frame `f' should be a repeat of the frame before it */
235 void
236 Writer::repeat (int f)
237 {
238         boost::mutex::scoped_lock lock (_mutex);
239         _queue.push_back (make_pair (shared_ptr<const EncodedData> (), f));
240 }