Remove make dcp job.
[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<const 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                         String::compose ("video_%1.mxf", 0),
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                                 String::compose ("audio_%1.mxf", 0),
58                                 DCPFrameRate (_film->frames_per_second()).frames_per_second,
59                                 _film->audio_channels(),
60                                 _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                                 _last_written = encoded.first;
128                         } else {
129                                 _picture_asset_writer->write (_last_written->data(), _last_written->size());
130                         }
131                         lock.lock ();
132
133                         ++_last_written_frame;
134                 }
135
136                 while (_queue.size() > _maximum_frames_in_memory) {
137                         /* Too many frames in memory which can't yet be written to the stream.
138                            Put some to disk.
139                         */
140
141                         pair<boost::shared_ptr<const EncodedData>, int> encoded = _queue.back ();
142                         _queue.pop_back ();
143                         if (!encoded.first) {
144                                 /* This is a `repeat-last' frame, so no need to write it to disk */
145                                 continue;
146                         }
147
148                         lock.unlock ();
149                         _film->log()->log (String::compose ("Writer full (awaiting %1); pushes %2 to disk", _last_written_frame + 1, encoded.second));
150                         encoded.first->write (_film, encoded.second);
151                         lock.lock ();
152
153                         _pending.push_back (encoded.second);
154                 }
155
156                 while (_queue.size() < _maximum_frames_in_memory && !_pending.empty()) {
157                         /* We have some space in memory.  Fetch some frames back off disk. */
158
159                         _pending.sort ();
160                         int const fetch = _pending.front ();
161
162                         lock.unlock ();
163                         _film->log()->log (String::compose ("Writer pulls %1 back from disk", fetch));
164                         shared_ptr<const EncodedData> encoded;
165                         if (boost::filesystem::exists (_film->frame_out_path (fetch, false))) {
166                                 /* It's an actual frame (not a repeat-last); load it in */
167                                 encoded.reset (new EncodedData (_film->frame_out_path (fetch, false)));
168                         }
169                         lock.lock ();
170
171                         _queue.push_back (make_pair (encoded, fetch));
172                         _pending.remove (fetch);
173                 }
174         }
175
176 }
177
178 void
179 Writer::finish ()
180 {
181         if (!_thread) {
182                 return;
183         }
184         
185         boost::mutex::scoped_lock lock (_mutex);
186         _finish = true;
187         _condition.notify_all ();
188         lock.unlock ();
189
190         _thread->join ();
191         delete _thread;
192         _thread = 0;
193
194         _picture_asset_writer->finalize ();
195         _sound_asset_writer->finalize ();
196
197
198         int const frames = _film->dcp_intrinsic_duration().get();
199         int const duration = frames - _film->trim_start() - _film->trim_end();
200         
201         _picture_asset->set_entry_point (_film->trim_start ());
202         _picture_asset->set_duration (duration);
203         _sound_asset->set_entry_point (_film->trim_start ());
204         _sound_asset->set_duration (duration);
205         
206         libdcp::DCP dcp (_film->dir (_film->dcp_name()));
207         DCPFrameRate dfr (_film->frames_per_second ());
208
209         shared_ptr<libdcp::CPL> cpl (
210                 new libdcp::CPL (_film->dir (_film->dcp_name()), _film->dcp_name(), _film->dcp_content_type()->libdcp_kind (), frames, dfr.frames_per_second)
211                 );
212         
213         dcp.add_cpl (cpl);
214
215         cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
216                                                          _picture_asset,
217                                                          _sound_asset,
218                                                          shared_ptr<libdcp::SubtitleAsset> ()
219                                                          )
220                                ));
221
222         dcp.write_xml ();
223 }
224
225 /** Tell the writer that frame `f' should be a repeat of the frame before it */
226 void
227 Writer::repeat (int f)
228 {
229         boost::mutex::scoped_lock lock (_mutex);
230         _queue.push_back (make_pair (shared_ptr<const EncodedData> (), f));
231 }