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...
This commit is contained in:
Jonathan Thomas
2023-02-02 16:29:38 -06:00
committed by GitHub
parent 510a7690f6
commit 70e86ef044
11 changed files with 1064 additions and 647 deletions

View File

@@ -41,6 +41,9 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe
info.width = reader->info.width;
info.height = reader->info.height;
// Enable/Disable audio (based on settings)
info.has_audio = info.sample_rate > 0 && info.channels > 0;
// Used to toggle odd / even fields
field_toggle = true;
@@ -60,11 +63,11 @@ FrameMapper::~FrameMapper() {
/// Get the current reader
ReaderBase* FrameMapper::Reader()
{
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.");
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.");
}
void FrameMapper::AddField(int64_t frame)
@@ -84,14 +87,14 @@ void FrameMapper::AddField(Field field)
// Clear both the fields & frames lists
void FrameMapper::Clear() {
// Prevent async calls to the following code
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
// Prevent async calls to the following code
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
// Clear the fields & frames lists
fields.clear();
fields.shrink_to_fit();
frames.clear();
frames.shrink_to_fit();
// Clear the fields & frames lists
fields.clear();
fields.shrink_to_fit();
frames.clear();
frames.shrink_to_fit();
}
// Use the original and target frame rates and a pull-down technique to create
@@ -114,14 +117,14 @@ void FrameMapper::Init()
Clear();
// Find parent position (if any)
Clip *parent = (Clip *) ParentClip();
if (parent) {
parent_position = parent->Position();
parent_start = parent->Start();
} else {
parent_position = 0.0;
parent_start = 0.0;
}
Clip *parent = (Clip *) ParentClip();
if (parent) {
parent_position = parent->Position();
parent_start = parent->Start();
} else {
parent_position = 0.0;
parent_start = 0.0;
}
// Mark as not dirty
is_dirty = false;
@@ -419,19 +422,19 @@ std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)
// Create a scoped lock, allowing only a single thread to run the following code at one time
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
// 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;
}
}
// 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;
}
}
// Check if mappings are dirty (and need to be recalculated)
// Check if mappings are dirty (and need to be recalculated)
if (is_dirty)
Init();
@@ -513,9 +516,12 @@ std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)
std::make_shared<QImage>(*even_frame->GetImage()), false);
}
// Determine if reader contains audio samples
bool reader_has_audio = frame->SampleRate() > 0 && frame->GetAudioChannelsCount() > 0;
// Resample audio on frame (if needed)
bool need_resampling = false;
if (info.has_audio &&
if ((info.has_audio && reader_has_audio) &&
(info.sample_rate != frame->SampleRate() ||
info.channels != frame->GetAudioChannelsCount() ||
info.channel_layout != frame->ChannelsLayout()))
@@ -537,7 +543,7 @@ std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)
copy_samples.sample_end += EXTRA_INPUT_SAMPLES;
int samples_per_end_frame =
Frame::GetSamplesPerFrame(copy_samples.frame_end, original,
reader->info.sample_rate, reader->info.channels);
reader->info.sample_rate, reader->info.channels);
if (copy_samples.sample_end >= samples_per_end_frame)
{
// check for wrapping
@@ -553,7 +559,7 @@ std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)
copy_samples.sample_start += EXTRA_INPUT_SAMPLES;
int samples_per_start_frame =
Frame::GetSamplesPerFrame(copy_samples.frame_start, original,
reader->info.sample_rate, reader->info.channels);
reader->info.sample_rate, reader->info.channels);
if (copy_samples.sample_start >= samples_per_start_frame)
{
// check for wrapping
@@ -643,15 +649,15 @@ void FrameMapper::PrintMapping(std::ostream* out)
{
MappedFrame frame = frames[map - 1];
*out << "Target frame #: " << map
<< " mapped to original frame #:\t("
<< frame.Odd.Frame << " odd, "
<< frame.Even.Frame << " even)" << std::endl;
<< " mapped to original frame #:\t("
<< frame.Odd.Frame << " odd, "
<< frame.Even.Frame << " even)" << std::endl;
*out << " - Audio samples mapped to frame "
<< frame.Samples.frame_start << ":"
<< frame.Samples.sample_start << " to frame "
<< frame.Samples.frame_end << ":"
<< frame.Samples.sample_end << endl;
<< frame.Samples.frame_start << ":"
<< frame.Samples.sample_start << " to frame "
<< frame.Samples.frame_end << ":"
<< frame.Samples.sample_end << endl;
}
}
@@ -690,21 +696,21 @@ void FrameMapper::Close()
reader->Close();
}
// Clear the fields & frames lists
Clear();
// Clear the fields & frames lists
Clear();
// Mark as dirty
is_dirty = true;
// Mark as dirty
is_dirty = true;
// Clear cache
final_cache.Clear();
// Clear cache
final_cache.Clear();
// Deallocate resample buffer
if (avr) {
SWR_CLOSE(avr);
SWR_FREE(&avr);
avr = NULL;
}
// Deallocate resample buffer
if (avr) {
SWR_CLOSE(avr);
SWR_FREE(&avr);
avr = NULL;
}
}
@@ -785,6 +791,9 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow
info.channels = target_channels;
info.channel_layout = target_channel_layout;
// Enable/Disable audio (based on settings)
info.has_audio = info.sample_rate > 0 && info.channels > 0;
// Clear cache
final_cache.Clear();
@@ -913,28 +922,28 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
int nb_samples = 0;
// 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);
}
// 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);
}
// 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
// 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
// Create a new array (to hold all resampled S16 audio samples)
int16_t* resampled_samples = new int16_t[(nb_samples * info.channels)];