28 reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL), parent_position(0.0), parent_start(0.0), previous_frame(0)
73 throw ReaderClosed(
"No Reader has been initialized for FrameMapper. Call Reader(*reader) before calling this method.");
76void FrameMapper::AddField(int64_t frame)
79 Field f = { frame, bool(field_toggle) };
83void FrameMapper::AddField(int64_t frame,
bool isOdd)
86 Field f = { frame, isOdd };
90void FrameMapper::AddField(
Field field)
96 field_toggle = (field_toggle ? false :
true);
100void FrameMapper::Clear() {
102 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
115void FrameMapper::Init()
125 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
133 parent_position = parent->
Position();
134 parent_start = parent->
Start();
136 parent_position = 0.0;
148 if ((fabs(original.
ToFloat() - 24.0) < 1e-7 || fabs(original.
ToFloat() - 25.0) < 1e-7 || fabs(original.
ToFloat() - 30.0) < 1e-7) &&
149 (fabs(target.
ToFloat() - 24.0) < 1e-7 || fabs(target.
ToFloat() - 25.0) < 1e-7 || fabs(target.
ToFloat() - 30.0) < 1e-7)) {
152 float difference = target.
ToInt() - original.
ToInt();
155 int field_interval = 0;
156 int frame_interval = 0;
160 field_interval = round(fabs(original.
ToInt() / difference));
163 frame_interval = field_interval * 2.0f;
172 for (int64_t field = 1; field <= number_of_fields; field++)
180 else if (difference > 0)
190 else if (pulldown ==
PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
197 AddField(frame + 1, field_toggle);
199 else if (pulldown ==
PULLDOWN_NONE && field % frame_interval == 0)
206 else if (difference < 0)
212 field_toggle = (field_toggle ? false :
true);
214 else if (pulldown ==
PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
219 else if (pulldown ==
PULLDOWN_NONE && frame % field_interval == 0)
232 if (field % 2 == 0 && field > 0)
245 double original_frame_num = 1.0f;
246 for (int64_t frame_num = 1; frame_num <= new_length; frame_num++)
249 AddField(round(original_frame_num));
250 AddField(round(original_frame_num));
253 original_frame_num += value_increment;
258 Field Odd = {0,
true};
259 Field Even = {0,
true};
262 int64_t start_samples_frame = 1;
263 int start_samples_position = 0;
265 for (std::vector<Field>::size_type field = 1; field <=
fields.size(); field++)
271 if (field % 2 == 0 && field > 0)
274 int64_t frame_number = field / 2;
285 int64_t end_samples_frame = start_samples_frame;
286 int end_samples_position = start_samples_position;
289 while (remaining_samples > 0)
296 if (original_samples >= remaining_samples)
299 end_samples_position += remaining_samples - 1;
300 remaining_samples = 0;
304 end_samples_frame += 1;
305 end_samples_position = 0;
306 remaining_samples -= original_samples;
316 start_samples_frame = end_samples_frame;
317 start_samples_position = end_samples_position + 1;
320 start_samples_frame += 1;
321 start_samples_position = 0;
362 frame.
Odd.
Frame = TargetFrameNumber;
372 if(TargetFrameNumber < 1 ||
frames.size() == 0)
376 else if (TargetFrameNumber > (int64_t)
frames.size())
378 TargetFrameNumber =
frames.size();
382 "FrameMapper::GetMappedFrame",
383 "TargetFrameNumber", TargetFrameNumber,
384 "frames.size()",
frames.size(),
385 "frames[...].Odd",
frames[TargetFrameNumber - 1].Odd.Frame,
386 "frames[...].Even",
frames[TargetFrameNumber - 1].Even.Frame);
389 return frames[TargetFrameNumber - 1];
393std::shared_ptr<Frame> FrameMapper::GetOrCreateFrame(int64_t number)
395 std::shared_ptr<Frame> new_frame;
403 "FrameMapper::GetOrCreateFrame (from reader)",
405 "samples_in_frame", samples_in_frame);
408 new_frame = reader->
GetFrame(number);
421 "FrameMapper::GetOrCreateFrame (create blank)",
423 "samples_in_frame", samples_in_frame);
429 new_frame->AddAudioSilence(samples_in_frame);
437 std::shared_ptr<Frame> final_frame = final_cache.
GetFrame(requested_frame);
438 if (final_frame)
return final_frame;
441 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
445 bool is_increasing =
true;
446 bool direction_flipped =
false;
449 const std::lock_guard<std::recursive_mutex> lock(directionMutex);
453 is_increasing = hint_increasing;
455 }
else if (previous_frame > 0 && std::llabs(requested_frame - previous_frame) == 1) {
457 is_increasing = (requested_frame > previous_frame);
458 }
else if (last_dir_initialized) {
460 is_increasing = last_is_increasing;
462 is_increasing =
true;
466 if (!last_dir_initialized) {
467 last_is_increasing = is_increasing;
468 last_dir_initialized =
true;
469 }
else if (last_is_increasing != is_increasing) {
470 direction_flipped =
true;
471 last_is_increasing = is_increasing;
475 float position = parent->
Position();
476 float start = parent->
Start();
477 if (parent_position != position || parent_start != start) {
489 final_frame = final_cache.
GetFrame(requested_frame);
490 if (final_frame)
return final_frame;
494 int minimum_frames = 1;
498 "FrameMapper::GetFrame (Loop through frames)",
499 "requested_frame", requested_frame,
500 "minimum_frames", minimum_frames);
503 for (int64_t frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
507 "FrameMapper::GetFrame (inside omp for loop)",
508 "frame_number", frame_number,
509 "minimum_frames", minimum_frames,
510 "requested_frame", requested_frame);
514 std::shared_ptr<Frame> mapped_frame = GetOrCreateFrame(mapped.
Odd.
Frame);
517 int channels_in_frame = mapped_frame->GetAudioChannelsCount();
518 int samples_in_frame =
Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, mapped_frame->SampleRate(), channels_in_frame);
527 info.
channels == mapped_frame->GetAudioChannelsCount() &&
529 mapped.
Samples.
total == mapped_frame->GetAudioSamplesCount() &&
530 mapped.
Samples.
total == samples_in_frame && is_increasing &&
533 mapped_frame->number == frame_number &&
537 final_cache.
Add(mapped_frame);
542 auto frame = std::make_shared<Frame>(
543 frame_number, 1, 1,
"#000000", samples_in_frame, channels_in_frame);
544 frame->SampleRate(mapped_frame->SampleRate());
545 frame->ChannelsLayout(mapped_frame->ChannelsLayout());
549 std::shared_ptr<Frame> odd_frame = mapped_frame;
551 if (odd_frame && odd_frame->has_image_data)
552 frame->AddImage(std::make_shared<QImage>(*odd_frame->GetImage()),
true);
555 std::shared_ptr<Frame> even_frame;
556 even_frame = GetOrCreateFrame(mapped.
Even.
Frame);
557 if (even_frame && even_frame->has_image_data)
558 frame->AddImage(std::make_shared<QImage>(*even_frame->GetImage()),
false);
565 mapped_frame->SampleRate() > 0 &&
566 mapped_frame->GetAudioChannelsCount() > 0;
569 bool need_resampling =
false;
575 need_resampling =
true;
583 if (direction_flipped || (previous_frame > 0 && std::llabs(requested_frame - previous_frame) > 1)) {
594 const int EXTRA_INPUT_SAMPLES = 64;
608 if (reader_has_audio && samples_in_frame > 0)
609 frame->AddAudioSilence(samples_in_frame);
612 int samples_copied = 0;
614 while (reader_has_audio && samples_copied < copy_samples.
total)
617 int remaining_samples = copy_samples.
total - samples_copied;
618 int number_to_copy = 0;
621 std::shared_ptr<Frame> original_frame = mapped_frame;
622 if (starting_frame != original_frame->number) {
623 original_frame = GetOrCreateFrame(starting_frame);
626 int original_samples = original_frame->GetAudioSamplesCount();
627 if (original_samples <= 0) {
628 if (starting_frame >= copy_samples.
frame_end)
635 number_to_copy = original_samples - copy_samples.
sample_start;
637 number_to_copy = original_samples;
641 if (number_to_copy <= 0) {
642 if (starting_frame >= copy_samples.
frame_end)
647 if (number_to_copy > remaining_samples)
648 number_to_copy = remaining_samples;
651 for (
int channel = 0; channel < channels_in_frame; channel++)
656 frame->AddAudio(
true, channel, samples_copied, original_frame->GetAudioSamples(channel) + copy_samples.
sample_start, number_to_copy, 1.0);
661 frame->AddAudio(
true, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
666 frame->AddAudio(
false, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
671 samples_copied += number_to_copy;
676 frame->SetAudioDirection(is_increasing);
684 final_cache.
Add(frame);
689 return final_cache.
GetFrame(requested_frame);
700 for (
float map = 1; map <=
frames.size(); map++)
703 *out <<
"Target frame #: " << map
704 <<
" mapped to original frame #:\t("
706 << frame.
Even.
Frame <<
" even)" << std::endl;
708 *out <<
" - Audio samples mapped to frame "
743 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
781 root[
"type"] =
"FrameMapper";
800 if (!root[
"reader"].isNull())
806 catch (
const std::exception& e)
809 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
824 "FrameMapper::ChangeMapping",
825 "target_fps.num", target_fps.
num,
826 "target_fps.den", target_fps.
den,
827 "target_pulldown", target_pulldown,
828 "target_sample_rate", target_sample_rate,
829 "target_channels", target_channels,
830 "target_channel_layout", target_channel_layout);
836 target.
num = target_fps.
num;
837 target.
den = target_fps.
den;
843 pulldown = target_pulldown;
875 int total_frame_samples = 0;
876 int channels_in_frame = frame->GetAudioChannelsCount();
877 int sample_rate_in_frame = frame->SampleRate();
878 int samples_in_frame = frame->GetAudioSamplesCount();
879 ChannelLayout channel_layout_in_frame = frame->ChannelsLayout();
882 "FrameMapper::ResampleMappedAudio",
883 "frame->number", frame->number,
884 "original_frame_number", original_frame_number,
885 "channels_in_frame", channels_in_frame,
886 "samples_in_frame", samples_in_frame,
887 "sample_rate_in_frame", sample_rate_in_frame);
890 float* frame_samples_float = NULL;
892 frame_samples_float = frame->GetInterleavedAudioSamples(&samples_in_frame);
895 total_frame_samples = samples_in_frame * channels_in_frame;
898 int16_t* frame_samples = (int16_t*) av_malloc(
sizeof(int16_t)*total_frame_samples);
903 const int16_t max16 = 32767;
904 const int16_t min16 = -32768;
905 for (
int s = 0; s < total_frame_samples; s++) {
906 valF = frame_samples_float[s] * (1 << 15);
909 else if (valF < min16)
912 conv = int(valF + 32768.5) - 32768;
915 frame_samples[s] = conv;
919 delete[] frame_samples_float;
920 frame_samples_float = NULL;
925 audio_frame->nb_samples = total_frame_samples / channels_in_frame;
927 int buf_size = audio_frame->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * channels_in_frame;
928 int error_code = avcodec_fill_audio_frame(
929 audio_frame, channels_in_frame, AV_SAMPLE_FMT_S16,
930 (uint8_t *) frame_samples, buf_size, 1);
935 "FrameMapper::ResampleMappedAudio ERROR [" + av_err2string(error_code) +
"]",
936 "error_code", error_code);
937 throw ErrorEncodingVideo(
"Error while resampling audio in frame mapper", frame->number);
946 audio_converted->nb_samples = total_frame_samples;
947 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.
channels, total_frame_samples, AV_SAMPLE_FMT_S16, 0);
954 av_opt_set_int(avr,
"in_channel_layout", channel_layout_in_frame, 0);
956 av_opt_set_int(avr,
"in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
957 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
958 av_opt_set_int(avr,
"in_sample_rate", sample_rate_in_frame, 0);
960 av_opt_set_int(avr,
"in_channels", channels_in_frame, 0);
967 audio_converted->data,
968 audio_converted->linesize[0],
969 audio_converted->nb_samples,
971 audio_frame->linesize[0],
972 audio_frame->nb_samples);
975 int16_t* resampled_samples =
new int16_t[(nb_samples *
info.
channels)];
978 memcpy(resampled_samples, audio_converted->data[0], (nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) *
info.
channels));
981 av_freep(&audio_frame->data[0]);
983 av_freep(&audio_converted->data[0]);
985 frame_samples = NULL;
988 int channel_buffer_size = nb_samples;
992 "FrameMapper::ResampleMappedAudio (Audio successfully resampled)",
993 "nb_samples", nb_samples,
994 "total_frame_samples", total_frame_samples,
996 "channels_in_frame", channels_in_frame,
1001 float *channel_buffer =
new float[channel_buffer_size];
1004 for (
int channel_filter = 0; channel_filter <
info.
channels; channel_filter++)
1007 for (
int z = 0; z < channel_buffer_size; z++)
1008 channel_buffer[z] = 0.0f;
1014 for (
int sample = 0; sample < (nb_samples *
info.
channels); sample++)
1017 if (channel_filter == channel)
1020 channel_buffer[position] = resampled_samples[sample] * (1.0f / (1 << 15));
1036 frame->AddAudio(
true, channel_filter, 0, channel_buffer, position, 1.0f);
1044 delete[] channel_buffer;
1045 channel_buffer = NULL;
1048 delete[] resampled_samples;
1049 resampled_samples = NULL;
1052 previous_frame = frame->number;
1056int64_t FrameMapper::AdjustFrameNumber(int64_t clip_frame_number) {
1059 float position = 0.0;
1064 start = parent->
Start();
1072 int64_t clip_start_position = round(position *
info.
fps.
ToDouble()) + 1;
1073 int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
1075 return frame_number;
1081 const std::lock_guard<std::recursive_mutex> lock(directionMutex);
1082 hint_increasing = increasing;
Header file for Clip class.
Header file for all Exception classes.
#define AV_FREE_FRAME(av_frame)
#define SWR_CONVERT(ctx, out, linesize, out_count, in, linesize2, in_count)
#define AV_ALLOCATE_FRAME()
#define AV_RESET_FRAME(av_frame)
Header file for the FrameMapper class.
Cross-platform helper to encourage returning freed memory to the OS.
#define OPEN_MP_NUM_PROCESSORS
Header file for ZeroMQ-based Logger class.
void SetMaxBytesFromInfo(int64_t number_of_frames, int width, int height, int sample_rate, int channels)
Set maximum bytes to a different amount based on a ReaderInfo struct.
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
void Clear()
Clear the cache of all frames.
float Start() const
Get start position (in seconds) of clip (trim start of video)
float Position() const
Get position on timeline (in seconds)
This class represents a clip (used to arrange readers on the timeline)
Exception when encoding audio packet.
This class represents a fraction.
int num
Numerator for the fraction.
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
int ToInt()
Return a rounded integer of the fraction (for example 30000/1001 returns 30)
int den
Denominator for the fraction.
std::shared_ptr< Frame > GetFrame(int64_t requested_frame) override
This method is required for all derived classes of ReaderBase, and return the openshot::Frame object,...
void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
Change frame rate or audio mapping details.
MappedFrame GetMappedFrame(int64_t TargetFrameNumber)
Get a frame based on the target frame rate and the new frame number of a frame.
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
bool IsOpen() override
Determine if reader is open or closed.
std::vector< Field > fields
ReaderBase * Reader()
Get the current reader.
std::vector< MappedFrame > frames
void ResampleMappedAudio(std::shared_ptr< Frame > frame, int64_t original_frame_number)
Resample audio and map channels (if needed)
void Open() override
Open the internal reader.
void Close() override
Close the openshot::FrameMapper and internal reader.
std::string Json() const override
Generate JSON string of this object.
FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
Default constructor for openshot::FrameMapper class.
void SetDirectionHint(const bool increasing)
Set time-curve informed direction hint (from Clip class) for the next call to GetFrame.
void PrintMapping(std::ostream *out=&std::cout)
Print all of the original frames and which new frames they map to.
void SetJson(const std::string value) override
Load JSON string into this object.
Json::Value JsonValue() const override
Generate Json::Value for this object.
virtual ~FrameMapper()
Destructor.
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Exception for invalid JSON.
Exception for frames that are out of bounds.
This abstract class is the base class, used by all readers in libopenshot.
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::ReaderInfo info
Information about the current media file.
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
virtual void Close()=0
Close the reader (and any resources it was consuming)
Exception when a reader is closed, and a frame is requested.
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
void AppendDebugMethod(std::string method_name, std::string arg1_name="", float arg1_value=-1.0, std::string arg2_name="", float arg2_value=-1.0, std::string arg3_name="", float arg3_value=-1.0, std::string arg4_name="", float arg4_value=-1.0, std::string arg5_name="", float arg5_value=-1.0, std::string arg6_name="", float arg6_value=-1.0)
Append debug information.
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
This namespace is the default namespace for all code in the openshot library.
PulldownType
This enumeration determines how frame rates are increased or decreased.
@ PULLDOWN_CLASSIC
Classic 2:3:2:3 pull-down.
@ PULLDOWN_ADVANCED
Advanced 2:3:3:2 pull-down (minimal dirty frames)
@ PULLDOWN_NONE
Do not apply pull-down techniques, just repeat or skip entire frames.
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround,...
const Json::Value stringToJson(const std::string value)
This struct holds a single field (half a frame).
This struct holds two fields which together make up a complete video frame.
bool has_single_image
Determines if this file only contains a single image.
float duration
Length of time (in seconds)
int width
The width of the video (in pixesl)
int channels
The number of audio channels used in the audio stream.
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
int height
The height of the video (in pixels)
int64_t video_length
The number of frames in the video stream.
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
bool has_video
Determines if this file has a video stream.
bool has_audio
Determines if this file has an audio stream.
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
This struct holds a the range of samples needed by this frame.
void Extend(int64_t samples, openshot::Fraction fps, int sample_rate, int channels, bool right_side)
Extend SampleRange on either side.
void Shift(int64_t samples, openshot::Fraction fps, int sample_rate, int channels, bool right_side)