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