summaryrefslogtreecommitdiff
path: root/src/lib/writer.h
blob: 1cd278221738397523b1d6f1bcdc2e2c0aea8c3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
    Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>

    This file is part of DCP-o-matic.

    DCP-o-matic is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    DCP-o-matic is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.

*/


#ifndef DCPOMATIC_WRITER_H
#define DCPOMATIC_WRITER_H


/** @file  src/lib/writer.h
 *  @brief Writer class.
 */


#include "atmos_metadata.h"
#include "dcp_text_track.h"
#include "dcpomatic_time.h"
#include "exception_store.h"
#include "font_id_map.h"
#include "player_text.h"
#include "text_type.h"
#include "weak_film.h"
#include <dcp/atmos_frame.h>
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include <list>


namespace dcp {
	class Data;
}

class AudioBuffers;
class Film;
class Job;
class ReelWriter;
class ReferencedReelAsset;
struct writer_disambiguate_font_ids1;
struct writer_disambiguate_font_ids2;
struct writer_disambiguate_font_ids3;


struct QueueItem
{
public:
	QueueItem () {}

	enum class Type {
		/** a normal frame with some JPEG200 data */
		FULL,
		/** a frame whose data already exists in the MXF,
		    and we fake-write it; i.e. we update the writer's
		    state but we use the data that is already on disk.
		*/
		FAKE,
		REPEAT,
	} type;

	/** encoded data for FULL */
	std::shared_ptr<const dcp::Data> encoded;
	/** size of data for FAKE */
	int size = 0;
	/** reel index */
	size_t reel = 0;
	/** frame index within the reel */
	int frame = 0;
	/** eyes for FULL, FAKE and REPEAT */
	Eyes eyes = Eyes::BOTH;
};


bool operator< (QueueItem const & a, QueueItem const & b);
bool operator== (QueueItem const & a, QueueItem const & b);


/** @class Writer
 *  @brief Class to manage writing JPEG2000 and audio data to assets on disk.
 *
 *  This class creates sound and picture assets, then takes Data
 *  or AudioBuffers objects (containing image or sound data respectively)
 *  and writes them to the assets.
 *
 *  write() for Data (picture) can be called out of order, and the Writer
 *  will sort it out.  write() for AudioBuffers must be called in order.
 */

class Writer : public ExceptionStore, public WeakConstFilm
{
public:
	Writer (std::weak_ptr<const Film>, std::weak_ptr<Job>, bool text_only = false);
	~Writer ();

	Writer (Writer const &) = delete;
	Writer& operator= (Writer const &) = delete;

	void start ();

	bool can_fake_write (Frame) const;

	void write (std::shared_ptr<const dcp::Data>, Frame, Eyes);
	void fake_write (Frame, Eyes);
	bool can_repeat (Frame) const;
	void repeat (Frame, Eyes);
	void write (std::shared_ptr<const AudioBuffers>, dcpomatic::DCPTime time);
	void write (PlayerText text, TextType type, boost::optional<DCPTextTrack>, dcpomatic::DCPTimePeriod period);
	void write (std::vector<std::shared_ptr<dcpomatic::Font>> fonts);
	void write (ReferencedReelAsset asset);
	void write (std::shared_ptr<const dcp::AtmosFrame> atmos, dcpomatic::DCPTime time, AtmosMetadata metadata);
	void finish (boost::filesystem::path output_dcp);

	void set_encoder_threads (int threads);

private:
	friend struct ::writer_disambiguate_font_ids1;
	friend struct ::writer_disambiguate_font_ids2;
	friend struct ::writer_disambiguate_font_ids3;

	void thread ();
	void terminate_thread (bool);
	bool have_sequenced_image_at_queue_head ();
	size_t video_reel (int frame) const;
	void set_digest_progress(Job* job, int id, int64_t done, int64_t size);
	void write_cover_sheet (boost::filesystem::path output_dcp);
	void calculate_referenced_digests(std::function<void (int64_t, int64_t)> set_progress);
	void write_hanging_text (ReelWriter& reel);
	void calculate_digests ();

	std::weak_ptr<Job> _job;
	std::vector<ReelWriter> _reels;
	std::vector<ReelWriter>::iterator _audio_reel;
	std::vector<ReelWriter>::iterator _subtitle_reel;
	std::map<DCPTextTrack, std::vector<ReelWriter>::iterator> _caption_reels;
	std::vector<ReelWriter>::iterator _atmos_reel;

	/** our thread */
	boost::thread _thread;
	/** true if our thread should finish */
	bool _finish = false;
	/** queue of things to write to disk */
	std::list<QueueItem> _queue;
	/** number of FULL frames whose JPEG200 data is currently held in RAM */
	int _queued_full_in_memory = 0;
	/** mutex for thread state */
	mutable boost::mutex _state_mutex;
	/** condition to manage thread wakeups when we have nothing to do  */
	boost::condition _empty_condition;
	/** condition to manage thread wakeups when we have too much to do */
	boost::condition _full_condition;
	/** maximum number of frames to hold in memory, for when we are managing
	 *  ordering
	 */
	int _maximum_frames_in_memory;
	unsigned int _maximum_queue_size;

	class LastWritten
	{
	public:
		LastWritten()
			: _frame(-1)
			, _eyes(Eyes::RIGHT)
		{}

		/** @return true if qi is the next item after this one */
		bool next (QueueItem qi) const;
		void update (QueueItem qi);

		int frame () const {
			return _frame;
		}

	private:
		int _frame;
		Eyes _eyes;
	};

	/** The last frame written to each reel */
	std::vector<LastWritten> _last_written;

	/** number of FULL written frames */
	int _full_written = 0;
	/** number of FAKE written frames */
	int _fake_written = 0;
	int _repeat_written = 0;
	/** number of frames pushed to disk and then recovered
	    due to the limit of frames to be held in memory.
	*/
	int _pushed_to_disk = 0;

	bool _text_only;

	boost::mutex _digest_progresses_mutex;
	std::map<int, std::pair<int64_t, int64_t>> _digest_progresses;

	std::list<ReferencedReelAsset> _reel_assets;

	FontIdMap _fonts;
	/** If we are given many fonts, but we're making an Interop DCP, we'll choose a single
	 *  one that we'll use everywher.  This is that chosen font.
	 */
	std::shared_ptr<dcpomatic::Font> _chosen_interop_font;

	/** true if any reel has any subtitles */
	bool _have_subtitles = false;
	/** all closed caption tracks that we have on any reel */
	std::set<DCPTextTrack> _have_closed_captions;

	struct HangingText {
		PlayerText text;
		TextType type;
		boost::optional<DCPTextTrack> track;
		dcpomatic::DCPTimePeriod period;
	};

	std::vector<HangingText> _hanging_texts;
};


#endif