You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08: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...
This commit is contained in:
@@ -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)];
|
||||
|
||||
Reference in New Issue
Block a user