2011-10-11 08:44:27 -05:00
|
|
|
/**
|
2013-09-09 23:32:16 -05:00
|
|
|
* @file
|
2013-09-12 17:52:10 -05:00
|
|
|
* @brief Source file for the FrameMapper class
|
|
|
|
|
* @author Jonathan Thomas <jonathan@openshot.org>
|
|
|
|
|
*
|
2019-06-09 08:31:04 -04:00
|
|
|
* @ref License
|
|
|
|
|
*/
|
|
|
|
|
|
2021-10-16 01:26:26 -04:00
|
|
|
// Copyright (c) 2008-2019 OpenShot Studios, LLC
|
|
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
2011-10-11 08:44:27 -05:00
|
|
|
|
2021-04-19 20:57:47 -04:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
|
2020-10-18 07:43:37 -04:00
|
|
|
#include "FrameMapper.h"
|
2021-01-26 10:52:04 -05:00
|
|
|
#include "Exceptions.h"
|
2020-10-19 16:28:49 -04:00
|
|
|
#include "Clip.h"
|
2021-11-01 11:04:31 -04:00
|
|
|
#include "ZmqLogger.h"
|
2011-10-11 08:44:27 -05:00
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace openshot;
|
|
|
|
|
|
2020-10-13 17:08:27 -05:00
|
|
|
FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) :
|
2022-10-10 11:17:53 -05:00
|
|
|
reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL), parent_position(0.0), parent_start(0.0)
|
2011-10-11 08:44:27 -05:00
|
|
|
{
|
2012-10-14 23:24:27 -05:00
|
|
|
// Set the original frame rate from the reader
|
2014-01-05 23:12:56 -06:00
|
|
|
original = Fraction(reader->info.fps.num, reader->info.fps.den);
|
2012-10-14 23:24:27 -05:00
|
|
|
|
2012-10-17 09:57:02 -05:00
|
|
|
// Set all info struct members equal to the internal reader
|
|
|
|
|
info = reader->info;
|
2014-01-05 23:12:56 -06:00
|
|
|
info.fps.num = target.num;
|
|
|
|
|
info.fps.den = target.den;
|
|
|
|
|
info.video_timebase.num = target.den;
|
|
|
|
|
info.video_timebase.den = target.num;
|
2012-10-17 09:57:02 -05:00
|
|
|
info.video_length = round(info.duration * info.fps.ToDouble());
|
2015-03-01 22:36:39 -06:00
|
|
|
info.sample_rate = target_sample_rate;
|
|
|
|
|
info.channels = target_channels;
|
|
|
|
|
info.channel_layout = target_channel_layout;
|
2012-11-20 16:22:50 -06:00
|
|
|
info.width = reader->info.width;
|
|
|
|
|
info.height = reader->info.height;
|
2011-10-11 08:44:27 -05:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Enable/Disable audio (based on settings)
|
|
|
|
|
info.has_audio = info.sample_rate > 0 && info.channels > 0;
|
|
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Used to toggle odd / even fields
|
|
|
|
|
field_toggle = true;
|
|
|
|
|
|
2016-07-27 13:18:55 -05:00
|
|
|
// Adjust cache size based on size of frame and audio
|
2022-10-10 11:17:53 -05:00
|
|
|
final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS, info.width, info.height, info.sample_rate, info.channels);
|
2011-10-11 08:44:27 -05:00
|
|
|
}
|
|
|
|
|
|
2015-12-24 16:44:45 -06:00
|
|
|
// Destructor
|
|
|
|
|
FrameMapper::~FrameMapper() {
|
2020-03-22 12:33:59 -04:00
|
|
|
|
|
|
|
|
// Auto Close if not already
|
|
|
|
|
Close();
|
2019-05-08 14:53:23 -07:00
|
|
|
|
|
|
|
|
reader = NULL;
|
2015-12-24 16:44:45 -06:00
|
|
|
}
|
|
|
|
|
|
2017-03-10 00:51:08 -06:00
|
|
|
/// Get the current reader
|
2017-10-26 18:44:35 -05:00
|
|
|
ReaderBase* FrameMapper::Reader()
|
2017-03-10 00:51:08 -06:00
|
|
|
{
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
if (reader)
|
|
|
|
|
return reader;
|
|
|
|
|
else
|
|
|
|
|
// Throw error if reader not initialized
|
|
|
|
|
throw ReaderClosed("No Reader has been initialized for FrameMapper. Call Reader(*reader) before calling this method.");
|
2017-03-10 00:51:08 -06:00
|
|
|
}
|
|
|
|
|
|
2017-09-28 16:03:01 -05:00
|
|
|
void FrameMapper::AddField(int64_t frame)
|
2011-10-11 08:44:27 -05:00
|
|
|
{
|
|
|
|
|
// Add a field, and toggle the odd / even field
|
2012-10-15 17:45:20 -05:00
|
|
|
AddField(Field(frame, field_toggle));
|
2011-10-11 08:44:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FrameMapper::AddField(Field field)
|
|
|
|
|
{
|
|
|
|
|
// Add a field to the end of the field list
|
|
|
|
|
fields.push_back(field);
|
|
|
|
|
|
|
|
|
|
// toggle the odd / even flag
|
|
|
|
|
field_toggle = (field_toggle ? false : true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 18:32:47 -06:00
|
|
|
// Clear both the fields & frames lists
|
|
|
|
|
void FrameMapper::Clear() {
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Prevent async calls to the following code
|
|
|
|
|
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
|
2022-10-11 18:14:36 -05:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Clear the fields & frames lists
|
|
|
|
|
fields.clear();
|
|
|
|
|
fields.shrink_to_fit();
|
|
|
|
|
frames.clear();
|
|
|
|
|
frames.shrink_to_fit();
|
2022-02-02 18:32:47 -06:00
|
|
|
}
|
|
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Use the original and target frame rates and a pull-down technique to create
|
|
|
|
|
// a mapping between the original fields and frames or a video to a new frame rate.
|
|
|
|
|
// This might repeat or skip fields and frames of the original video, depending on
|
|
|
|
|
// whether the frame rate is increasing or decreasing.
|
|
|
|
|
void FrameMapper::Init()
|
|
|
|
|
{
|
2019-07-03 14:14:02 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Init (Calculate frame mappings)");
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2015-09-28 22:05:50 -05:00
|
|
|
// Do not initialize anything if just a picture with no audio
|
|
|
|
|
if (info.has_video and !info.has_audio and info.has_single_image)
|
|
|
|
|
// Skip initialization
|
|
|
|
|
return;
|
|
|
|
|
|
2022-10-11 18:14:36 -05:00
|
|
|
// Prevent async calls to the following code
|
|
|
|
|
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
|
|
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Clear the fields & frames lists
|
2022-02-02 18:32:47 -06:00
|
|
|
Clear();
|
2011-10-11 08:44:27 -05:00
|
|
|
|
2021-04-08 22:34:48 -05:00
|
|
|
// Find parent position (if any)
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
Clip *parent = (Clip *) ParentClip();
|
|
|
|
|
if (parent) {
|
|
|
|
|
parent_position = parent->Position();
|
|
|
|
|
parent_start = parent->Start();
|
|
|
|
|
} else {
|
|
|
|
|
parent_position = 0.0;
|
|
|
|
|
parent_start = 0.0;
|
|
|
|
|
}
|
2021-04-08 22:34:48 -05:00
|
|
|
|
2015-02-26 17:33:09 -06:00
|
|
|
// Mark as not dirty
|
|
|
|
|
is_dirty = false;
|
|
|
|
|
|
|
|
|
|
// Clear cache
|
|
|
|
|
final_cache.Clear();
|
|
|
|
|
|
2013-10-17 17:46:58 -05:00
|
|
|
// Some framerates are handled special, and some use a generic Keyframe curve to
|
|
|
|
|
// map the framerates. These are the special framerates:
|
2016-07-27 13:18:55 -05:00
|
|
|
if ((fabs(original.ToFloat() - 24.0) < 1e-7 || fabs(original.ToFloat() - 25.0) < 1e-7 || fabs(original.ToFloat() - 30.0) < 1e-7) &&
|
|
|
|
|
(fabs(target.ToFloat() - 24.0) < 1e-7 || fabs(target.ToFloat() - 25.0) < 1e-7 || fabs(target.ToFloat() - 30.0) < 1e-7)) {
|
2012-10-30 18:53:26 -05:00
|
|
|
|
2013-10-17 17:46:58 -05:00
|
|
|
// Get the difference (in frames) between the original and target frame rates
|
2014-01-05 23:12:56 -06:00
|
|
|
float difference = target.ToInt() - original.ToInt();
|
2011-10-11 08:44:27 -05:00
|
|
|
|
2013-10-17 17:46:58 -05:00
|
|
|
// Find the number (i.e. interval) of fields that need to be skipped or repeated
|
|
|
|
|
int field_interval = 0;
|
|
|
|
|
int frame_interval = 0;
|
|
|
|
|
|
|
|
|
|
if (difference != 0)
|
2011-10-11 08:44:27 -05:00
|
|
|
{
|
2014-01-05 23:12:56 -06:00
|
|
|
field_interval = round(fabs(original.ToInt() / difference));
|
2011-10-11 08:44:27 -05:00
|
|
|
|
2013-10-17 17:46:58 -05:00
|
|
|
// Get frame interval (2 fields per frame)
|
|
|
|
|
frame_interval = field_interval * 2.0f;
|
2011-10-11 08:44:27 -05:00
|
|
|
}
|
|
|
|
|
|
2013-10-17 17:46:58 -05:00
|
|
|
|
|
|
|
|
// Calculate # of fields to map
|
2017-09-28 16:03:01 -05:00
|
|
|
int64_t frame = 1;
|
|
|
|
|
int64_t number_of_fields = reader->info.video_length * 2;
|
2013-10-17 17:46:58 -05:00
|
|
|
|
|
|
|
|
// Loop through all fields in the original video file
|
2017-09-28 16:03:01 -05:00
|
|
|
for (int64_t field = 1; field <= number_of_fields; field++)
|
2013-10-17 17:46:58 -05:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (difference == 0) // Same frame rate, NO pull-down or special techniques required
|
|
|
|
|
{
|
|
|
|
|
// Add fields
|
|
|
|
|
AddField(frame);
|
|
|
|
|
}
|
|
|
|
|
else if (difference > 0) // Need to ADD fake fields & frames, because original video has too few frames
|
|
|
|
|
{
|
|
|
|
|
// Add current field
|
|
|
|
|
AddField(frame);
|
|
|
|
|
|
|
|
|
|
if (pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
|
|
|
|
|
{
|
|
|
|
|
// Add extra field for each 'field interval
|
|
|
|
|
AddField(frame);
|
|
|
|
|
}
|
|
|
|
|
else if (pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
|
|
|
|
|
{
|
|
|
|
|
// Add both extra fields in the middle 'together' (i.e. 2:3:3:2 technique)
|
|
|
|
|
AddField(frame); // add field for current frame
|
|
|
|
|
|
|
|
|
|
if (frame + 1 <= info.video_length)
|
|
|
|
|
// add field for next frame (if the next frame exists)
|
|
|
|
|
AddField(Field(frame + 1, field_toggle));
|
|
|
|
|
}
|
|
|
|
|
else if (pulldown == PULLDOWN_NONE && field % frame_interval == 0)
|
|
|
|
|
{
|
|
|
|
|
// No pull-down technique needed, just repeat this frame
|
|
|
|
|
AddField(frame);
|
|
|
|
|
AddField(frame);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (difference < 0) // Need to SKIP fake fields & frames, because we want to return to the original film frame rate
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
|
|
|
|
|
{
|
|
|
|
|
// skip current field and toggle the odd/even flag
|
|
|
|
|
field_toggle = (field_toggle ? false : true);
|
|
|
|
|
}
|
|
|
|
|
else if (pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
|
|
|
|
|
{
|
|
|
|
|
// skip this field, plus the next field
|
|
|
|
|
field++;
|
|
|
|
|
}
|
|
|
|
|
else if (pulldown == PULLDOWN_NONE && frame % field_interval == 0)
|
|
|
|
|
{
|
|
|
|
|
// skip this field, plus the next one
|
|
|
|
|
field++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No skipping needed, so add the field
|
|
|
|
|
AddField(frame);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// increment frame number (if field is divisible by 2)
|
|
|
|
|
if (field % 2 == 0 && field > 0)
|
|
|
|
|
frame++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2019-03-06 15:35:03 -06:00
|
|
|
// Map the remaining framerates using a linear algorithm
|
2017-05-26 01:08:20 -05:00
|
|
|
double rate_diff = target.ToDouble() / original.ToDouble();
|
2017-09-28 16:03:01 -05:00
|
|
|
int64_t new_length = reader->info.video_length * rate_diff;
|
2013-10-17 17:46:58 -05:00
|
|
|
|
2019-03-06 15:35:03 -06:00
|
|
|
// Calculate the value difference
|
2022-06-25 17:42:30 -05:00
|
|
|
double value_increment = reader->info.video_length / (double) (new_length);
|
2013-10-17 17:46:58 -05:00
|
|
|
|
|
|
|
|
// Loop through curve, and build list of frames
|
2019-03-06 15:35:03 -06:00
|
|
|
double original_frame_num = 1.0f;
|
2017-09-28 16:03:01 -05:00
|
|
|
for (int64_t frame_num = 1; frame_num <= new_length; frame_num++)
|
2013-10-17 17:46:58 -05:00
|
|
|
{
|
|
|
|
|
// Add 2 fields per frame
|
2019-03-06 15:35:03 -06:00
|
|
|
AddField(round(original_frame_num));
|
|
|
|
|
AddField(round(original_frame_num));
|
|
|
|
|
|
|
|
|
|
// Increment original frame number
|
|
|
|
|
original_frame_num += value_increment;
|
2013-10-17 17:46:58 -05:00
|
|
|
}
|
2011-10-11 08:44:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Loop through the target frames again (combining fields into frames)
|
|
|
|
|
Field Odd(0, true); // temp field used to track the ODD field
|
|
|
|
|
Field Even(0, true); // temp field used to track the EVEN field
|
|
|
|
|
|
2012-10-15 17:45:20 -05:00
|
|
|
// Variables used to remap audio samples
|
2017-09-28 16:03:01 -05:00
|
|
|
int64_t start_samples_frame = 1;
|
2012-10-15 17:45:20 -05:00
|
|
|
int start_samples_position = 0;
|
|
|
|
|
|
2019-12-15 14:22:59 -05:00
|
|
|
for (std::vector<Field>::size_type field = 1; field <= fields.size(); field++)
|
2011-10-11 08:44:27 -05:00
|
|
|
{
|
|
|
|
|
// Get the current field
|
|
|
|
|
Field f = fields[field - 1];
|
|
|
|
|
|
|
|
|
|
// Is field divisible by 2?
|
|
|
|
|
if (field % 2 == 0 && field > 0)
|
|
|
|
|
{
|
2012-10-15 17:45:20 -05:00
|
|
|
// New frame number
|
2018-05-30 03:20:31 -05:00
|
|
|
int64_t frame_number = field / 2;
|
2012-10-15 17:45:20 -05:00
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Set the bottom frame
|
|
|
|
|
if (f.isOdd)
|
|
|
|
|
Odd = f;
|
|
|
|
|
else
|
|
|
|
|
Even = f;
|
|
|
|
|
|
2015-03-01 22:36:39 -06:00
|
|
|
// Determine the range of samples (from the original rate). Resampling happens in real-time when
|
|
|
|
|
// calling the GetFrame() method. So this method only needs to redistribute the original samples with
|
|
|
|
|
// the original sample rate.
|
2017-09-28 16:03:01 -05:00
|
|
|
int64_t end_samples_frame = start_samples_frame;
|
2012-10-15 17:45:20 -05:00
|
|
|
int end_samples_position = start_samples_position;
|
2020-10-20 13:03:10 -05:00
|
|
|
int remaining_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, reader->info.sample_rate, reader->info.channels);
|
2012-10-15 17:45:20 -05:00
|
|
|
|
|
|
|
|
while (remaining_samples > 0)
|
|
|
|
|
{
|
2021-04-08 22:34:48 -05:00
|
|
|
// Get original samples (with NO framerate adjustments)
|
|
|
|
|
// This is the original reader's frame numbers
|
|
|
|
|
int original_samples = Frame::GetSamplesPerFrame(end_samples_frame, original, reader->info.sample_rate, reader->info.channels) - end_samples_position;
|
2012-10-15 17:45:20 -05:00
|
|
|
|
|
|
|
|
// Enough samples
|
|
|
|
|
if (original_samples >= remaining_samples)
|
|
|
|
|
{
|
|
|
|
|
// Take all that we need, and break loop
|
2017-01-06 20:48:47 -05:00
|
|
|
end_samples_position += remaining_samples - 1;
|
2012-10-15 17:45:20 -05:00
|
|
|
remaining_samples = 0;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
// Not enough samples (take them all, and keep looping)
|
|
|
|
|
end_samples_frame += 1; // next frame
|
|
|
|
|
end_samples_position = 0; // next frame, starting on 1st sample
|
|
|
|
|
remaining_samples -= original_samples; // reduce the remaining amount
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-17 17:46:58 -05:00
|
|
|
|
|
|
|
|
|
2012-10-15 17:45:20 -05:00
|
|
|
// Create the sample mapping struct
|
2020-10-20 13:03:10 -05:00
|
|
|
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)};
|
2012-10-15 17:45:20 -05:00
|
|
|
|
|
|
|
|
// Reset the audio variables
|
|
|
|
|
start_samples_frame = end_samples_frame;
|
|
|
|
|
start_samples_position = end_samples_position + 1;
|
2020-10-20 13:03:10 -05:00
|
|
|
if (start_samples_position >= Frame::GetSamplesPerFrame(AdjustFrameNumber(start_samples_frame), original, reader->info.sample_rate, reader->info.channels))
|
2012-10-15 17:45:20 -05:00
|
|
|
{
|
|
|
|
|
start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one)
|
|
|
|
|
start_samples_position = 0; // reset to 0, since we wrapped
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Create a frame and ADD it to the frames collection
|
2012-10-15 17:45:20 -05:00
|
|
|
MappedFrame frame = {Odd, Even, Samples};
|
2011-10-11 08:44:27 -05:00
|
|
|
frames.push_back(frame);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Set the top field
|
|
|
|
|
if (f.isOdd)
|
|
|
|
|
Odd = f;
|
|
|
|
|
else
|
|
|
|
|
Even = f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the internal fields list (no longer needed)
|
|
|
|
|
fields.clear();
|
2022-02-02 18:32:47 -06:00
|
|
|
fields.shrink_to_fit();
|
2011-10-11 08:44:27 -05:00
|
|
|
}
|
|
|
|
|
|
2017-10-26 18:44:35 -05:00
|
|
|
MappedFrame FrameMapper::GetMappedFrame(int64_t TargetFrameNumber)
|
2011-10-11 08:44:27 -05:00
|
|
|
{
|
2019-03-06 15:35:03 -06:00
|
|
|
// Check if mappings are dirty (and need to be recalculated)
|
|
|
|
|
if (is_dirty)
|
|
|
|
|
// Recalculate mappings
|
|
|
|
|
Init();
|
|
|
|
|
|
2015-09-28 22:05:50 -05:00
|
|
|
// Ignore mapping on single image readers
|
|
|
|
|
if (info.has_video and !info.has_audio and info.has_single_image) {
|
|
|
|
|
// Return the same number
|
|
|
|
|
MappedFrame frame;
|
|
|
|
|
frame.Even.Frame = TargetFrameNumber;
|
|
|
|
|
frame.Odd.Frame = TargetFrameNumber;
|
|
|
|
|
frame.Samples.frame_start = 0;
|
|
|
|
|
frame.Samples.frame_end = 0;
|
|
|
|
|
frame.Samples.sample_start = 0;
|
|
|
|
|
frame.Samples.sample_end = 0;
|
|
|
|
|
frame.Samples.total = 0;
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Check if frame number is valid
|
2014-01-06 00:04:40 -06:00
|
|
|
if(TargetFrameNumber < 1 || frames.size() == 0)
|
2013-11-04 17:30:14 -06:00
|
|
|
// frame too small, return error
|
2011-10-11 08:44:27 -05:00
|
|
|
throw OutOfBoundsFrame("An invalid frame was requested.", TargetFrameNumber, frames.size());
|
2013-11-04 17:30:14 -06:00
|
|
|
|
2019-12-15 14:22:59 -05:00
|
|
|
else if (TargetFrameNumber > (int64_t)frames.size())
|
2013-11-04 17:30:14 -06:00
|
|
|
// frame too large, set to end frame
|
|
|
|
|
TargetFrameNumber = frames.size();
|
2011-10-11 08:44:27 -05:00
|
|
|
|
2016-04-24 15:37:47 -05:00
|
|
|
// Debug output
|
2022-01-12 10:53:58 -05:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::GetMappedFrame",
|
|
|
|
|
"TargetFrameNumber", TargetFrameNumber,
|
|
|
|
|
"frames.size()", frames.size(),
|
|
|
|
|
"frames[...].Odd", frames[TargetFrameNumber - 1].Odd.Frame,
|
|
|
|
|
"frames[...].Even", frames[TargetFrameNumber - 1].Even.Frame);
|
2016-04-24 15:37:47 -05:00
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Return frame
|
|
|
|
|
return frames[TargetFrameNumber - 1];
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-24 16:44:45 -06:00
|
|
|
// Get or generate a blank frame
|
2017-09-28 16:03:01 -05:00
|
|
|
std::shared_ptr<Frame> FrameMapper::GetOrCreateFrame(int64_t number)
|
2015-12-24 16:44:45 -06:00
|
|
|
{
|
2017-08-20 17:37:39 -05:00
|
|
|
std::shared_ptr<Frame> new_frame;
|
2015-12-24 16:44:45 -06:00
|
|
|
|
2016-07-27 13:18:55 -05:00
|
|
|
// Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now)
|
2020-10-20 13:03:10 -05:00
|
|
|
int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(number), target, reader->info.sample_rate, reader->info.channels);
|
2015-12-24 16:44:45 -06:00
|
|
|
|
|
|
|
|
try {
|
2016-04-24 15:37:47 -05:00
|
|
|
// Debug output
|
2022-01-12 10:53:58 -05:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::GetOrCreateFrame (from reader)",
|
|
|
|
|
"number", number,
|
|
|
|
|
"samples_in_frame", samples_in_frame);
|
2016-04-24 15:37:47 -05:00
|
|
|
|
2015-12-24 16:44:45 -06:00
|
|
|
// Attempt to get a frame (but this could fail if a reader has just been closed)
|
|
|
|
|
new_frame = reader->GetFrame(number);
|
|
|
|
|
|
|
|
|
|
// Return real frame
|
|
|
|
|
return new_frame;
|
|
|
|
|
|
|
|
|
|
} catch (const ReaderClosed & e) {
|
|
|
|
|
// ...
|
|
|
|
|
} catch (const OutOfBoundsFrame & e) {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-24 15:37:47 -05:00
|
|
|
// Debug output
|
2022-01-12 10:53:58 -05:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::GetOrCreateFrame (create blank)",
|
|
|
|
|
"number", number,
|
|
|
|
|
"samples_in_frame", samples_in_frame);
|
2016-04-24 15:37:47 -05:00
|
|
|
|
2015-12-24 16:44:45 -06:00
|
|
|
// Create blank frame
|
2017-08-20 17:37:39 -05:00
|
|
|
new_frame = std::make_shared<Frame>(number, info.width, info.height, "#000000", samples_in_frame, reader->info.channels);
|
2016-07-27 13:18:55 -05:00
|
|
|
new_frame->SampleRate(reader->info.sample_rate);
|
2015-12-24 16:44:45 -06:00
|
|
|
new_frame->ChannelsLayout(info.channel_layout);
|
2018-09-11 00:40:31 -05:00
|
|
|
new_frame->AddAudioSilence(samples_in_frame);
|
2015-12-24 16:44:45 -06:00
|
|
|
return new_frame;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-17 09:57:02 -05:00
|
|
|
// Get an openshot::Frame object for a specific frame number of this reader.
|
2017-10-26 18:44:35 -05:00
|
|
|
std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)
|
2012-10-17 09:57:02 -05:00
|
|
|
{
|
2015-06-01 00:20:14 -07:00
|
|
|
// Check final cache, and just return the frame (if it's available)
|
2017-08-20 17:37:39 -05:00
|
|
|
std::shared_ptr<Frame> final_frame = final_cache.GetFrame(requested_frame);
|
2015-08-05 23:40:58 -05:00
|
|
|
if (final_frame) return final_frame;
|
2015-06-01 00:20:14 -07:00
|
|
|
|
|
|
|
|
// Create a scoped lock, allowing only a single thread to run the following code at one time
|
2021-10-27 14:34:05 -04:00
|
|
|
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
|
2015-06-01 00:20:14 -07:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Find parent properties (if any)
|
|
|
|
|
Clip *parent = (Clip *) ParentClip();
|
|
|
|
|
if (parent) {
|
|
|
|
|
float position = parent->Position();
|
|
|
|
|
float start = parent->Start();
|
|
|
|
|
if (parent_position != position || parent_start != start) {
|
|
|
|
|
// Force dirty if parent clip has moved or been trimmed
|
|
|
|
|
// since this heavily affects frame #s and audio mappings
|
|
|
|
|
is_dirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-08 22:34:48 -05:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Check if mappings are dirty (and need to be recalculated)
|
2015-02-26 17:33:09 -06:00
|
|
|
if (is_dirty)
|
|
|
|
|
Init();
|
|
|
|
|
|
2015-06-01 00:20:14 -07:00
|
|
|
// Check final cache a 2nd time (due to potential lock already generating this frame)
|
2015-08-05 23:40:58 -05:00
|
|
|
final_frame = final_cache.GetFrame(requested_frame);
|
|
|
|
|
if (final_frame) return final_frame;
|
2012-10-17 09:57:02 -05:00
|
|
|
|
2015-03-08 21:42:53 -05:00
|
|
|
// Minimum number of frames to process (for performance reasons)
|
2017-07-19 16:05:07 -05:00
|
|
|
// Dialing this down to 1 for now, as it seems to improve performance, and reduce export crashes
|
|
|
|
|
int minimum_frames = 1;
|
2015-02-26 17:33:09 -06:00
|
|
|
|
2016-04-11 17:43:56 -05:00
|
|
|
// Debug output
|
2022-01-12 10:53:58 -05:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::GetFrame (Loop through frames)",
|
|
|
|
|
"requested_frame", requested_frame,
|
|
|
|
|
"minimum_frames", minimum_frames);
|
2016-04-11 17:43:56 -05:00
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Loop through all requested frames
|
2017-09-28 16:03:01 -05:00
|
|
|
for (int64_t frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
|
2012-10-17 09:57:02 -05:00
|
|
|
{
|
2017-06-22 15:26:40 -05:00
|
|
|
|
|
|
|
|
// Debug output
|
2022-01-12 10:53:58 -05:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::GetFrame (inside omp for loop)",
|
|
|
|
|
"frame_number", frame_number,
|
|
|
|
|
"minimum_frames", minimum_frames,
|
|
|
|
|
"requested_frame", requested_frame);
|
2017-06-22 15:26:40 -05:00
|
|
|
|
|
|
|
|
// Get the mapped frame
|
|
|
|
|
MappedFrame mapped = GetMappedFrame(frame_number);
|
2017-08-20 17:37:39 -05:00
|
|
|
std::shared_ptr<Frame> mapped_frame;
|
2017-06-22 15:26:40 -05:00
|
|
|
|
|
|
|
|
// Get the mapped frame (keeping the sample rate and channels the same as the original... for the moment)
|
|
|
|
|
mapped_frame = GetOrCreateFrame(mapped.Odd.Frame);
|
|
|
|
|
|
|
|
|
|
// Get # of channels in the actual frame
|
|
|
|
|
int channels_in_frame = mapped_frame->GetAudioChannelsCount();
|
2020-10-20 13:03:10 -05:00
|
|
|
int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, mapped_frame->SampleRate(), channels_in_frame);
|
2017-06-22 15:26:40 -05:00
|
|
|
|
|
|
|
|
// Determine if mapped frame is identical to source frame
|
|
|
|
|
// including audio sample distribution according to mapped.Samples,
|
|
|
|
|
// and frame_number. In some cases such as end of stream, the reader
|
|
|
|
|
// will return a frame with a different frame number. In these cases,
|
|
|
|
|
// we cannot use the frame as is, nor can we modify the frame number,
|
|
|
|
|
// otherwise the reader's cache object internals become invalid.
|
|
|
|
|
if (info.sample_rate == mapped_frame->SampleRate() &&
|
|
|
|
|
info.channels == mapped_frame->GetAudioChannelsCount() &&
|
|
|
|
|
info.channel_layout == mapped_frame->ChannelsLayout() &&
|
|
|
|
|
mapped.Samples.total == mapped_frame->GetAudioSamplesCount() &&
|
|
|
|
|
mapped.Samples.frame_start == mapped.Odd.Frame &&
|
|
|
|
|
mapped.Samples.sample_start == 0 &&
|
|
|
|
|
mapped_frame->number == frame_number &&// in some conditions (e.g. end of stream)
|
|
|
|
|
info.fps.num == reader->info.fps.num &&
|
|
|
|
|
info.fps.den == reader->info.fps.den) {
|
|
|
|
|
// Add original frame to cache, and skip the rest (for performance reasons)
|
|
|
|
|
final_cache.Add(mapped_frame);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a new frame
|
2020-08-20 16:50:12 -04:00
|
|
|
auto frame = std::make_shared<Frame>(
|
|
|
|
|
frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame);
|
2017-06-22 15:26:40 -05:00
|
|
|
frame->SampleRate(mapped_frame->SampleRate());
|
|
|
|
|
frame->ChannelsLayout(mapped_frame->ChannelsLayout());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Copy the image from the odd field
|
2017-08-20 17:37:39 -05:00
|
|
|
std::shared_ptr<Frame> odd_frame;
|
2017-06-22 15:26:40 -05:00
|
|
|
odd_frame = GetOrCreateFrame(mapped.Odd.Frame);
|
|
|
|
|
|
|
|
|
|
if (odd_frame)
|
2020-08-20 16:50:12 -04:00
|
|
|
frame->AddImage(std::make_shared<QImage>(*odd_frame->GetImage()), true);
|
2017-06-22 15:26:40 -05:00
|
|
|
if (mapped.Odd.Frame != mapped.Even.Frame) {
|
|
|
|
|
// Add even lines (if different than the previous image)
|
2017-08-20 17:37:39 -05:00
|
|
|
std::shared_ptr<Frame> even_frame;
|
2017-06-22 15:26:40 -05:00
|
|
|
even_frame = GetOrCreateFrame(mapped.Even.Frame);
|
|
|
|
|
if (even_frame)
|
2020-08-20 16:50:12 -04:00
|
|
|
frame->AddImage(
|
|
|
|
|
std::make_shared<QImage>(*even_frame->GetImage()), false);
|
2017-06-22 15:26:40 -05:00
|
|
|
}
|
|
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Determine if reader contains audio samples
|
|
|
|
|
bool reader_has_audio = frame->SampleRate() > 0 && frame->GetAudioChannelsCount() > 0;
|
|
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Resample audio on frame (if needed)
|
|
|
|
|
bool need_resampling = false;
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
if ((info.has_audio && reader_has_audio) &&
|
2017-06-22 15:26:40 -05:00
|
|
|
(info.sample_rate != frame->SampleRate() ||
|
|
|
|
|
info.channels != frame->GetAudioChannelsCount() ||
|
|
|
|
|
info.channel_layout != frame->ChannelsLayout()))
|
|
|
|
|
// Resample audio and correct # of channels if needed
|
|
|
|
|
need_resampling = true;
|
|
|
|
|
|
|
|
|
|
// create a copy of mapped.Samples that will be used by copy loop
|
|
|
|
|
SampleRange copy_samples = mapped.Samples;
|
|
|
|
|
|
|
|
|
|
if (need_resampling)
|
2012-10-17 09:57:02 -05:00
|
|
|
{
|
2017-06-22 15:26:40 -05:00
|
|
|
// Resampling needed, modify copy of SampleRange object that
|
|
|
|
|
// includes some additional input samples on first iteration,
|
|
|
|
|
// and continues the offset to ensure that the sample rate
|
|
|
|
|
// converter isn't input limited.
|
2020-09-14 03:13:54 -05:00
|
|
|
const int EXTRA_INPUT_SAMPLES = 100;
|
2016-04-24 15:37:47 -05:00
|
|
|
|
2018-01-06 02:22:05 -06:00
|
|
|
// Extend end sample count by an additional EXTRA_INPUT_SAMPLES samples
|
2017-06-22 15:26:40 -05:00
|
|
|
copy_samples.sample_end += EXTRA_INPUT_SAMPLES;
|
|
|
|
|
int samples_per_end_frame =
|
|
|
|
|
Frame::GetSamplesPerFrame(copy_samples.frame_end, original,
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
reader->info.sample_rate, reader->info.channels);
|
2017-06-22 15:26:40 -05:00
|
|
|
if (copy_samples.sample_end >= samples_per_end_frame)
|
2017-01-06 20:48:47 -05:00
|
|
|
{
|
2017-06-22 15:26:40 -05:00
|
|
|
// check for wrapping
|
|
|
|
|
copy_samples.frame_end++;
|
|
|
|
|
copy_samples.sample_end -= samples_per_end_frame;
|
|
|
|
|
}
|
|
|
|
|
copy_samples.total += EXTRA_INPUT_SAMPLES;
|
2017-01-06 20:48:47 -05:00
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
if (avr) {
|
|
|
|
|
// Sample rate conversion has been allocated on this clip, so
|
|
|
|
|
// this is not the first iteration. Extend start position by
|
|
|
|
|
// EXTRA_INPUT_SAMPLES to keep step with previous frame
|
|
|
|
|
copy_samples.sample_start += EXTRA_INPUT_SAMPLES;
|
|
|
|
|
int samples_per_start_frame =
|
|
|
|
|
Frame::GetSamplesPerFrame(copy_samples.frame_start, original,
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
reader->info.sample_rate, reader->info.channels);
|
2017-06-22 15:26:40 -05:00
|
|
|
if (copy_samples.sample_start >= samples_per_start_frame)
|
2017-01-06 20:48:47 -05:00
|
|
|
{
|
|
|
|
|
// check for wrapping
|
2017-06-22 15:26:40 -05:00
|
|
|
copy_samples.frame_start++;
|
|
|
|
|
copy_samples.sample_start -= samples_per_start_frame;
|
2017-01-06 20:48:47 -05:00
|
|
|
}
|
2017-06-22 15:26:40 -05:00
|
|
|
copy_samples.total -= EXTRA_INPUT_SAMPLES;
|
2017-01-06 20:48:47 -05:00
|
|
|
}
|
2017-06-22 15:26:40 -05:00
|
|
|
}
|
2017-01-06 20:48:47 -05:00
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Copy the samples
|
|
|
|
|
int samples_copied = 0;
|
2017-09-28 16:03:01 -05:00
|
|
|
int64_t starting_frame = copy_samples.frame_start;
|
2017-06-22 15:26:40 -05:00
|
|
|
while (info.has_audio && samples_copied < copy_samples.total)
|
|
|
|
|
{
|
|
|
|
|
// Init number of samples to copy this iteration
|
|
|
|
|
int remaining_samples = copy_samples.total - samples_copied;
|
|
|
|
|
int number_to_copy = 0;
|
|
|
|
|
|
2017-07-19 16:05:07 -05:00
|
|
|
// number of original samples on this frame
|
2017-08-20 17:37:39 -05:00
|
|
|
std::shared_ptr<Frame> original_frame = GetOrCreateFrame(starting_frame);
|
2017-07-19 16:05:07 -05:00
|
|
|
int original_samples = original_frame->GetAudioSamplesCount();
|
|
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Loop through each channel
|
|
|
|
|
for (int channel = 0; channel < channels_in_frame; channel++)
|
2012-10-17 09:57:02 -05:00
|
|
|
{
|
2017-06-22 15:26:40 -05:00
|
|
|
if (starting_frame == copy_samples.frame_start)
|
2015-03-08 21:42:53 -05:00
|
|
|
{
|
2017-06-22 15:26:40 -05:00
|
|
|
// Starting frame (take the ending samples)
|
|
|
|
|
number_to_copy = original_samples - copy_samples.sample_start;
|
|
|
|
|
if (number_to_copy > remaining_samples)
|
|
|
|
|
number_to_copy = remaining_samples;
|
2015-03-08 21:42:53 -05:00
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Add samples to new frame
|
|
|
|
|
frame->AddAudio(true, channel, samples_copied, original_frame->GetAudioSamples(channel) + copy_samples.sample_start, number_to_copy, 1.0);
|
2015-03-08 21:42:53 -05:00
|
|
|
}
|
2017-06-22 15:26:40 -05:00
|
|
|
else if (starting_frame > copy_samples.frame_start && starting_frame < copy_samples.frame_end)
|
|
|
|
|
{
|
|
|
|
|
// Middle frame (take all samples)
|
|
|
|
|
number_to_copy = original_samples;
|
|
|
|
|
if (number_to_copy > remaining_samples)
|
|
|
|
|
number_to_copy = remaining_samples;
|
2015-03-08 21:42:53 -05:00
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Add samples to new frame
|
|
|
|
|
frame->AddAudio(true, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Ending frame (take the beginning samples)
|
|
|
|
|
number_to_copy = copy_samples.sample_end + 1;
|
|
|
|
|
if (number_to_copy > remaining_samples)
|
|
|
|
|
number_to_copy = remaining_samples;
|
|
|
|
|
|
|
|
|
|
// Add samples to new frame
|
|
|
|
|
frame->AddAudio(false, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
|
|
|
|
|
}
|
2012-10-17 09:57:02 -05:00
|
|
|
}
|
|
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// increment frame
|
|
|
|
|
samples_copied += number_to_copy;
|
|
|
|
|
starting_frame++;
|
|
|
|
|
}
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Resample audio on frame (if needed)
|
|
|
|
|
if (need_resampling)
|
|
|
|
|
// Resample audio and correct # of channels if needed
|
|
|
|
|
ResampleMappedAudio(frame, mapped.Odd.Frame);
|
2015-03-08 21:42:53 -05:00
|
|
|
|
2017-06-22 15:26:40 -05:00
|
|
|
// Add frame to final cache
|
|
|
|
|
final_cache.Add(frame);
|
|
|
|
|
|
|
|
|
|
} // for loop
|
2012-10-17 09:57:02 -05:00
|
|
|
|
2015-03-09 15:17:56 -05:00
|
|
|
// Return processed openshot::Frame
|
2015-03-08 21:42:53 -05:00
|
|
|
return final_cache.GetFrame(requested_frame);
|
2012-10-17 09:57:02 -05:00
|
|
|
}
|
|
|
|
|
|
2021-04-19 20:57:47 -04:00
|
|
|
void FrameMapper::PrintMapping(std::ostream* out)
|
2011-10-11 08:44:27 -05:00
|
|
|
{
|
2016-07-27 13:18:55 -05:00
|
|
|
// Check if mappings are dirty (and need to be recalculated)
|
|
|
|
|
if (is_dirty)
|
|
|
|
|
// Recalculate mappings
|
|
|
|
|
Init();
|
|
|
|
|
|
2011-10-11 08:44:27 -05:00
|
|
|
// Loop through frame mappings
|
|
|
|
|
for (float map = 1; map <= frames.size(); map++)
|
|
|
|
|
{
|
|
|
|
|
MappedFrame frame = frames[map - 1];
|
2021-04-19 20:57:47 -04:00
|
|
|
*out << "Target frame #: " << map
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
<< " mapped to original frame #:\t("
|
|
|
|
|
<< frame.Odd.Frame << " odd, "
|
|
|
|
|
<< frame.Even.Frame << " even)" << std::endl;
|
2021-04-19 20:57:47 -04:00
|
|
|
|
|
|
|
|
*out << " - Audio samples mapped to frame "
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
<< frame.Samples.frame_start << ":"
|
|
|
|
|
<< frame.Samples.sample_start << " to frame "
|
|
|
|
|
<< frame.Samples.frame_end << ":"
|
|
|
|
|
<< frame.Samples.sample_end << endl;
|
2011-10-11 08:44:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2012-10-17 09:57:02 -05:00
|
|
|
|
2013-12-18 21:55:43 -06:00
|
|
|
// Determine if reader is open or closed
|
|
|
|
|
bool FrameMapper::IsOpen() {
|
|
|
|
|
if (reader)
|
|
|
|
|
return reader->IsOpen();
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-17 09:57:02 -05:00
|
|
|
// Open the internal reader
|
2017-10-26 18:44:35 -05:00
|
|
|
void FrameMapper::Open()
|
2012-10-17 09:57:02 -05:00
|
|
|
{
|
|
|
|
|
if (reader)
|
|
|
|
|
{
|
2019-07-03 14:14:02 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Open");
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2012-10-17 09:57:02 -05:00
|
|
|
// Open the reader
|
|
|
|
|
reader->Open();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close the internal reader
|
|
|
|
|
void FrameMapper::Close()
|
|
|
|
|
{
|
|
|
|
|
if (reader)
|
2015-03-01 22:36:39 -06:00
|
|
|
{
|
2015-12-24 16:44:45 -06:00
|
|
|
// Create a scoped lock, allowing only a single thread to run the following code at one time
|
2021-10-27 14:34:05 -04:00
|
|
|
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
|
2015-12-24 16:44:45 -06:00
|
|
|
|
2019-07-03 14:14:02 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Close");
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2015-03-08 21:42:53 -05:00
|
|
|
// Close internal reader
|
|
|
|
|
reader->Close();
|
2015-03-01 22:36:39 -06:00
|
|
|
}
|
2022-01-26 17:56:33 -06:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Clear the fields & frames lists
|
|
|
|
|
Clear();
|
2022-01-26 17:56:33 -06:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Mark as dirty
|
|
|
|
|
is_dirty = true;
|
2022-01-26 17:56:33 -06:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Clear cache
|
|
|
|
|
final_cache.Clear();
|
2022-01-26 17:56:33 -06:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Deallocate resample buffer
|
|
|
|
|
if (avr) {
|
|
|
|
|
SWR_CLOSE(avr);
|
|
|
|
|
SWR_FREE(&avr);
|
|
|
|
|
avr = NULL;
|
|
|
|
|
}
|
2012-10-17 09:57:02 -05:00
|
|
|
}
|
2013-12-07 21:09:55 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate JSON string of this object
|
2019-12-27 08:51:51 -05:00
|
|
|
std::string FrameMapper::Json() const {
|
2013-12-07 21:09:55 -06:00
|
|
|
|
|
|
|
|
// Return formatted string
|
|
|
|
|
return JsonValue().toStyledString();
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 08:51:51 -05:00
|
|
|
// Generate Json::Value for this object
|
|
|
|
|
Json::Value FrameMapper::JsonValue() const {
|
2013-12-07 21:09:55 -06:00
|
|
|
|
|
|
|
|
// Create root json object
|
|
|
|
|
Json::Value root = ReaderBase::JsonValue(); // get parent properties
|
|
|
|
|
root["type"] = "FrameMapper";
|
2023-02-13 16:42:21 -06:00
|
|
|
if (reader) {
|
|
|
|
|
root["reader"] = reader->JsonValue();
|
|
|
|
|
}
|
2013-12-07 21:09:55 -06:00
|
|
|
|
|
|
|
|
// return JsonValue
|
|
|
|
|
return root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load JSON string into this object
|
2019-12-27 08:51:51 -05:00
|
|
|
void FrameMapper::SetJson(const std::string value) {
|
2013-12-07 21:09:55 -06:00
|
|
|
|
|
|
|
|
// Parse JSON string into JSON objects
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-12-27 08:51:51 -05:00
|
|
|
const Json::Value root = openshot::stringToJson(value);
|
2013-12-07 21:09:55 -06:00
|
|
|
// Set all values that match
|
|
|
|
|
SetJsonValue(root);
|
2023-02-13 16:42:21 -06:00
|
|
|
|
|
|
|
|
if (!root["reader"].isNull()) // does Json contain a reader?
|
|
|
|
|
{
|
|
|
|
|
// Placeholder to load reader
|
|
|
|
|
// TODO: need a createReader method for this and Clip JSON methods
|
|
|
|
|
}
|
2013-12-07 21:09:55 -06:00
|
|
|
}
|
2019-07-03 12:58:02 -04:00
|
|
|
catch (const std::exception& e)
|
2013-12-07 21:09:55 -06:00
|
|
|
{
|
|
|
|
|
// Error parsing JSON (or missing keys)
|
2019-08-27 15:47:39 -04:00
|
|
|
throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
|
2013-12-07 21:09:55 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 08:51:51 -05:00
|
|
|
// Load Json::Value into this object
|
|
|
|
|
void FrameMapper::SetJsonValue(const Json::Value root) {
|
2013-12-07 21:09:55 -06:00
|
|
|
|
|
|
|
|
// Set parent data
|
|
|
|
|
ReaderBase::SetJsonValue(root);
|
|
|
|
|
|
|
|
|
|
// Re-Open path, and re-init everything (if needed)
|
|
|
|
|
if (reader) {
|
|
|
|
|
|
|
|
|
|
Close();
|
|
|
|
|
Open();
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-26 17:33:09 -06:00
|
|
|
|
|
|
|
|
// Change frame rate or audio mapping details
|
2020-10-13 17:08:27 -05:00
|
|
|
void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
|
2015-02-26 17:33:09 -06:00
|
|
|
{
|
2021-05-07 23:05:16 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ChangeMapping",
|
|
|
|
|
"target_fps.num", target_fps.num,
|
|
|
|
|
"target_fps.den", target_fps.den,
|
|
|
|
|
"target_pulldown", target_pulldown,
|
|
|
|
|
"target_sample_rate", target_sample_rate,
|
|
|
|
|
"target_channels", target_channels,
|
|
|
|
|
"target_channel_layout", target_channel_layout);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2015-02-26 17:33:09 -06:00
|
|
|
// Mark as dirty
|
|
|
|
|
is_dirty = true;
|
|
|
|
|
|
|
|
|
|
// Update mapping details
|
2017-05-26 01:08:20 -05:00
|
|
|
target.num = target_fps.num;
|
|
|
|
|
target.den = target_fps.den;
|
|
|
|
|
info.fps.num = target_fps.num;
|
|
|
|
|
info.fps.den = target_fps.den;
|
|
|
|
|
info.video_timebase.num = target_fps.den;
|
|
|
|
|
info.video_timebase.den = target_fps.num;
|
2022-08-17 22:14:36 -05:00
|
|
|
info.video_length = round(info.duration * info.fps.ToDouble());
|
2015-02-26 17:33:09 -06:00
|
|
|
pulldown = target_pulldown;
|
2015-03-01 22:36:39 -06:00
|
|
|
info.sample_rate = target_sample_rate;
|
|
|
|
|
info.channels = target_channels;
|
|
|
|
|
info.channel_layout = target_channel_layout;
|
2015-03-04 15:26:08 -06:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Enable/Disable audio (based on settings)
|
|
|
|
|
info.has_audio = info.sample_rate > 0 && info.channels > 0;
|
|
|
|
|
|
2015-03-08 22:22:40 -05:00
|
|
|
// Clear cache
|
|
|
|
|
final_cache.Clear();
|
|
|
|
|
|
2016-07-27 13:18:55 -05:00
|
|
|
// Adjust cache size based on size of frame and audio
|
2022-10-10 11:17:53 -05:00
|
|
|
final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS, info.width, info.height, info.sample_rate, info.channels);
|
2016-07-27 13:18:55 -05:00
|
|
|
|
2015-03-08 22:22:40 -05:00
|
|
|
// Deallocate resample buffer
|
|
|
|
|
if (avr) {
|
2018-09-11 00:40:31 -05:00
|
|
|
SWR_CLOSE(avr);
|
|
|
|
|
SWR_FREE(&avr);
|
2015-03-08 22:22:40 -05:00
|
|
|
avr = NULL;
|
|
|
|
|
}
|
2015-02-26 17:33:09 -06:00
|
|
|
}
|
|
|
|
|
|
2015-03-01 22:36:39 -06:00
|
|
|
// Resample audio and map channels (if needed)
|
2017-09-28 16:03:01 -05:00
|
|
|
void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t original_frame_number)
|
2015-03-01 22:36:39 -06:00
|
|
|
{
|
2019-03-06 15:35:03 -06:00
|
|
|
// Check if mappings are dirty (and need to be recalculated)
|
|
|
|
|
if (is_dirty)
|
|
|
|
|
// Recalculate mappings
|
|
|
|
|
Init();
|
|
|
|
|
|
2015-03-01 22:36:39 -06:00
|
|
|
// Init audio buffers / variables
|
|
|
|
|
int total_frame_samples = 0;
|
|
|
|
|
int channels_in_frame = frame->GetAudioChannelsCount();
|
|
|
|
|
int sample_rate_in_frame = frame->SampleRate();
|
|
|
|
|
int samples_in_frame = frame->GetAudioSamplesCount();
|
|
|
|
|
ChannelLayout channel_layout_in_frame = frame->ChannelsLayout();
|
|
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ResampleMappedAudio",
|
|
|
|
|
"frame->number", frame->number,
|
|
|
|
|
"original_frame_number", original_frame_number,
|
|
|
|
|
"channels_in_frame", channels_in_frame,
|
|
|
|
|
"samples_in_frame", samples_in_frame,
|
|
|
|
|
"sample_rate_in_frame", sample_rate_in_frame);
|
2015-03-08 21:42:53 -05:00
|
|
|
|
2015-03-01 22:36:39 -06:00
|
|
|
// Get audio sample array
|
|
|
|
|
float* frame_samples_float = NULL;
|
|
|
|
|
// Get samples interleaved together (c1 c2 c1 c2 c1 c2)
|
|
|
|
|
frame_samples_float = frame->GetInterleavedAudioSamples(sample_rate_in_frame, NULL, &samples_in_frame);
|
|
|
|
|
|
|
|
|
|
// Calculate total samples
|
|
|
|
|
total_frame_samples = samples_in_frame * channels_in_frame;
|
|
|
|
|
|
2015-03-04 15:26:08 -06:00
|
|
|
// Create a new array (to hold all S16 audio samples for the current queued frames)
|
2016-07-27 13:18:55 -05:00
|
|
|
int16_t* frame_samples = (int16_t*) av_malloc(sizeof(int16_t)*total_frame_samples);
|
2015-03-04 15:26:08 -06:00
|
|
|
|
2020-03-26 10:17:22 +02:00
|
|
|
// Translate audio sample values back to 16 bit integers with saturation
|
|
|
|
|
float valF;
|
|
|
|
|
int16_t conv;
|
|
|
|
|
const int16_t max16 = 32767;
|
|
|
|
|
const int16_t min16 = -32768;
|
|
|
|
|
for (int s = 0; s < total_frame_samples; s++) {
|
|
|
|
|
valF = frame_samples_float[s] * (1 << 15);
|
|
|
|
|
if (valF > max16)
|
|
|
|
|
conv = max16;
|
|
|
|
|
else if (valF < min16)
|
|
|
|
|
conv = min16;
|
|
|
|
|
else
|
|
|
|
|
conv = int(valF + 32768.5) - 32768; // +0.5 is for rounding
|
|
|
|
|
|
|
|
|
|
// Copy into buffer
|
|
|
|
|
frame_samples[s] = conv;
|
|
|
|
|
}
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2015-03-09 15:17:56 -05:00
|
|
|
|
2015-03-01 22:36:39 -06:00
|
|
|
// Deallocate float array
|
|
|
|
|
delete[] frame_samples_float;
|
|
|
|
|
frame_samples_float = NULL;
|
|
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ResampleMappedAudio (got sample data from frame)",
|
|
|
|
|
"frame->number", frame->number,
|
|
|
|
|
"total_frame_samples", total_frame_samples,
|
|
|
|
|
"target channels", info.channels,
|
|
|
|
|
"channels_in_frame", channels_in_frame,
|
|
|
|
|
"target sample_rate", info.sample_rate,
|
|
|
|
|
"samples_in_frame", samples_in_frame);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create input frame (and allocate arrays)
|
2015-09-23 00:27:28 -05:00
|
|
|
AVFrame *audio_frame = AV_ALLOCATE_FRAME();
|
|
|
|
|
AV_RESET_FRAME(audio_frame);
|
2015-03-01 22:36:39 -06:00
|
|
|
audio_frame->nb_samples = total_frame_samples / channels_in_frame;
|
|
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
int buf_size = audio_frame->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * channels_in_frame;
|
|
|
|
|
int error_code = avcodec_fill_audio_frame(
|
|
|
|
|
audio_frame, channels_in_frame, AV_SAMPLE_FMT_S16,
|
|
|
|
|
(uint8_t *) frame_samples, buf_size, 1);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2015-10-01 13:00:50 -05:00
|
|
|
if (error_code < 0)
|
2015-03-01 22:36:39 -06:00
|
|
|
{
|
2021-08-11 03:58:45 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ResampleMappedAudio ERROR [" + av_err2string(error_code) + "]",
|
|
|
|
|
"error_code", error_code);
|
2015-03-01 22:36:39 -06:00
|
|
|
throw ErrorEncodingVideo("Error while resampling audio in frame mapper", frame->number);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update total samples & input frame size (due to bigger or smaller data types)
|
2020-10-20 13:03:10 -05:00
|
|
|
total_frame_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame->number), target, info.sample_rate, info.channels);
|
2015-03-08 21:42:53 -05:00
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ResampleMappedAudio (adjust # of samples)",
|
|
|
|
|
"total_frame_samples", total_frame_samples,
|
|
|
|
|
"info.sample_rate", info.sample_rate,
|
|
|
|
|
"sample_rate_in_frame", sample_rate_in_frame,
|
|
|
|
|
"info.channels", info.channels,
|
|
|
|
|
"channels_in_frame", channels_in_frame,
|
|
|
|
|
"original_frame_number", original_frame_number);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
// Create output frame (and allocate arrays)
|
2015-09-23 00:27:28 -05:00
|
|
|
AVFrame *audio_converted = AV_ALLOCATE_FRAME();
|
|
|
|
|
AV_RESET_FRAME(audio_converted);
|
2015-03-08 21:42:53 -05:00
|
|
|
audio_converted->nb_samples = total_frame_samples;
|
|
|
|
|
av_samples_alloc(audio_converted->data, audio_converted->linesize, info.channels, total_frame_samples, AV_SAMPLE_FMT_S16, 0);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ResampleMappedAudio (preparing for resample)",
|
|
|
|
|
"in_sample_fmt", AV_SAMPLE_FMT_S16,
|
|
|
|
|
"out_sample_fmt", AV_SAMPLE_FMT_S16,
|
|
|
|
|
"in_sample_rate", sample_rate_in_frame,
|
|
|
|
|
"out_sample_rate", info.sample_rate,
|
|
|
|
|
"in_channels", channels_in_frame,
|
|
|
|
|
"out_channels", info.channels);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
int nb_samples = 0;
|
|
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// setup resample context
|
|
|
|
|
if (!avr) {
|
|
|
|
|
avr = SWR_ALLOC();
|
|
|
|
|
av_opt_set_int(avr, "in_channel_layout", channel_layout_in_frame, 0);
|
|
|
|
|
av_opt_set_int(avr, "out_channel_layout", info.channel_layout, 0);
|
|
|
|
|
av_opt_set_int(avr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
|
|
|
|
|
av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
|
|
|
|
|
av_opt_set_int(avr, "in_sample_rate", sample_rate_in_frame, 0);
|
|
|
|
|
av_opt_set_int(avr, "out_sample_rate", info.sample_rate, 0);
|
|
|
|
|
av_opt_set_int(avr, "in_channels", channels_in_frame, 0);
|
|
|
|
|
av_opt_set_int(avr, "out_channels", info.channels, 0);
|
|
|
|
|
SWR_INIT(avr);
|
|
|
|
|
}
|
2017-06-22 15:26:40 -05:00
|
|
|
|
Improved Profile Class (Helper methods, Sortable, Unit tests) (#895)
* Removing legacy profile property. Add new operators for Profile classes (for comparison). Also added new functions to generate different variations of the Profile data (key, short name, long name, long name w/description).
* Add empty constructor for Profile class, and new Profile unit tets
* Adding zero padding to profile Key function, for easier sorting: 01920x1080i2997_16:09
* Clear setfill flag after creating Key() output
* Updating example exe to load an *.osp project file via C++, which makes debugging complex broken projects much easier.
* - Add new unit test to FFmpegWriter to create an animated GIF and verify it can be wrapped with a FrameMapper (with no audio track)
- Improve FrameMapper to ignore missing audio data (i.e. when no audio samples present, don't try and find them or resample them)
* Fix some whitespace issues
* Fix inline documentation mistype
* Fixed missing reuse licensing on new example profile files
* Changing Profile::Key() format to exclude the : character, since Windows file names cannot contain that
* - Large memory leak fixed in FFmpegWriter when closing the video & audio contexts
- Reducing # of cached frames and rescalers to 1, since we no longer use OMP and this is unneeded - we need to refactor much of this code out eventually
* - Fixing whitespace issues
- Code clean-up / line wrapping / etc...
2023-02-02 16:29:38 -06:00
|
|
|
// Convert audio samples
|
|
|
|
|
nb_samples = SWR_CONVERT(avr, // audio resample context
|
|
|
|
|
audio_converted->data, // output data pointers
|
|
|
|
|
audio_converted->linesize[0], // output plane size, in bytes. (0 if unknown)
|
|
|
|
|
audio_converted->nb_samples, // maximum number of samples that the output buffer can hold
|
|
|
|
|
audio_frame->data, // input data pointers
|
|
|
|
|
audio_frame->linesize[0], // input plane size, in bytes (0 if unknown)
|
|
|
|
|
audio_frame->nb_samples); // number of input samples to convert
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2015-03-04 15:26:08 -06:00
|
|
|
// Create a new array (to hold all resampled S16 audio samples)
|
2016-01-30 17:12:41 -06:00
|
|
|
int16_t* resampled_samples = new int16_t[(nb_samples * info.channels)];
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
// Copy audio samples over original samples
|
2015-06-01 00:20:14 -07:00
|
|
|
memcpy(resampled_samples, audio_converted->data[0], (nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * info.channels));
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
// Free frames
|
2016-07-27 13:18:55 -05:00
|
|
|
av_freep(&audio_frame->data[0]);
|
2015-09-23 00:27:28 -05:00
|
|
|
AV_FREE_FRAME(&audio_frame);
|
2016-07-27 13:18:55 -05:00
|
|
|
av_freep(&audio_converted->data[0]);
|
2015-09-23 00:27:28 -05:00
|
|
|
AV_FREE_FRAME(&audio_converted);
|
2015-03-04 15:26:08 -06:00
|
|
|
frame_samples = NULL;
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
// Resize the frame to hold the right # of channels and samples
|
|
|
|
|
int channel_buffer_size = nb_samples;
|
|
|
|
|
frame->ResizeAudio(info.channels, channel_buffer_size, info.sample_rate, info.channel_layout);
|
|
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ResampleMappedAudio (Audio successfully resampled)",
|
|
|
|
|
"nb_samples", nb_samples,
|
|
|
|
|
"total_frame_samples", total_frame_samples,
|
|
|
|
|
"info.sample_rate", info.sample_rate,
|
|
|
|
|
"channels_in_frame", channels_in_frame,
|
|
|
|
|
"info.channels", info.channels,
|
|
|
|
|
"info.channel_layout", info.channel_layout);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
// Array of floats (to hold samples for each channel)
|
|
|
|
|
float *channel_buffer = new float[channel_buffer_size];
|
|
|
|
|
|
|
|
|
|
// Divide audio into channels. Loop through each channel
|
|
|
|
|
for (int channel_filter = 0; channel_filter < info.channels; channel_filter++)
|
|
|
|
|
{
|
|
|
|
|
// Init array
|
|
|
|
|
for (int z = 0; z < channel_buffer_size; z++)
|
|
|
|
|
channel_buffer[z] = 0.0f;
|
|
|
|
|
|
|
|
|
|
// Loop through all samples and add them to our Frame based on channel.
|
|
|
|
|
// Toggle through each channel number, since channel data is stored like (left right left right)
|
|
|
|
|
int channel = 0;
|
|
|
|
|
int position = 0;
|
|
|
|
|
for (int sample = 0; sample < (nb_samples * info.channels); sample++)
|
|
|
|
|
{
|
|
|
|
|
// Only add samples for current channel
|
|
|
|
|
if (channel_filter == channel)
|
|
|
|
|
{
|
|
|
|
|
// Add sample (convert from (-32768 to 32768) to (-1.0 to 1.0))
|
2015-03-04 15:26:08 -06:00
|
|
|
channel_buffer[position] = resampled_samples[sample] * (1.0f / (1 << 15));
|
2015-03-01 22:36:39 -06:00
|
|
|
|
|
|
|
|
// Increment audio position
|
|
|
|
|
position++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// increment channel (if needed)
|
|
|
|
|
if ((channel + 1) < info.channels)
|
|
|
|
|
// move to next channel
|
|
|
|
|
channel ++;
|
|
|
|
|
else
|
|
|
|
|
// reset channel
|
|
|
|
|
channel = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add samples to frame for this channel
|
2015-03-08 21:42:53 -05:00
|
|
|
frame->AddAudio(true, channel_filter, 0, channel_buffer, position, 1.0f);
|
2015-03-01 22:36:39 -06:00
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
ZmqLogger::Instance()->AppendDebugMethod(
|
|
|
|
|
"FrameMapper::ResampleMappedAudio (Add audio to channel)",
|
|
|
|
|
"number of samples", position,
|
|
|
|
|
"channel_filter", channel_filter);
|
2015-03-01 22:36:39 -06:00
|
|
|
}
|
|
|
|
|
|
2015-06-01 00:20:14 -07:00
|
|
|
// Update frame's audio meta data
|
|
|
|
|
frame->SampleRate(info.sample_rate);
|
|
|
|
|
frame->ChannelsLayout(info.channel_layout);
|
|
|
|
|
|
2015-03-01 22:36:39 -06:00
|
|
|
// clear channel buffer
|
|
|
|
|
delete[] channel_buffer;
|
|
|
|
|
channel_buffer = NULL;
|
|
|
|
|
|
|
|
|
|
// Delete arrays
|
2015-03-04 15:26:08 -06:00
|
|
|
delete[] resampled_samples;
|
|
|
|
|
resampled_samples = NULL;
|
2015-03-01 22:36:39 -06:00
|
|
|
}
|
2020-10-10 17:01:24 -03:00
|
|
|
|
2020-10-13 17:08:27 -05:00
|
|
|
// Adjust frame number for Clip position and start (which can result in a different number)
|
2020-10-20 13:03:10 -05:00
|
|
|
int64_t FrameMapper::AdjustFrameNumber(int64_t clip_frame_number) {
|
2020-10-10 17:01:24 -03:00
|
|
|
|
2020-10-20 13:03:10 -05:00
|
|
|
// Get clip position from parent clip (if any)
|
|
|
|
|
float position = 0.0;
|
|
|
|
|
float start = 0.0;
|
|
|
|
|
Clip *parent = (Clip *) ParentClip();
|
|
|
|
|
if (parent) {
|
|
|
|
|
position = parent->Position();
|
|
|
|
|
start = parent->Start();
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 23:05:16 -04:00
|
|
|
// Adjust start frame and position based on parent clip.
|
|
|
|
|
// This ensures the same frame # is used by mapped readers and clips,
|
|
|
|
|
// when calculating samples per frame.
|
|
|
|
|
// Thus, this prevents gaps and mismatches in # of samples.
|
2020-10-10 17:01:24 -03:00
|
|
|
int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1;
|
|
|
|
|
int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1;
|
|
|
|
|
int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
|
|
|
|
|
|
|
|
|
|
return frame_number;
|
2020-10-19 16:28:49 -04:00
|
|
|
}
|