OpenShot Library | libopenshot 0.6.0
Loading...
Searching...
No Matches
FrameMapper.cpp
Go to the documentation of this file.
1
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include <cmath>
14#include <algorithm>
15#include <iostream>
16#include <iomanip>
17
18#include "FrameMapper.h"
19#include "Exceptions.h"
20#include "Clip.h"
21#include "MemoryTrim.h"
22#include "ZmqLogger.h"
23
24using namespace std;
25using namespace openshot;
26
27FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) :
28 reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL), parent_position(0.0), parent_start(0.0), previous_frame(0)
29{
30 // Set the original frame rate from the reader
31 original = Fraction(reader->info.fps.num, reader->info.fps.den);
32
33 // Set all info struct members equal to the internal reader
34 info = reader->info;
35 info.fps.num = target.num;
36 info.fps.den = target.den;
37 info.video_timebase.num = target.den;
38 info.video_timebase.den = target.num;
40 info.sample_rate = target_sample_rate;
41 info.channels = target_channels;
42 info.channel_layout = target_channel_layout;
43 info.width = reader->info.width;
44 info.height = reader->info.height;
45
46 // Enable/Disable audio (based on settings)
48
49 // Used to toggle odd / even fields
50 field_toggle = true;
51
52 // Adjust cache size based on size of frame and audio
53 const int initial_cache_frames = std::max(Settings::Instance()->CACHE_MIN_FRAMES, OPEN_MP_NUM_PROCESSORS);
54 final_cache.SetMaxBytesFromInfo(initial_cache_frames, info.width, info.height, info.sample_rate, info.channels);
55}
56
57// Destructor
59
60 // Auto Close if not already
61 Close();
62
63 reader = NULL;
64}
65
68{
69 if (reader)
70 return reader;
71 else
72 // Throw error if reader not initialized
73 throw ReaderClosed("No Reader has been initialized for FrameMapper. Call Reader(*reader) before calling this method.");
74}
75
76void FrameMapper::AddField(int64_t frame)
77{
78 // Add a field, and toggle the odd / even field
79 Field f = { frame, bool(field_toggle) };
80 AddField(f);
81}
82
83void FrameMapper::AddField(int64_t frame, bool isOdd)
84{
85 // Add a field, and toggle the odd / even field
86 Field f = { frame, isOdd };
87 AddField(f);
88}
89
90void FrameMapper::AddField(Field field)
91{
92 // Add a field to the end of the field list
93 fields.push_back(field);
94
95 // toggle the odd / even flag
96 field_toggle = (field_toggle ? false : true);
97}
98
99// Clear both the fields & frames lists
100void FrameMapper::Clear() {
101 // Prevent async calls to the following code
102 const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
103
104 // Clear the fields & frames lists
105 fields.clear();
106 fields.shrink_to_fit();
107 frames.clear();
108 frames.shrink_to_fit();
109}
110
111// Use the original and target frame rates and a pull-down technique to create
112// a mapping between the original fields and frames or a video to a new frame rate.
113// This might repeat or skip fields and frames of the original video, depending on
114// whether the frame rate is increasing or decreasing.
115void FrameMapper::Init()
116{
117 ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Init (Calculate frame mappings)");
118
119 // Do not initialize anything if just a picture with no audio
121 // Skip initialization
122 return;
123
124 // Prevent async calls to the following code
125 const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
126
127 // Clear the fields & frames lists
128 Clear();
129
130 // Find parent position (if any)
131 Clip *parent = static_cast<Clip *>(ParentClip());
132 if (parent) {
133 parent_position = parent->Position();
134 parent_start = parent->Start();
135 } else {
136 parent_position = 0.0;
137 parent_start = 0.0;
138 }
139
140 // Mark as not dirty
141 is_dirty = false;
142
143 // Clear cache
144 final_cache.Clear();
145
146 // Some framerates are handled special, and some use a generic Keyframe curve to
147 // map the framerates. These are the special framerates:
148 if ((fabs(original.ToFloat() - 24.0) < 1e-7 || fabs(original.ToFloat() - 25.0) < 1e-7 || fabs(original.ToFloat() - 30.0) < 1e-7) &&
149 (fabs(target.ToFloat() - 24.0) < 1e-7 || fabs(target.ToFloat() - 25.0) < 1e-7 || fabs(target.ToFloat() - 30.0) < 1e-7)) {
150
151 // Get the difference (in frames) between the original and target frame rates
152 float difference = target.ToInt() - original.ToInt();
153
154 // Find the number (i.e. interval) of fields that need to be skipped or repeated
155 int field_interval = 0;
156 int frame_interval = 0;
157
158 if (difference != 0)
159 {
160 field_interval = round(fabs(original.ToInt() / difference));
161
162 // Get frame interval (2 fields per frame)
163 frame_interval = field_interval * 2.0f;
164 }
165
166
167 // Calculate # of fields to map
168 int64_t frame = 1;
169 int64_t number_of_fields = reader->info.video_length * 2;
170
171 // Loop through all fields in the original video file
172 for (int64_t field = 1; field <= number_of_fields; field++)
173 {
174
175 if (difference == 0) // Same frame rate, NO pull-down or special techniques required
176 {
177 // Add fields
178 AddField(frame);
179 }
180 else if (difference > 0) // Need to ADD fake fields & frames, because original video has too few frames
181 {
182 // Add current field
183 AddField(frame);
184
185 if (pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
186 {
187 // Add extra field for each 'field interval
188 AddField(frame);
189 }
190 else if (pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
191 {
192 // Add both extra fields in the middle 'together' (i.e. 2:3:3:2 technique)
193 AddField(frame); // add field for current frame
194
195 if (frame + 1 <= info.video_length)
196 // add field for next frame (if the next frame exists)
197 AddField(frame + 1, field_toggle);
198 }
199 else if (pulldown == PULLDOWN_NONE && field % frame_interval == 0)
200 {
201 // No pull-down technique needed, just repeat this frame
202 AddField(frame);
203 AddField(frame);
204 }
205 }
206 else if (difference < 0) // Need to SKIP fake fields & frames, because we want to return to the original film frame rate
207 {
208
209 if (pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
210 {
211 // skip current field and toggle the odd/even flag
212 field_toggle = (field_toggle ? false : true);
213 }
214 else if (pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
215 {
216 // skip this field, plus the next field
217 field++;
218 }
219 else if (pulldown == PULLDOWN_NONE && frame % field_interval == 0)
220 {
221 // skip this field, plus the next one
222 field++;
223 }
224 else
225 {
226 // No skipping needed, so add the field
227 AddField(frame);
228 }
229 }
230
231 // increment frame number (if field is divisible by 2)
232 if (field % 2 == 0 && field > 0)
233 frame++;
234 }
235
236 } else {
237 // Map the remaining framerates using a linear algorithm
238 double rate_diff = target.ToDouble() / original.ToDouble();
239 int64_t new_length = reader->info.video_length * rate_diff;
240
241 // Calculate the value difference
242 double value_increment = reader->info.video_length / (double) (new_length);
243
244 // Loop through curve, and build list of frames
245 double original_frame_num = 1.0f;
246 for (int64_t frame_num = 1; frame_num <= new_length; frame_num++)
247 {
248 // Add 2 fields per frame
249 AddField(round(original_frame_num));
250 AddField(round(original_frame_num));
251
252 // Increment original frame number
253 original_frame_num += value_increment;
254 }
255 }
256
257 // Loop through the target frames again (combining fields into frames)
258 Field Odd = {0, true}; // temp field used to track the ODD field
259 Field Even = {0, true}; // temp field used to track the EVEN field
260
261 // Variables used to remap audio samples
262 int64_t start_samples_frame = 1;
263 int start_samples_position = 0;
264
265 for (std::vector<Field>::size_type field = 1; field <= fields.size(); field++)
266 {
267 // Get the current field
268 Field f = fields[field - 1];
269
270 // Is field divisible by 2?
271 if (field % 2 == 0 && field > 0)
272 {
273 // New frame number
274 int64_t frame_number = field / 2;
275
276 // Set the bottom frame
277 if (f.isOdd)
278 Odd = f;
279 else
280 Even = f;
281
282 // Determine the range of samples (from the original rate). Resampling happens in real-time when
283 // calling the GetFrame() method. So this method only needs to redistribute the original samples with
284 // the original sample rate.
285 int64_t end_samples_frame = start_samples_frame;
286 int end_samples_position = start_samples_position;
287 int remaining_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, reader->info.sample_rate, reader->info.channels);
288
289 while (remaining_samples > 0)
290 {
291 // Get original samples (with NO framerate adjustments)
292 // This is the original reader's frame numbers
293 int original_samples = Frame::GetSamplesPerFrame(end_samples_frame, original, reader->info.sample_rate, reader->info.channels) - end_samples_position;
294
295 // Enough samples
296 if (original_samples >= remaining_samples)
297 {
298 // Take all that we need, and break loop
299 end_samples_position += remaining_samples - 1;
300 remaining_samples = 0;
301 } else
302 {
303 // Not enough samples (take them all, and keep looping)
304 end_samples_frame += 1; // next frame
305 end_samples_position = 0; // next frame, starting on 1st sample
306 remaining_samples -= original_samples; // reduce the remaining amount
307 }
308 }
309
310
311
312 // Create the sample mapping struct
313 SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, reader->info.sample_rate, reader->info.channels)};
314
315 // Reset the audio variables
316 start_samples_frame = end_samples_frame;
317 start_samples_position = end_samples_position + 1;
318 if (start_samples_position >= Frame::GetSamplesPerFrame(AdjustFrameNumber(start_samples_frame), original, reader->info.sample_rate, reader->info.channels))
319 {
320 start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one)
321 start_samples_position = 0; // reset to 0, since we wrapped
322 }
323
324 // Create a frame and ADD it to the frames collection
325 MappedFrame frame = {Odd, Even, Samples};
326 frames.push_back(frame);
327 }
328 else
329 {
330 // Set the top field
331 if (f.isOdd)
332 Odd = f;
333 else
334 Even = f;
335 }
336 }
337
338 // Clear the internal fields list (no longer needed)
339 fields.clear();
340 fields.shrink_to_fit();
341
342 if (avr) {
343 // Delete resampler (if exists)
344 SWR_CLOSE(avr);
345 SWR_FREE(&avr);
346 avr = NULL;
347 }
348}
349
350MappedFrame FrameMapper::GetMappedFrame(int64_t TargetFrameNumber)
351{
352 // Check if mappings are dirty (and need to be recalculated)
353 if (is_dirty)
354 // Recalculate mappings
355 Init();
356
357 // Ignore mapping on single image readers
359 // Return the same number
360 MappedFrame frame;
361 frame.Even.Frame = TargetFrameNumber;
362 frame.Odd.Frame = TargetFrameNumber;
363 frame.Samples.frame_start = 0;
364 frame.Samples.frame_end = 0;
365 frame.Samples.sample_start = 0;
366 frame.Samples.sample_end = 0;
367 frame.Samples.total = 0;
368 return frame;
369 }
370
371 // Check if frame number is valid
372 if(TargetFrameNumber < 1 || frames.size() == 0)
373 // frame too small, return error
374 throw OutOfBoundsFrame("An invalid frame was requested.", TargetFrameNumber, frames.size());
375
376 else if (TargetFrameNumber > (int64_t)frames.size())
377 // frame too large, set to end frame
378 TargetFrameNumber = frames.size();
379
380 // Debug output
382 "FrameMapper::GetMappedFrame",
383 "TargetFrameNumber", TargetFrameNumber,
384 "frames.size()", frames.size(),
385 "frames[...].Odd", frames[TargetFrameNumber - 1].Odd.Frame,
386 "frames[...].Even", frames[TargetFrameNumber - 1].Even.Frame);
387
388 // Return frame
389 return frames[TargetFrameNumber - 1];
390}
391
392// Get or generate a blank frame
393std::shared_ptr<Frame> FrameMapper::GetOrCreateFrame(int64_t number)
394{
395 std::shared_ptr<Frame> new_frame;
396
397 // Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now)
398 int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(number), target, reader->info.sample_rate, reader->info.channels);
399
400 try {
401 // Debug output
403 "FrameMapper::GetOrCreateFrame (from reader)",
404 "number", number,
405 "samples_in_frame", samples_in_frame);
406
407 // Attempt to get a frame (but this could fail if a reader has just been closed)
408 new_frame = reader->GetFrame(number);
409
410 // Return real frame
411 return new_frame;
412
413 } catch (const ReaderClosed & e) {
414 // ...
415 } catch (const OutOfBoundsFrame & e) {
416 // ...
417 }
418
419 // Debug output
421 "FrameMapper::GetOrCreateFrame (create blank)",
422 "number", number,
423 "samples_in_frame", samples_in_frame);
424
425 // Create blank frame
426 new_frame = std::make_shared<Frame>(number, info.width, info.height, "#000000", samples_in_frame, reader->info.channels);
427 new_frame->SampleRate(reader->info.sample_rate);
428 new_frame->ChannelsLayout(info.channel_layout);
429 new_frame->AddAudioSilence(samples_in_frame);
430 return new_frame;
431}
432
433// Get an openshot::Frame object for a specific frame number of this reader.
434std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)
435{
436 // Check final cache, and just return the frame (if it's available)
437 std::shared_ptr<Frame> final_frame = final_cache.GetFrame(requested_frame);
438 if (final_frame) return final_frame;
439
440 // Create a scoped lock, allowing only a single thread to run the following code at one time
441 const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
442
443 // Find parent properties (if any)
444 Clip *parent = static_cast<Clip *>(ParentClip());
445 bool is_increasing = true;
446 bool direction_flipped = false;
447
448 {
449 const std::lock_guard<std::recursive_mutex> lock(directionMutex);
450
451 // One-shot: if a hint exists, consume it for THIS call, regardless of frame number.
452 if (have_hint) {
453 is_increasing = hint_increasing;
454 have_hint = false;
455 } else if (previous_frame > 0 && std::llabs(requested_frame - previous_frame) == 1) {
456 // Infer from request order when adjacent
457 is_increasing = (requested_frame > previous_frame);
458 } else if (last_dir_initialized) {
459 // Reuse last known direction if non-adjacent and no hint
460 is_increasing = last_is_increasing;
461 } else {
462 is_increasing = true; // default on first call
463 }
464
465 // Detect flips so we can reset SR context
466 if (!last_dir_initialized) {
467 last_is_increasing = is_increasing;
468 last_dir_initialized = true;
469 } else if (last_is_increasing != is_increasing) {
470 direction_flipped = true;
471 last_is_increasing = is_increasing;
472 }
473 }
474 if (parent) {
475 float position = parent->Position();
476 float start = parent->Start();
477 if (parent_position != position || parent_start != start) {
478 // Force dirty if parent clip has moved or been trimmed
479 // since this heavily affects frame #s and audio mappings
480 is_dirty = true;
481 }
482 }
483
484 // Check if mappings are dirty (and need to be recalculated)
485 if (is_dirty)
486 Init();
487
488 // Check final cache a 2nd time (due to potential lock already generating this frame)
489 final_frame = final_cache.GetFrame(requested_frame);
490 if (final_frame) return final_frame;
491
492 // Minimum number of frames to process (for performance reasons)
493 // Dialing this down to 1 for now, as it seems to improve performance, and reduce export crashes
494 int minimum_frames = 1;
495
496 // Debug output
498 "FrameMapper::GetFrame (Loop through frames)",
499 "requested_frame", requested_frame,
500 "minimum_frames", minimum_frames);
501
502 // Loop through all requested frames
503 for (int64_t frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
504 {
505 // Debug output
507 "FrameMapper::GetFrame (inside omp for loop)",
508 "frame_number", frame_number,
509 "minimum_frames", minimum_frames,
510 "requested_frame", requested_frame);
511
512 // Get the mapped frame
513 MappedFrame mapped = GetMappedFrame(frame_number);
514 std::shared_ptr<Frame> mapped_frame = GetOrCreateFrame(mapped.Odd.Frame);
515
516 // Get # of channels in the actual frame
517 int channels_in_frame = mapped_frame->GetAudioChannelsCount();
518 int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, mapped_frame->SampleRate(), channels_in_frame);
519
520 // Determine if mapped frame is identical to source frame
521 // including audio sample distribution according to mapped.Samples,
522 // and frame_number. In some cases such as end of stream, the reader
523 // will return a frame with a different frame number. In these cases,
524 // we cannot use the frame as is, nor can we modify the frame number,
525 // otherwise the reader's cache object internals become invalid.
526 if (info.sample_rate == mapped_frame->SampleRate() &&
527 info.channels == mapped_frame->GetAudioChannelsCount() &&
528 info.channel_layout == mapped_frame->ChannelsLayout() &&
529 mapped.Samples.total == mapped_frame->GetAudioSamplesCount() &&
530 mapped.Samples.total == samples_in_frame && is_increasing &&
531 mapped.Samples.frame_start == mapped.Odd.Frame &&
532 mapped.Samples.sample_start == 0 &&
533 mapped_frame->number == frame_number &&// in some conditions (e.g. end of stream)
534 info.fps.num == reader->info.fps.num &&
535 info.fps.den == reader->info.fps.den) {
536 // Add original frame to cache, and skip the rest (for performance reasons)
537 final_cache.Add(mapped_frame);
538 continue;
539 }
540
541 // Create a new frame
542 auto frame = std::make_shared<Frame>(
543 frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame);
544 frame->SampleRate(mapped_frame->SampleRate());
545 frame->ChannelsLayout(mapped_frame->ChannelsLayout());
546
547
548 // Copy the image from the odd field
549 std::shared_ptr<Frame> odd_frame = mapped_frame;
550
551 if (odd_frame && odd_frame->has_image_data)
552 frame->AddImage(std::make_shared<QImage>(*odd_frame->GetImage()), true);
553 if (mapped.Odd.Frame != mapped.Even.Frame) {
554 // Add even lines (if different than the previous image)
555 std::shared_ptr<Frame> even_frame;
556 even_frame = GetOrCreateFrame(mapped.Even.Frame);
557 if (even_frame && even_frame->has_image_data)
558 frame->AddImage(std::make_shared<QImage>(*even_frame->GetImage()), false);
559 }
560
561 // Only treat the reader as audio-capable when the wrapped reader reports
562 // audio. Individual source frames can still be empty near boundaries, but
563 // those cases should pad with silence instead of dropping this mapped frame.
564 const bool reader_has_audio = info.has_audio &&
565 mapped_frame->SampleRate() > 0 &&
566 mapped_frame->GetAudioChannelsCount() > 0;
567
568 // Resample audio on frame (if needed)
569 bool need_resampling = false;
570 if ((info.has_audio && reader_has_audio) &&
571 (info.sample_rate != frame->SampleRate() ||
572 info.channels != frame->GetAudioChannelsCount() ||
573 info.channel_layout != frame->ChannelsLayout()))
574 // Resample audio and correct # of channels if needed
575 need_resampling = true;
576
577 // create a copy of mapped.Samples that will be used by copy loop
578 SampleRange copy_samples = mapped.Samples;
579
580 if (need_resampling)
581 {
582 // Reset resampler when non-adjacent request OR playback direction flips
583 if (direction_flipped || (previous_frame > 0 && std::llabs(requested_frame - previous_frame) > 1)) {
584 if (avr) {
585 // Delete resampler (if exists)
586 SWR_CLOSE(avr);
587 SWR_FREE(&avr);
588 avr = nullptr;
589 }
590 }
591
592 // Resampling needed, modify copy of SampleRange object that includes some additional input samples on
593 // first iteration, and continues the offset to ensure that the resampler is not input limited.
594 const int EXTRA_INPUT_SAMPLES = 64;
595
596 if (!avr) {
597 // This is the first iteration, and we need to extend # of samples for this frame
598 // Extend sample count range by an additional EXTRA_INPUT_SAMPLES
599 copy_samples.Extend(EXTRA_INPUT_SAMPLES, original, reader->info.sample_rate, reader->info.channels, is_increasing);
600 } else {
601 // Sample rate conversion has already been allocated on this clip, so
602 // this is not the first iteration. Shift position by EXTRA_INPUT_SAMPLES to correctly align samples
603 copy_samples.Shift(EXTRA_INPUT_SAMPLES, original, reader->info.sample_rate, reader->info.channels, is_increasing);
604 }
605 }
606
607 // Preserve the target frame duration even when a source frame has no samples.
608 if (reader_has_audio && samples_in_frame > 0)
609 frame->AddAudioSilence(samples_in_frame);
610
611 // Copy the samples
612 int samples_copied = 0;
613 int64_t starting_frame = copy_samples.frame_start;
614 while (reader_has_audio && samples_copied < copy_samples.total)
615 {
616 // Init number of samples to copy this iteration
617 int remaining_samples = copy_samples.total - samples_copied;
618 int number_to_copy = 0;
619
620 // number of original samples on this frame
621 std::shared_ptr<Frame> original_frame = mapped_frame;
622 if (starting_frame != original_frame->number) {
623 original_frame = GetOrCreateFrame(starting_frame);
624 }
625
626 int original_samples = original_frame->GetAudioSamplesCount();
627 if (original_samples <= 0) {
628 if (starting_frame >= copy_samples.frame_end)
629 break;
630 starting_frame++;
631 continue;
632 }
633
634 if (starting_frame == copy_samples.frame_start)
635 number_to_copy = original_samples - copy_samples.sample_start;
636 else if (starting_frame > copy_samples.frame_start && starting_frame < copy_samples.frame_end)
637 number_to_copy = original_samples;
638 else
639 number_to_copy = copy_samples.sample_end + 1;
640
641 if (number_to_copy <= 0) {
642 if (starting_frame >= copy_samples.frame_end)
643 break;
644 starting_frame++;
645 continue;
646 }
647 if (number_to_copy > remaining_samples)
648 number_to_copy = remaining_samples;
649
650 // Loop through each channel
651 for (int channel = 0; channel < channels_in_frame; channel++)
652 {
653 if (starting_frame == copy_samples.frame_start)
654 {
655 // Starting frame (take the ending samples)
656 frame->AddAudio(true, channel, samples_copied, original_frame->GetAudioSamples(channel) + copy_samples.sample_start, number_to_copy, 1.0);
657 }
658 else if (starting_frame > copy_samples.frame_start && starting_frame < copy_samples.frame_end)
659 {
660 // Middle frame (take all samples)
661 frame->AddAudio(true, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
662 }
663 else
664 {
665 // Ending frame (take the beginning samples)
666 frame->AddAudio(false, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
667 }
668 }
669
670 // increment frame
671 samples_copied += number_to_copy;
672 starting_frame++;
673 }
674
675 // Set audio direction
676 frame->SetAudioDirection(is_increasing);
677
678 // Resample audio on frame (if needed)
679 if (need_resampling)
680 // Resample audio and correct # of channels if needed
681 ResampleMappedAudio(frame, mapped.Odd.Frame);
682
683 // Add frame to final cache
684 final_cache.Add(frame);
685
686 } // for loop
687
688 // Return processed openshot::Frame
689 return final_cache.GetFrame(requested_frame);
690}
691
692void FrameMapper::PrintMapping(std::ostream* out)
693{
694 // Check if mappings are dirty (and need to be recalculated)
695 if (is_dirty)
696 // Recalculate mappings
697 Init();
698
699 // Loop through frame mappings
700 for (float map = 1; map <= frames.size(); map++)
701 {
702 MappedFrame frame = frames[map - 1];
703 *out << "Target frame #: " << map
704 << " mapped to original frame #:\t("
705 << frame.Odd.Frame << " odd, "
706 << frame.Even.Frame << " even)" << std::endl;
707
708 *out << " - Audio samples mapped to frame "
709 << frame.Samples.frame_start << ":"
710 << frame.Samples.sample_start << " to frame "
711 << frame.Samples.frame_end << ":"
712 << frame.Samples.sample_end << endl;
713 }
714
715}
716
717// Determine if reader is open or closed
719 if (reader)
720 return reader->IsOpen();
721 else
722 return false;
723}
724
725// Open the internal reader
727{
728 if (reader)
729 {
730 ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Open");
731
732 // Open the reader
733 reader->Open();
734 }
735}
736
737// Close the internal reader
739{
740 if (reader)
741 {
742 // Create a scoped lock, allowing only a single thread to run the following code at one time
743 const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
744
745 ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Close");
746
747 // Close internal reader
748 reader->Close();
749 }
750
751 // Clear the fields & frames lists
752 Clear();
753
754 // Mark as dirty
755 is_dirty = true;
756
757 // Clear cache
758 final_cache.Clear();
759
760 // Deallocate resample buffer
761 if (avr) {
762 SWR_CLOSE(avr);
763 SWR_FREE(&avr);
764 avr = NULL;
765 }
766}
767
768
769// Generate JSON string of this object
770std::string FrameMapper::Json() const {
771
772 // Return formatted string
773 return JsonValue().toStyledString();
774}
775
776// Generate Json::Value for this object
777Json::Value FrameMapper::JsonValue() const {
778
779 // Create root json object
780 Json::Value root = ReaderBase::JsonValue(); // get parent properties
781 root["type"] = "FrameMapper";
782 if (reader) {
783 root["reader"] = reader->JsonValue();
784 }
785
786 // return JsonValue
787 return root;
788}
789
790// Load JSON string into this object
791void FrameMapper::SetJson(const std::string value) {
792
793 // Parse JSON string into JSON objects
794 try
795 {
796 const Json::Value root = openshot::stringToJson(value);
797 // Set all values that match
798 SetJsonValue(root);
799
800 if (!root["reader"].isNull()) // does Json contain a reader?
801 {
802 // Placeholder to load reader
803 // TODO: need a createReader method for this and Clip JSON methods
804 }
805 }
806 catch (const std::exception& e)
807 {
808 // Error parsing JSON (or missing keys)
809 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
810 }
811}
812
813// Load Json::Value into this object
814void FrameMapper::SetJsonValue(const Json::Value root) {
815
816 // Set parent data
818}
819
820// Change frame rate or audio mapping details
821void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
822{
824 "FrameMapper::ChangeMapping",
825 "target_fps.num", target_fps.num,
826 "target_fps.den", target_fps.den,
827 "target_pulldown", target_pulldown,
828 "target_sample_rate", target_sample_rate,
829 "target_channels", target_channels,
830 "target_channel_layout", target_channel_layout);
831
832 // Mark as dirty
833 is_dirty = true;
834
835 // Update mapping details
836 target.num = target_fps.num;
837 target.den = target_fps.den;
838 info.fps.num = target_fps.num;
839 info.fps.den = target_fps.den;
840 info.video_timebase.num = target_fps.den;
841 info.video_timebase.den = target_fps.num;
843 pulldown = target_pulldown;
844 info.sample_rate = target_sample_rate;
845 info.channels = target_channels;
846 info.channel_layout = target_channel_layout;
847
848 // Enable/Disable audio (based on settings)
850
851 // Clear cache
852 final_cache.Clear();
853
854 // Adjust cache size based on size of frame and audio
855 const int reset_cache_frames = std::max(Settings::Instance()->CACHE_MIN_FRAMES, OPEN_MP_NUM_PROCESSORS * 4);
856 final_cache.SetMaxBytesFromInfo(reset_cache_frames, info.width, info.height, info.sample_rate, info.channels);
857
858 // Deallocate resample buffer
859 if (avr) {
860 SWR_CLOSE(avr);
861 SWR_FREE(&avr);
862 avr = NULL;
863 }
864}
865
866// Resample audio and map channels (if needed)
867void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t original_frame_number)
868{
869 // Check if mappings are dirty (and need to be recalculated)
870 if (is_dirty)
871 // Recalculate mappings
872 Init();
873
874 // Init audio buffers / variables
875 int total_frame_samples = 0;
876 int channels_in_frame = frame->GetAudioChannelsCount();
877 int sample_rate_in_frame = frame->SampleRate();
878 int samples_in_frame = frame->GetAudioSamplesCount();
879 ChannelLayout channel_layout_in_frame = frame->ChannelsLayout();
880
882 "FrameMapper::ResampleMappedAudio",
883 "frame->number", frame->number,
884 "original_frame_number", original_frame_number,
885 "channels_in_frame", channels_in_frame,
886 "samples_in_frame", samples_in_frame,
887 "sample_rate_in_frame", sample_rate_in_frame);
888
889 // Get audio sample array
890 float* frame_samples_float = NULL;
891 // Get samples interleaved together (c1 c2 c1 c2 c1 c2)
892 frame_samples_float = frame->GetInterleavedAudioSamples(&samples_in_frame);
893
894 // Calculate total samples
895 total_frame_samples = samples_in_frame * channels_in_frame;
896
897 // Create a new array (to hold all S16 audio samples for the current queued frames)
898 int16_t* frame_samples = (int16_t*) av_malloc(sizeof(int16_t)*total_frame_samples);
899
900 // Translate audio sample values back to 16 bit integers with saturation
901 float valF;
902 int16_t conv;
903 const int16_t max16 = 32767;
904 const int16_t min16 = -32768;
905 for (int s = 0; s < total_frame_samples; s++) {
906 valF = frame_samples_float[s] * (1 << 15);
907 if (valF > max16)
908 conv = max16;
909 else if (valF < min16)
910 conv = min16;
911 else
912 conv = int(valF + 32768.5) - 32768; // +0.5 is for rounding
913
914 // Copy into buffer
915 frame_samples[s] = conv;
916 }
917
918 // Deallocate float array
919 delete[] frame_samples_float;
920 frame_samples_float = NULL;
921
922 // Create input frame (and allocate arrays)
923 AVFrame *audio_frame = AV_ALLOCATE_FRAME();
924 AV_RESET_FRAME(audio_frame);
925 audio_frame->nb_samples = total_frame_samples / channels_in_frame;
926
927 int buf_size = audio_frame->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * channels_in_frame;
928 int error_code = avcodec_fill_audio_frame(
929 audio_frame, channels_in_frame, AV_SAMPLE_FMT_S16,
930 (uint8_t *) frame_samples, buf_size, 1);
931
932 if (error_code < 0)
933 {
935 "FrameMapper::ResampleMappedAudio ERROR [" + av_err2string(error_code) + "]",
936 "error_code", error_code);
937 throw ErrorEncodingVideo("Error while resampling audio in frame mapper", frame->number);
938 }
939
940 // Update total samples & input frame size (due to bigger or smaller data types)
941 total_frame_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame->number), target, info.sample_rate, info.channels);
942
943 // Create output frame (and allocate arrays)
944 AVFrame *audio_converted = AV_ALLOCATE_FRAME();
945 AV_RESET_FRAME(audio_converted);
946 audio_converted->nb_samples = total_frame_samples;
947 av_samples_alloc(audio_converted->data, audio_converted->linesize, info.channels, total_frame_samples, AV_SAMPLE_FMT_S16, 0);
948
949 int nb_samples = 0;
950
951 // setup resample context
952 if (!avr) {
953 avr = SWR_ALLOC();
954 av_opt_set_int(avr, "in_channel_layout", channel_layout_in_frame, 0);
955 av_opt_set_int(avr, "out_channel_layout", info.channel_layout, 0);
956 av_opt_set_int(avr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
957 av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
958 av_opt_set_int(avr, "in_sample_rate", sample_rate_in_frame, 0);
959 av_opt_set_int(avr, "out_sample_rate", info.sample_rate, 0);
960 av_opt_set_int(avr, "in_channels", channels_in_frame, 0);
961 av_opt_set_int(avr, "out_channels", info.channels, 0);
962 SWR_INIT(avr);
963 }
964
965 // Convert audio samples
966 nb_samples = SWR_CONVERT(avr, // audio resample context
967 audio_converted->data, // output data pointers
968 audio_converted->linesize[0], // output plane size, in bytes. (0 if unknown)
969 audio_converted->nb_samples, // maximum number of samples that the output buffer can hold
970 audio_frame->data, // input data pointers
971 audio_frame->linesize[0], // input plane size, in bytes (0 if unknown)
972 audio_frame->nb_samples); // number of input samples to convert
973
974 // Create a new array (to hold all resampled S16 audio samples)
975 int16_t* resampled_samples = new int16_t[(nb_samples * info.channels)];
976
977 // Copy audio samples over original samples
978 memcpy(resampled_samples, audio_converted->data[0], (nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * info.channels));
979
980 // Free frames
981 av_freep(&audio_frame->data[0]);
982 AV_FREE_FRAME(&audio_frame);
983 av_freep(&audio_converted->data[0]);
984 AV_FREE_FRAME(&audio_converted);
985 frame_samples = NULL;
986
987 // Resize the frame to hold the right # of channels and samples
988 int channel_buffer_size = nb_samples;
989 frame->ResizeAudio(info.channels, channel_buffer_size, info.sample_rate, info.channel_layout);
990
992 "FrameMapper::ResampleMappedAudio (Audio successfully resampled)",
993 "nb_samples", nb_samples,
994 "total_frame_samples", total_frame_samples,
995 "info.sample_rate", info.sample_rate,
996 "channels_in_frame", channels_in_frame,
997 "info.channels", info.channels,
998 "info.channel_layout", info.channel_layout);
999
1000 // Array of floats (to hold samples for each channel)
1001 float *channel_buffer = new float[channel_buffer_size];
1002
1003 // Divide audio into channels. Loop through each channel
1004 for (int channel_filter = 0; channel_filter < info.channels; channel_filter++)
1005 {
1006 // Init array
1007 for (int z = 0; z < channel_buffer_size; z++)
1008 channel_buffer[z] = 0.0f;
1009
1010 // Loop through all samples and add them to our Frame based on channel.
1011 // Toggle through each channel number, since channel data is stored like (left right left right)
1012 int channel = 0;
1013 int position = 0;
1014 for (int sample = 0; sample < (nb_samples * info.channels); sample++)
1015 {
1016 // Only add samples for current channel
1017 if (channel_filter == channel)
1018 {
1019 // Add sample (convert from (-32768 to 32768) to (-1.0 to 1.0))
1020 channel_buffer[position] = resampled_samples[sample] * (1.0f / (1 << 15));
1021
1022 // Increment audio position
1023 position++;
1024 }
1025
1026 // increment channel (if needed)
1027 if ((channel + 1) < info.channels)
1028 // move to next channel
1029 channel ++;
1030 else
1031 // reset channel
1032 channel = 0;
1033 }
1034
1035 // Add samples to frame for this channel
1036 frame->AddAudio(true, channel_filter, 0, channel_buffer, position, 1.0f);
1037 }
1038
1039 // Update frame's audio meta data
1040 frame->SampleRate(info.sample_rate);
1041 frame->ChannelsLayout(info.channel_layout);
1042
1043 // clear channel buffer
1044 delete[] channel_buffer;
1045 channel_buffer = NULL;
1046
1047 // Delete arrays
1048 delete[] resampled_samples;
1049 resampled_samples = NULL;
1050
1051 // Keep track of last resampled frame
1052 previous_frame = frame->number;
1053}
1054
1055// Adjust frame number for Clip position and start (which can result in a different number)
1056int64_t FrameMapper::AdjustFrameNumber(int64_t clip_frame_number) {
1057
1058 // Get clip position from parent clip (if any)
1059 float position = 0.0;
1060 float start = 0.0;
1061 Clip *parent = static_cast<Clip *>(ParentClip());
1062 if (parent) {
1063 position = parent->Position();
1064 start = parent->Start();
1065 }
1066
1067 // Adjust start frame and position based on parent clip.
1068 // This ensures the same frame # is used by mapped readers and clips,
1069 // when calculating samples per frame.
1070 // Thus, this prevents gaps and mismatches in # of samples.
1071 int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1;
1072 int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1;
1073 int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
1074
1075 return frame_number;
1076}
1077
1078// Set direction hint for the next call to GetFrame
1079void FrameMapper::SetDirectionHint(const bool increasing)
1080{
1081 const std::lock_guard<std::recursive_mutex> lock(directionMutex);
1082 hint_increasing = increasing;
1083 have_hint = true;
1084}
Header file for Clip class.
Header file for all Exception classes.
#define SWR_INIT(ctx)
#define AV_FREE_FRAME(av_frame)
#define SWR_CONVERT(ctx, out, linesize, out_count, in, linesize2, in_count)
#define SWR_ALLOC()
#define SWR_CLOSE(ctx)
#define AV_ALLOCATE_FRAME()
#define SWR_FREE(ctx)
#define AV_RESET_FRAME(av_frame)
Header file for the FrameMapper class.
Cross-platform helper to encourage returning freed memory to the OS.
#define OPEN_MP_NUM_PROCESSORS
Header file for ZeroMQ-based Logger class.
void SetMaxBytesFromInfo(int64_t number_of_frames, int width, int height, int sample_rate, int channels)
Set maximum bytes to a different amount based on a ReaderInfo struct.
Definition CacheBase.cpp:28
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
void Clear()
Clear the cache of all frames.
float Start() const
Get start position (in seconds) of clip (trim start of video)
Definition ClipBase.h:88
float Position() const
Get position on timeline (in seconds)
Definition ClipBase.h:86
This class represents a clip (used to arrange readers on the timeline)
Definition Clip.h:89
Exception when encoding audio packet.
Definition Exceptions.h:149
This class represents a fraction.
Definition Fraction.h:30
int num
Numerator for the fraction.
Definition Fraction.h:32
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
Definition Fraction.cpp:35
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition Fraction.cpp:40
int ToInt()
Return a rounded integer of the fraction (for example 30000/1001 returns 30)
Definition Fraction.cpp:45
int den
Denominator for the fraction.
Definition Fraction.h:33
std::shared_ptr< Frame > GetFrame(int64_t requested_frame) override
This method is required for all derived classes of ReaderBase, and return the openshot::Frame object,...
void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
Change frame rate or audio mapping details.
MappedFrame GetMappedFrame(int64_t TargetFrameNumber)
Get a frame based on the target frame rate and the new frame number of a frame.
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
bool IsOpen() override
Determine if reader is open or closed.
std::vector< Field > fields
ReaderBase * Reader()
Get the current reader.
std::vector< MappedFrame > frames
void ResampleMappedAudio(std::shared_ptr< Frame > frame, int64_t original_frame_number)
Resample audio and map channels (if needed)
void Open() override
Open the internal reader.
void Close() override
Close the openshot::FrameMapper and internal reader.
std::string Json() const override
Generate JSON string of this object.
FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
Default constructor for openshot::FrameMapper class.
void SetDirectionHint(const bool increasing)
Set time-curve informed direction hint (from Clip class) for the next call to GetFrame.
void PrintMapping(std::ostream *out=&std::cout)
Print all of the original frames and which new frames they map to.
void SetJson(const std::string value) override
Load JSON string into this object.
Json::Value JsonValue() const override
Generate Json::Value for this object.
virtual ~FrameMapper()
Destructor.
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Definition Frame.cpp:484
Exception for invalid JSON.
Definition Exceptions.h:224
Exception for frames that are out of bounds.
Definition Exceptions.h:307
This abstract class is the base class, used by all readers in libopenshot.
Definition ReaderBase.h:76
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
Definition ReaderBase.h:79
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
virtual void Close()=0
Close the reader (and any resources it was consuming)
Exception when a reader is closed, and a frame is requested.
Definition Exceptions.h:370
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition Settings.cpp:23
void AppendDebugMethod(std::string method_name, std::string arg1_name="", float arg1_value=-1.0, std::string arg2_name="", float arg2_value=-1.0, std::string arg3_name="", float arg3_value=-1.0, std::string arg4_name="", float arg4_value=-1.0, std::string arg5_name="", float arg5_value=-1.0, std::string arg6_name="", float arg6_value=-1.0)
Append debug information.
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition ZmqLogger.cpp:35
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
PulldownType
This enumeration determines how frame rates are increased or decreased.
Definition FrameMapper.h:43
@ PULLDOWN_CLASSIC
Classic 2:3:2:3 pull-down.
Definition FrameMapper.h:44
@ PULLDOWN_ADVANCED
Advanced 2:3:3:2 pull-down (minimal dirty frames)
Definition FrameMapper.h:45
@ PULLDOWN_NONE
Do not apply pull-down techniques, just repeat or skip entire frames.
Definition FrameMapper.h:46
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround,...
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
This struct holds a single field (half a frame).
Definition FrameMapper.h:56
This struct holds two fields which together make up a complete video frame.
bool has_single_image
Determines if this file only contains a single image.
Definition ReaderBase.h:42
float duration
Length of time (in seconds)
Definition ReaderBase.h:43
int width
The width of the video (in pixesl)
Definition ReaderBase.h:46
int channels
The number of audio channels used in the audio stream.
Definition ReaderBase.h:61
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition ReaderBase.h:48
int height
The height of the video (in pixels)
Definition ReaderBase.h:45
int64_t video_length
The number of frames in the video stream.
Definition ReaderBase.h:53
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
Definition ReaderBase.h:62
bool has_video
Determines if this file has a video stream.
Definition ReaderBase.h:40
bool has_audio
Determines if this file has an audio stream.
Definition ReaderBase.h:41
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
Definition ReaderBase.h:55
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition ReaderBase.h:60
This struct holds a the range of samples needed by this frame.
Definition FrameMapper.h:68
void Extend(int64_t samples, openshot::Fraction fps, int sample_rate, int channels, bool right_side)
Extend SampleRange on either side.
Definition FrameMapper.h:76
void Shift(int64_t samples, openshot::Fraction fps, int sample_rate, int channels, bool right_side)