WIP: stop using video directory and hard-linking (#2756).
[dcpomatic.git] / src / lib / writer.h
1 /*
2     Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #ifndef DCPOMATIC_WRITER_H
23 #define DCPOMATIC_WRITER_H
24
25
26 /** @file  src/lib/writer.h
27  *  @brief Writer class.
28  */
29
30
31 #include "atmos_metadata.h"
32 #include "dcp_text_track.h"
33 #include "dcpomatic_time.h"
34 #include "exception_store.h"
35 #include "font_id_map.h"
36 #include "player_text.h"
37 #include "text_type.h"
38 #include "types.h"
39 #include "weak_film.h"
40 #include <dcp/atmos_frame.h>
41 #include <dcp/frame_info.h>
42 #include <dcp/mono_mpeg2_picture_frame.h>
43 #include <boost/thread.hpp>
44 #include <boost/thread/condition.hpp>
45 #include <list>
46
47
48 namespace dcp {
49         class Data;
50 }
51
52 class AudioBuffers;
53 class Film;
54 class Job;
55 class ReelWriter;
56 class ReferencedReelAsset;
57 struct writer_disambiguate_font_ids1;
58 struct writer_disambiguate_font_ids2;
59 struct writer_disambiguate_font_ids3;
60
61
62 struct QueueItem
63 {
64 public:
65         QueueItem () {}
66
67         enum class Type {
68                 /** a normal frame with some JPEG200 data */
69                 FULL,
70                 /** a frame whose data already exists in the MXF,
71                     and we fake-write it; i.e. we update the writer's
72                     state but we use the data that is already on disk.
73                 */
74                 FAKE,
75                 REPEAT,
76         } type;
77
78         /** encoded data for FULL */
79         std::shared_ptr<const dcp::Data> encoded;
80         /** info for FAKE */
81         dcp::J2KFrameInfo info;
82         /** reel index */
83         size_t reel = 0;
84         /** frame index within the reel */
85         int frame = 0;
86         /** eyes for FULL, FAKE and REPEAT */
87         Eyes eyes = Eyes::BOTH;
88 };
89
90
91 bool operator< (QueueItem const & a, QueueItem const & b);
92 bool operator== (QueueItem const & a, QueueItem const & b);
93
94
95 /** @class Writer
96  *  @brief Class to manage writing JPEG2000 and audio data to assets on disk.
97  *
98  *  This class creates sound and picture assets, then takes Data
99  *  or AudioBuffers objects (containing image or sound data respectively)
100  *  and writes them to the assets.
101  *
102  *  write() for Data (picture) can be called out of order, and the Writer
103  *  will sort it out.  write() for AudioBuffers must be called in order.
104  */
105
106 class Writer : public ExceptionStore, public WeakConstFilm
107 {
108 public:
109         Writer(std::weak_ptr<const Film>, std::weak_ptr<Job>, boost::filesystem::path output_dir, bool text_only = false);
110         ~Writer ();
111
112         Writer (Writer const &) = delete;
113         Writer& operator= (Writer const &) = delete;
114
115         void start ();
116
117         bool can_fake_write (Frame) const;
118
119         void write (std::shared_ptr<const dcp::Data>, Frame, Eyes);
120         void fake_write (Frame, Eyes);
121         bool can_repeat (Frame) const;
122         void repeat (Frame, Eyes);
123         void write (std::shared_ptr<const AudioBuffers>, dcpomatic::DCPTime time);
124         void write (PlayerText text, TextType type, boost::optional<DCPTextTrack>, dcpomatic::DCPTimePeriod period);
125         void write (std::vector<std::shared_ptr<dcpomatic::Font>> fonts);
126         void write (ReferencedReelAsset asset);
127         void write (std::shared_ptr<const dcp::AtmosFrame> atmos, dcpomatic::DCPTime time, AtmosMetadata metadata);
128         void write (std::shared_ptr<dcp::MonoMPEG2PictureFrame> image, Frame frame);
129         void finish();
130
131         void set_encoder_threads (int threads);
132
133         void zombify();
134
135 private:
136         friend struct ::writer_disambiguate_font_ids1;
137         friend struct ::writer_disambiguate_font_ids2;
138         friend struct ::writer_disambiguate_font_ids3;
139
140         void thread ();
141         void terminate_thread (bool);
142         bool have_sequenced_image_at_queue_head ();
143         size_t video_reel (int frame) const;
144         void set_digest_progress(Job* job, int id, int64_t done, int64_t size);
145         void write_cover_sheet();
146         void calculate_referenced_digests(std::function<void (int64_t, int64_t)> set_progress);
147         void write_hanging_text (ReelWriter& reel);
148         void calculate_digests ();
149
150         std::weak_ptr<Job> _job;
151         std::vector<ReelWriter> _reels;
152         std::vector<ReelWriter>::iterator _audio_reel;
153         std::vector<ReelWriter>::iterator _subtitle_reel;
154         std::map<DCPTextTrack, std::vector<ReelWriter>::iterator> _caption_reels;
155         std::vector<ReelWriter>::iterator _atmos_reel;
156
157         boost::filesystem::path _output_dir;
158         /** our thread */
159         boost::thread _thread;
160         /** true if our thread should finish */
161         bool _finish = false;
162         /** queue of things to write to disk */
163         std::list<QueueItem> _queue;
164         /** number of FULL frames whose JPEG200 data is currently held in RAM */
165         int _queued_full_in_memory = 0;
166         /** mutex for thread state */
167         mutable boost::mutex _state_mutex;
168         /** condition to manage thread wakeups when we have nothing to do  */
169         boost::condition _empty_condition;
170         /** condition to manage thread wakeups when we have too much to do */
171         boost::condition _full_condition;
172         /** maximum number of frames to hold in memory, for when we are managing
173          *  ordering
174          */
175         int _maximum_frames_in_memory;
176         unsigned int _maximum_queue_size;
177
178         class LastWritten
179         {
180         public:
181                 LastWritten()
182                         : _frame(-1)
183                         , _eyes(Eyes::RIGHT)
184                 {}
185
186                 /** @return true if qi is the next item after this one */
187                 bool next (QueueItem qi) const;
188                 void update (QueueItem qi);
189
190                 int frame () const {
191                         return _frame;
192                 }
193
194         private:
195                 int _frame;
196                 Eyes _eyes;
197         };
198
199         /** The last frame written to each reel */
200         std::vector<LastWritten> _last_written;
201
202         /** number of FULL written frames */
203         int _full_written = 0;
204         /** number of FAKE written frames */
205         int _fake_written = 0;
206         int _repeat_written = 0;
207         /** number of frames pushed to disk and then recovered
208             due to the limit of frames to be held in memory.
209         */
210         int _pushed_to_disk = 0;
211
212         bool _text_only;
213
214         boost::mutex _digest_progresses_mutex;
215         std::map<int, std::pair<int64_t, int64_t>> _digest_progresses;
216
217         std::list<ReferencedReelAsset> _reel_assets;
218
219         FontIdMap _fonts;
220         /** If we are given many fonts, but we're making an Interop DCP, we'll choose a single
221          *  one that we'll use everywher.  This is that chosen font.
222          */
223         std::shared_ptr<dcpomatic::Font> _chosen_interop_font;
224
225         /** true if any reel has any subtitles */
226         bool _have_subtitles = false;
227         /** all closed caption tracks that we have on any reel */
228         std::set<DCPTextTrack> _have_closed_captions;
229
230         struct HangingText {
231                 PlayerText text;
232                 TextType type;
233                 boost::optional<DCPTextTrack> track;
234                 dcpomatic::DCPTimePeriod period;
235         };
236
237         std::vector<HangingText> _hanging_texts;
238
239         bool _zombie = false;
240 };
241
242
243 #endif
244