WIP: more
[dcpomatic.git] / src / lib / piece.cc
1 /*
2     Copyright (C) 2013-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 #include "atmos_decoder.h"
23 #include "atmos_metadata.h"
24 #include "audio_content.h"
25 #include "audio_decoder.h"
26 #include "content.h"
27 #include "dcp_content.h"
28 #include "dcpomatic_log.h"
29 #include "decoder.h"
30 #include "film.h"
31 #include "piece.h"
32 #include "piece_video.h"
33 #include "player_video.h"
34 #include "resampler.h"
35 #include "text_content.h"
36 #include "text_decoder.h"
37 #include "video_content.h"
38 #include "video_decoder.h"
39
40
41 using std::dynamic_pointer_cast;
42 using std::list;
43 using std::make_shared;
44 using std::pair;
45 using std::shared_ptr;
46 using std::vector;
47 using std::weak_ptr;
48 using boost::optional;
49 using namespace dcpomatic;
50
51
52 Piece::Piece (weak_ptr<const Film> film, vector<Pair> content, FrameRateChange frc, bool fast)
53         : _film (film)
54         , _content (std::move(content))
55         , _frc (frc)
56         , _fast (fast)
57 {
58         auto const rep = representative();
59         if (rep.content->audio) {
60                 int const streams = rep.content->audio->streams().size();
61                 for (int i = 0; i < streams; ++i) {
62                         _audio_streams.push_back(Stream(rep.content->position(), rep.content->audio->mapping()));
63                 }
64         }
65
66         for (auto const& i: _content) {
67                 if (i.decoder->video) {
68                         i.decoder->video->Data.connect (boost::bind(&Piece::video, this, i.content, _1, _2, _3, _4));
69                 }
70
71                 if (i.decoder->audio) {
72                         i.decoder->audio->Data.connect (boost::bind(&Piece::audio, this, i.content, _1, _2, _3));
73                 }
74
75                 for (auto j: i.decoder->text) {
76                         j->BitmapStart.connect (boost::bind(&Piece::bitmap_start, this, i.content, j->content(), _1, _2, _3));
77                         j->StringStart.connect (boost::bind(&Piece::string_start, this, i.content, j->content(), _1, _2));
78                         j->Stop.connect (boost::bind(&Piece::stop, this, i.content, j->content(), _1));
79                 }
80
81                 if (i.decoder->atmos) {
82                         i.decoder->atmos->Data.connect (boost::bind(&Piece::atmos, this, _1, _2, _3));
83                 }
84
85                 i.decoder->Flush.connect (boost::bind(&Piece::flush, this));
86         }
87 }
88
89
90 void
91 Piece::video (shared_ptr<const Content> content, shared_ptr<const ImageProxy> image, Frame frame, Eyes eyes, Part part)
92 {
93         if (!use_video()) {
94                 return;
95         }
96
97         if (_frc.skip && (frame % 2) == 1) {
98                 return;
99         }
100
101         auto const time = content_video_to_dcp (content, frame);
102
103         if (_ignore_video && _ignore_video->contains(time)) {
104                 return;
105         }
106
107         Video (PieceVideo(image, frame, time, eyes, part));
108 }
109
110
111 /** @param frame Frame position in the content, as if there were no trim */
112 void
113 Piece::audio (shared_ptr<const Content> content, AudioStreamPtr stream_ptr, shared_ptr<const AudioBuffers> audio, Frame frame)
114 {
115         auto film = _film.lock ();
116         DCPOMATIC_ASSERT (film);
117
118         /* Here we have the frame index into the content, and shortly we're going to lose that information and
119          * start thinking about frame indices into the piece.  Here's the last chance for us to apply this content's
120          * trim, so we'll take it.
121          */
122         auto const sfr = stream_ptr->frame_rate();
123         auto const remove_from_start = std::max(Frame(0), content->trim_start().frames_round(sfr) - frame);
124         auto const remove_from_end = std::max(Frame(0), frame + audio->frames() - ContentTime(content->full_length_content() - content->trim_end()).frames_round(sfr));
125         if ((remove_from_start + remove_from_end) >= audio->frames()) {
126                 std::cout << "trim whole block.\n";
127                 return;
128         }
129         if (remove_from_start || remove_from_end) {
130                 std::cout << "trim " << remove_from_start << " " << remove_from_end << "\n";
131                 auto trimmed = make_shared<AudioBuffers>(audio);
132                 trimmed->trim_start (remove_from_start);
133                 trimmed->trim_end (remove_from_end);
134                 audio = trimmed;
135         }
136
137         auto content_streams = content->audio->streams();
138         auto content_stream_iter = std::find(content_streams.begin(), content_streams.end(), stream_ptr);
139         DCPOMATIC_ASSERT (content_stream_iter != content_streams.end());
140         int index = std::distance(content_streams.begin(), content_stream_iter);
141         DCPOMATIC_ASSERT (index >= 0 && index < static_cast<int>(_audio_streams.size()));
142         auto& stream = _audio_streams[index];
143
144         int const resampled_rate = representative().content->audio->resampled_frame_rate(film);
145
146         auto resampler = stream.resampler;
147         if (!resampler && stream_ptr->frame_rate() != resampled_rate) {
148                 LOG_GENERAL (
149                         "Creating new resampler from %1 to %2 with %3 channels",
150                         stream_ptr->frame_rate(),
151                         resampled_rate,
152                         stream_ptr->channels()
153                         );
154
155                 resampler = make_shared<Resampler>(stream_ptr->frame_rate(), resampled_rate, stream_ptr->channels());
156                 if (_fast) {
157                         resampler->set_fast ();
158                 }
159                 stream.resampler = resampler;
160         }
161
162         if (resampler) {
163                 auto ro = resampler->run (audio);
164                 if (ro->frames() == 0) {
165                         return;
166                 }
167                 audio = ro;
168         }
169
170         if (stream.position == 0) {
171                 stream.position = frame;
172         }
173
174         Audio (PieceAudio(index, audio, resampled_audio_to_dcp(stream.position), stream_ptr->mapping()));
175         stream.position += audio->frames();
176 }
177
178
179 void
180 Piece::bitmap_start (weak_ptr<const Content> content, weak_ptr<const TextContent> text, dcpomatic::ContentTime time, shared_ptr<Image> image, dcpomatic::Rect<double> area)
181 {
182         BitmapTextStart (PieceBitmapTextStart(content, text, time, image, area));
183 }
184
185
186 void
187 Piece::string_start (weak_ptr<const Content> content, weak_ptr<const TextContent> text, dcpomatic::ContentTime time, list<dcp::SubtitleString> subs)
188 {
189         StringTextStart (PieceStringTextStart(content, text, time, subs));
190 }
191
192
193 void
194 Piece::stop (weak_ptr<const Content> content, weak_ptr<const TextContent> text, dcpomatic::ContentTime time)
195 {
196         TextStop (PieceTextStop(content, text, time));
197 }
198
199
200 void
201 Piece::atmos (shared_ptr<const dcp::AtmosFrame> data, Frame frame, AtmosMetadata metadata)
202 {
203         Atmos (PieceAtmos(data, frame, metadata));
204 }
205
206
207 void
208 Piece::update_pull_to (DCPTime& pull_to) const
209 {
210         if (done() || _audio_streams.empty()) {
211                 return;
212         }
213
214         for (auto const& i: _audio_streams) {
215                 pull_to = std::min(pull_to, i.last_push_end);
216         }
217 }
218
219
220 void
221 Piece::set_last_push_end (int stream, DCPTime end)
222 {
223         DCPOMATIC_ASSERT (stream >= 0 && stream < static_cast<int>(_audio_streams.size()));
224         _audio_streams[stream].last_push_end = end;
225 }
226
227
228 DCPTime
229 Piece::content_video_to_dcp (shared_ptr<const Content> content, Frame f) const
230 {
231         /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
232            then convert that ContentTime to frames at the content's rate.  However this fails for
233            situations like content at 29.9978733fps, DCP at 30fps.  The accuracy of the Time type is not
234            enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
235
236            Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
237         */
238         auto const d = DCPTime::from_frames(f * _frc.factor(), _frc.dcp) - DCPTime(content->trim_start(), _frc);
239         return d + content->position();
240 }
241
242
243 /** @param f Frame offset from start of the piece */
244 DCPTime
245 Piece::resampled_audio_to_dcp (Frame f) const
246 {
247         auto film = _film.lock ();
248         DCPOMATIC_ASSERT (film);
249
250         return DCPTime::from_frames(f, film->audio_frame_rate()) + position();
251 }
252
253
254 ContentTime
255 Piece::dcp_to_content_time (shared_ptr<const Content> content, DCPTime t) const
256 {
257         auto film = _film.lock ();
258         DCPOMATIC_ASSERT (film);
259
260         auto s = t - content->position ();
261         s = min (content->length_after_trim(film), s);
262         return max (ContentTime(), ContentTime(s, _frc) + content->trim_start());
263 }
264
265
266 optional<DCPTime>
267 Piece::content_time_to_dcp (shared_ptr<const Content> content, ContentTime t) const
268 {
269         if (std::find_if(_content.begin(), _content.end(), [content](Pair const& p) { return p.content == content; }) == _content.end()) {
270                 return {};
271         }
272
273         return max (DCPTime(), DCPTime(t - content->trim_start(), _frc) + content->position());
274 }
275
276
277 bool
278 Piece::use_video () const
279 {
280         for (auto const& i: _content) {
281                 if (i.content->video && i.content->video->use()) {
282                         return true;
283                 }
284         }
285
286         return false;
287 }
288
289
290 VideoFrameType
291 Piece::video_frame_type () const
292 {
293         DCPOMATIC_ASSERT (representative().content->video);
294         return representative().content->video->frame_type();
295 }
296
297
298 dcpomatic::DCPTime
299 Piece::position () const
300 {
301         return _content.front().content->position();
302 }
303
304
305 dcpomatic::DCPTime
306 Piece::end () const
307 {
308         auto film = _film.lock ();
309         DCPOMATIC_ASSERT (film);
310         return _content.back().content->end(film);
311 }
312
313
314 shared_ptr<PlayerVideo>
315 Piece::player_video (PieceVideo video, dcp::Size container_size) const
316 {
317         auto film = _film.lock ();
318         DCPOMATIC_ASSERT (film);
319
320         auto const rep = representative().content;
321
322         return std::make_shared<PlayerVideo>(
323                 video.image,
324                 rep->video->crop(),
325                 rep->video->fade(film, video.frame),
326                 scale_for_display(rep->video->scaled_size(film->frame_size()), container_size, film->frame_size()),
327                 container_size,
328                 video.eyes,
329                 video.part,
330                 rep->video->colour_conversion(),
331                 rep->video->range(),
332                 /* XXX: this isn't really right, but it's just for doing the reset_metadata() tricks so it's
333                  * OK that it won't work for sound-only content.
334                  */
335                 rep,
336                 video.frame,
337                 false
338                 );
339 }
340
341
342 int
343 Piece::resampled_audio_frame_rate () const
344 {
345         auto film = _film.lock ();
346         DCPOMATIC_ASSERT (film);
347
348         DCPOMATIC_ASSERT (representative().content->audio);
349         return representative().content->audio->resampled_frame_rate(film);
350 }
351
352
353 double
354 Piece::audio_gain () const
355 {
356         DCPOMATIC_ASSERT (representative().content->audio);
357         return representative().content->audio->gain();
358 }
359
360
361 shared_ptr<Decoder>
362 Piece::decoder_for (shared_ptr<Content> content) const
363 {
364         for (auto const& i: _content) {
365                 if (i.content == content) {
366                         return i.decoder;
367                 }
368         }
369
370         return {};
371 }
372
373
374 void
375 Piece::pass ()
376 {
377         auto film = _film.lock ();
378         DCPOMATIC_ASSERT (film);
379
380         for (auto& i: _content) {
381                 if (i.decoder->done()) {
382                         continue;
383                 }
384                 LOG_DEBUG_PLAYER ("Calling pass() on %1", i.content->path(0));
385                 i.decoder->pass();
386                 return;
387         }
388 }
389
390
391 bool
392 Piece::reference_dcp_audio () const
393 {
394         auto dcp = dynamic_pointer_cast<DCPContent>(representative().content);
395         return dcp && dcp->reference_audio();
396 }
397
398
399 void
400 Piece::seek (DCPTime time, bool accurate)
401 {
402         for (auto& i: _audio_streams) {
403                 if (i.resampler) {
404                         i.resampler->flush ();
405                         i.resampler->reset ();
406                 }
407                 i.position = 0;
408         }
409
410         for (auto& i: _content) {
411                 if (time < i.content->position()) {
412                         /* Before; seek to the start of the content.  Even if this request is for an inaccurate seek
413                            we must seek this (following) content accurately, otherwise when we come to the end of the current
414                            content we may not start right at the beginning of the next, causing a gap (if the next content has
415                            been trimmed to a point between keyframes, or something).
416                            */
417                         i.decoder->seek (i.content->trim_start(), true);
418                 } else if (position() <= time && time < end()) {
419                         /* During; seek to position */
420                         i.decoder->seek (dcp_to_content_time(i.content, time), accurate);
421                 }
422         }
423 }
424
425
426 optional<dcpomatic::DCPTime>
427 Piece::decoder_before(optional<dcpomatic::DCPTime> time)
428 {
429         auto film = _film.lock ();
430         DCPOMATIC_ASSERT (film);
431
432         for (auto const& i: _content) {
433                 if (!i.decoder->done()) {
434                         auto t = content_time_to_dcp(i.content, std::max(i.decoder->position(), i.content->trim_start()));
435                         DCPOMATIC_ASSERT (t);
436                         /* This is the first unfinished decoder we have, so we'll make a decision with it.
437                            Given two choices at the same time, pick the one with texts so we see it before
438                            the video.
439                            */
440                         if (!time || t < *time || (t == *time && !i.decoder->text.empty())) {
441                                 return t;
442                         } else {
443                                 return {};
444                         }
445                 }
446         }
447
448         return {};
449 }
450
451
452 vector<dcpomatic::FontData>
453 Piece::fonts () const
454 {
455         vector<dcpomatic::FontData> data;
456         for (auto const& i: _content) {
457                 auto f = i.decoder->fonts();
458                 std::copy (f.begin(), f.end(), std::back_inserter(data));
459         }
460         return data;
461 }
462
463
464 DCPTimePeriod
465 Piece::period () const
466 {
467         return DCPTimePeriod(position(), end());
468 }
469
470
471 void
472 Piece::flush ()
473 {
474         int index = 0;
475         for (auto& i: _audio_streams) {
476                 if (i.resampler) {
477                         auto ro = i.resampler->flush ();
478                         if (ro->frames() > 0) {
479                                 Audio (PieceAudio(index, ro, resampled_audio_to_dcp(i.position), i.mapping));
480                                 i.position += ro->frames();
481                         }
482                 }
483                 ++index;
484         }
485 }
486
487
488 bool
489 Piece::done () const
490 {
491         for (auto const& i: _content) {
492                 if (!i.decoder->done()) {
493                         return false;
494                 }
495         }
496
497         return true;
498 }
499