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
*/
/* LICENSE
2013-09-12 17:52:10 -05:00
*
2019-06-11 06:48:32 -04:00
* Copyright ( c ) 2008 - 2019 OpenShot Studios , LLC
2014-03-29 18:49:22 -05:00
* < http : //www.openshotstudios.com/>. This file is part of
* OpenShot Library ( libopenshot ) , an open - source project dedicated to
* delivering high quality video editing and animation solutions to the
* world . For more information visit < http : //www.openshot.org/>.
2013-09-12 17:52:10 -05:00
*
2014-03-29 18:49:22 -05:00
* OpenShot Library ( libopenshot ) is free software : you can redistribute it
2014-07-11 16:52:14 -05:00
* and / or modify it under the terms of the GNU Lesser General Public License
2014-03-29 18:49:22 -05:00
* as published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
2013-09-12 17:52:10 -05:00
*
2014-03-29 18:49:22 -05:00
* OpenShot Library ( libopenshot ) is distributed in the hope that it will be
* useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2014-07-11 16:52:14 -05:00
* GNU Lesser General Public License for more details .
2013-09-12 17:52:10 -05:00
*
2014-07-11 16:52:14 -05:00
* You should have received a copy of the GNU Lesser General Public License
2014-03-29 18:49:22 -05:00
* along with OpenShot Library . If not , see < http : //www.gnu.org/licenses/>.
2011-10-11 08:44:27 -05:00
*/
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"
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 ) :
2021-04-08 22:34:48 -05:00
reader ( reader ) , target ( target ) , pulldown ( target_pulldown ) , is_dirty ( true ) , avr ( NULL ) , parent_position ( 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
// 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
final_cache . SetMaxBytesFromInfo ( OPEN_MP_NUM_PROCESSORS * 2 , 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
{
if ( reader )
return reader ;
else
// Throw error if reader not initialized
2019-08-27 15:47:39 -04:00
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 ) ;
}
// 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 ;
2011-10-11 08:44:27 -05:00
// Clear the fields & frames lists
fields . clear ( ) ;
frames . clear ( ) ;
2021-04-08 22:34:48 -05:00
// 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 ;
}
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
double value_increment = ( reader - > info . video_length + 1 ) / ( 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 ( ) ;
}
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
2019-07-03 14:14:02 -04: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
2019-07-03 14:14:02 -04: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
2019-07-03 14:14:02 -04: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
const GenericScopedLock < CriticalSection > lock ( getFrameCriticalSection ) ;
2021-04-08 22:34:48 -05: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 ;
}
}
// 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
2019-07-03 14:14:02 -04: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
2019-07-03 14:14:02 -04: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
}
// Resample audio on frame (if needed)
bool need_resampling = false ;
if ( info . has_audio & &
( 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 ,
reader - > info . sample_rate , reader - > info . channels ) ;
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 ,
2017-01-06 20:48:47 -05: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
}
2011-10-11 08:44:27 -05:00
void FrameMapper : : PrintMapping ( )
{
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 ] ;
2015-03-01 22:36:39 -06:00
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 ;
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
const GenericScopedLock < CriticalSection > lock ( getFrameCriticalSection ) ;
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 ( ) ;
2019-05-30 09:40:18 -07:00
// Clear the fields & frames lists
fields . clear ( ) ;
frames . clear ( ) ;
// Mark as dirty
is_dirty = true ;
// Clear cache
final_cache . Clear ( ) ;
2015-03-01 22:36:39 -06:00
// Deallocate resample buffer
if ( avr ) {
2018-09-11 00:40:31 -05:00
SWR_CLOSE ( avr ) ;
SWR_FREE ( & avr ) ;
2015-03-01 22:36:39 -06:00
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 " ;
// 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 ) ;
}
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
{
2017-05-24 03:20:26 -05: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 ;
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
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
final_cache . SetMaxBytesFromInfo ( OPEN_MP_NUM_PROCESSORS * 2 , info . width , info . height , info . sample_rate , info . channels ) ;
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 ( ) ;
2019-07-03 14:14:02 -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 ;
2016-04-21 01:39:17 -05: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 ;
int error_code = avcodec_fill_audio_frame ( audio_frame , channels_in_frame , AV_SAMPLE_FMT_S16 , ( uint8_t * ) frame_samples ,
audio_frame - > nb_samples * av_get_bytes_per_sample ( AV_SAMPLE_FMT_S16 ) * channels_in_frame , 1 ) ;
2015-10-01 13:00:50 -05:00
if ( error_code < 0 )
2015-03-01 22:36:39 -06:00
{
2019-08-04 22:54:06 -04:00
ZmqLogger : : Instance ( ) - > AppendDebugMethod ( " FrameMapper::ResampleMappedAudio ERROR [ " + ( std : : string ) av_err2str ( 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
2016-04-21 01:39:17 -05: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
2016-04-21 01:39:17 -05: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 ;
2017-06-22 15:26:40 -05:00
// setup resample context
if ( ! avr ) {
2018-09-11 00:40:31 -05:00
avr = SWR_ALLOC ( ) ;
2017-06-22 15:26:40 -05:00
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 ) ;
2018-09-11 00:40:31 -05:00
SWR_INIT ( avr ) ;
2017-06-22 15:26:40 -05:00
}
// Convert audio samples
2018-09-11 00:40:31 -05:00
nb_samples = SWR_CONVERT ( avr , // audio resample context
2017-06-22 15:26:40 -05:00
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 ) ;
2016-04-21 01:39:17 -05: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
2019-07-03 14:14:02 -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 ( ) ;
}
// Adjust start frame and position based on parent clip. This prevents 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
}