You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Refactor of FrameMapper, and added in audio sample remapping..
This commit is contained in:
@@ -9,18 +9,14 @@
|
||||
using namespace std;
|
||||
using namespace openshot;
|
||||
|
||||
FrameMapper::FrameMapper(FileReaderBase *reader, Framerate target, Pulldown_Method pulldown)
|
||||
FrameMapper::FrameMapper(FileReaderBase *reader, Framerate target, Pulldown_Method pulldown) :
|
||||
reader(reader), target(target), pulldown(pulldown)
|
||||
{
|
||||
// init position
|
||||
m_target = target;
|
||||
m_pulldown = pulldown;
|
||||
m_reader = reader;
|
||||
|
||||
// Set the original frame rate from the reader
|
||||
m_original = Framerate(m_reader->info.fps.num, m_reader->info.fps.den);
|
||||
original = Framerate(reader->info.fps.num, reader->info.fps.den);
|
||||
|
||||
// Set length from reader
|
||||
m_length = m_reader->info.video_length;
|
||||
m_length = reader->info.video_length;
|
||||
|
||||
// Used to toggle odd / even fields
|
||||
field_toggle = true;
|
||||
@@ -32,8 +28,7 @@ FrameMapper::FrameMapper(FileReaderBase *reader, Framerate target, Pulldown_Meth
|
||||
void FrameMapper::AddField(int frame)
|
||||
{
|
||||
// Add a field, and toggle the odd / even field
|
||||
Field f(frame, field_toggle);
|
||||
AddField(f);
|
||||
AddField(Field(frame, field_toggle));
|
||||
}
|
||||
|
||||
void FrameMapper::AddField(Field field)
|
||||
@@ -52,7 +47,7 @@ void FrameMapper::AddField(Field field)
|
||||
void FrameMapper::Init()
|
||||
{
|
||||
// Get the difference (in frames) between the original and target frame rates
|
||||
float difference = m_target.GetRoundedFPS() - m_original.GetRoundedFPS();
|
||||
float difference = target.GetRoundedFPS() - original.GetRoundedFPS();
|
||||
|
||||
// Find the number (i.e. interval) of fields that need to be skipped or repeated
|
||||
int field_interval = 0;
|
||||
@@ -60,7 +55,7 @@ void FrameMapper::Init()
|
||||
|
||||
if (difference != 0)
|
||||
{
|
||||
field_interval = round(fabs(m_original.GetRoundedFPS() / difference));
|
||||
field_interval = round(fabs(original.GetRoundedFPS() / difference));
|
||||
|
||||
// Get frame interval (2 fields per frame)
|
||||
frame_interval = field_interval * 2.0f;
|
||||
@@ -86,12 +81,12 @@ void FrameMapper::Init()
|
||||
// Add current field
|
||||
AddField(frame);
|
||||
|
||||
if (m_pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
|
||||
if (pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
|
||||
{
|
||||
// Add extra field for each 'field interval
|
||||
AddField(frame);
|
||||
}
|
||||
else if (m_pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
|
||||
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
|
||||
@@ -100,7 +95,7 @@ void FrameMapper::Init()
|
||||
// add field for next frame (if the next frame exists)
|
||||
AddField(Field(frame + 1, field_toggle));
|
||||
}
|
||||
else if (m_pulldown == PULLDOWN_NONE && field % frame_interval == 0)
|
||||
else if (pulldown == PULLDOWN_NONE && field % frame_interval == 0)
|
||||
{
|
||||
// No pull-down technique needed, just repeat this frame
|
||||
AddField(frame);
|
||||
@@ -110,17 +105,17 @@ void FrameMapper::Init()
|
||||
else if (difference < 0) // Need to SKIP fake fields & frames, because we want to return to the original film frame rate
|
||||
{
|
||||
|
||||
if (m_pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
|
||||
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 (m_pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
|
||||
else if (pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
|
||||
{
|
||||
// skip this field, plus the next field
|
||||
field++;
|
||||
}
|
||||
else if (m_pulldown == PULLDOWN_NONE && frame % field_interval == 0)
|
||||
else if (pulldown == PULLDOWN_NONE && frame % field_interval == 0)
|
||||
{
|
||||
// skip this field, plus the next one
|
||||
field++;
|
||||
@@ -141,6 +136,10 @@ void FrameMapper::Init()
|
||||
Field Odd(0, true); // temp field used to track the ODD field
|
||||
Field Even(0, true); // temp field used to track the EVEN field
|
||||
|
||||
// Variables used to remap audio samples
|
||||
int start_samples_frame = 1;
|
||||
int start_samples_position = 0;
|
||||
|
||||
for (unsigned int field = 1; field <= fields.size(); field++)
|
||||
{
|
||||
// Get the current field
|
||||
@@ -149,14 +148,54 @@ void FrameMapper::Init()
|
||||
// Is field divisible by 2?
|
||||
if (field % 2 == 0 && field > 0)
|
||||
{
|
||||
// New frame number
|
||||
int frame_number = field / 2;
|
||||
|
||||
// Set the bottom frame
|
||||
if (f.isOdd)
|
||||
Odd = f;
|
||||
else
|
||||
Even = f;
|
||||
|
||||
// Determine the range of samples (from the original rate, to the new rate)
|
||||
int end_samples_frame = start_samples_frame;
|
||||
int end_samples_position = start_samples_position;
|
||||
int remaining_samples = GetSamplesPerFrame(frame_number, target.GetFraction());
|
||||
|
||||
while (remaining_samples > 0)
|
||||
{
|
||||
// get original samples
|
||||
int original_samples = GetSamplesPerFrame(end_samples_frame, original.GetFraction()) - end_samples_position;
|
||||
|
||||
// Enough samples
|
||||
if (original_samples >= remaining_samples)
|
||||
{
|
||||
// Take all that we need, and break loop
|
||||
end_samples_position = remaining_samples;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Create the sample mapping struct
|
||||
SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position};
|
||||
|
||||
// Reset the audio variables
|
||||
start_samples_frame = end_samples_frame;
|
||||
start_samples_position = end_samples_position + 1;
|
||||
if (start_samples_position >= GetSamplesPerFrame(start_samples_frame, original.GetFraction()))
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
// Create a frame and ADD it to the frames collection
|
||||
MappedFrame frame = {Odd, Even};
|
||||
MappedFrame frame = {Odd, Even, Samples};
|
||||
frames.push_back(frame);
|
||||
}
|
||||
else
|
||||
@@ -220,10 +259,24 @@ void FrameMapper::MapTime(Keyframe new_time) throw(OutOfBoundsFrame)
|
||||
|
||||
}
|
||||
|
||||
// Calculate the # of samples per video frame (for a specific frame number)
|
||||
int FrameMapper::GetSamplesPerFrame(int frame_number, Fraction rate)
|
||||
{
|
||||
// Get the total # of samples for the previous frame, and the current frame (rounded)
|
||||
double fps = rate.Reciprocal().ToDouble();
|
||||
double previous_samples = round((reader->info.sample_rate * fps) * (frame_number - 1));
|
||||
double total_samples = round((reader->info.sample_rate * fps) * frame_number);
|
||||
|
||||
// Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can
|
||||
// be evenly divided into frames, so each frame can have have different # of samples.
|
||||
double samples_per_frame = total_samples - previous_samples;
|
||||
return samples_per_frame;
|
||||
}
|
||||
|
||||
void FrameMapper::PrintMapping()
|
||||
{
|
||||
// Get the difference (in frames) between the original and target frame rates
|
||||
float difference = m_target.GetRoundedFPS() - m_original.GetRoundedFPS();
|
||||
float difference = target.GetRoundedFPS() - original.GetRoundedFPS();
|
||||
|
||||
int field_interval = 0;
|
||||
int frame_interval = 0;
|
||||
@@ -231,14 +284,14 @@ void FrameMapper::PrintMapping()
|
||||
if (difference != 0)
|
||||
{
|
||||
// Find the number (i.e. interval) of fields that need to be skipped or repeated
|
||||
field_interval = round(fabs(m_original.GetRoundedFPS() / difference));
|
||||
field_interval = round(fabs(original.GetRoundedFPS() / difference));
|
||||
|
||||
// Get frame interval (2 fields per frame)
|
||||
frame_interval = field_interval * 2.0f;
|
||||
}
|
||||
|
||||
// print header
|
||||
cout << "Convert " << m_original.GetRoundedFPS() << " fps to " << m_target.GetRoundedFPS() << " fps" << endl;
|
||||
cout << "Convert " << original.GetRoundedFPS() << " fps to " << target.GetRoundedFPS() << " fps" << endl;
|
||||
cout << "Difference of " << difference << " frames per second" << endl;
|
||||
cout << "Field Interval: " << field_interval << "th field" << endl;
|
||||
cout << "Frame Interval: " << frame_interval << "th field" << endl << endl;
|
||||
@@ -248,7 +301,7 @@ void FrameMapper::PrintMapping()
|
||||
{
|
||||
MappedFrame frame = frames[map - 1];
|
||||
cout << "Target frame #: " << map << " mapped to original frame #:\t(" << frame.Odd.Frame << " odd, " << frame.Even.Frame << " even)" << endl;
|
||||
|
||||
cout << " - Audio samples mapped to frame " << frame.Samples.frame_start << ":" << frame.Samples.sample_start << " to frame " << frame.Samples.frame_end << ":" << frame.Samples.sample_end << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user