27 reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL), parent_position(0.0), parent_start(0.0), previous_frame(0)
71 throw ReaderClosed(
"No Reader has been initialized for FrameMapper. Call Reader(*reader) before calling this method.");
74void FrameMapper::AddField(int64_t frame)
77 Field f = { frame, bool(field_toggle) };
81void FrameMapper::AddField(int64_t frame,
bool isOdd)
84 Field f = { frame, isOdd };
88void FrameMapper::AddField(
Field field)
94 field_toggle = (field_toggle ? false :
true);
98void FrameMapper::Clear() {
100 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
113void FrameMapper::Init()
123 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
131 parent_position = parent->
Position();
132 parent_start = parent->
Start();
134 parent_position = 0.0;
146 if ((fabs(original.
ToFloat() - 24.0) < 1e-7 || fabs(original.
ToFloat() - 25.0) < 1e-7 || fabs(original.
ToFloat() - 30.0) < 1e-7) &&
147 (fabs(target.
ToFloat() - 24.0) < 1e-7 || fabs(target.
ToFloat() - 25.0) < 1e-7 || fabs(target.
ToFloat() - 30.0) < 1e-7)) {
150 float difference = target.
ToInt() - original.
ToInt();
153 int field_interval = 0;
154 int frame_interval = 0;
158 field_interval = round(fabs(original.
ToInt() / difference));
161 frame_interval = field_interval * 2.0f;
170 for (int64_t field = 1; field <= number_of_fields; field++)
178 else if (difference > 0)
188 else if (pulldown ==
PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
195 AddField(frame + 1, field_toggle);
197 else if (pulldown ==
PULLDOWN_NONE && field % frame_interval == 0)
204 else if (difference < 0)
210 field_toggle = (field_toggle ? false :
true);
212 else if (pulldown ==
PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
217 else if (pulldown ==
PULLDOWN_NONE && frame % field_interval == 0)
230 if (field % 2 == 0 && field > 0)
243 double original_frame_num = 1.0f;
244 for (int64_t frame_num = 1; frame_num <= new_length; frame_num++)
247 AddField(round(original_frame_num));
248 AddField(round(original_frame_num));
251 original_frame_num += value_increment;
256 Field Odd = {0,
true};
257 Field Even = {0,
true};
260 int64_t start_samples_frame = 1;
261 int start_samples_position = 0;
263 for (std::vector<Field>::size_type field = 1; field <=
fields.size(); field++)
269 if (field % 2 == 0 && field > 0)
272 int64_t frame_number = field / 2;
283 int64_t end_samples_frame = start_samples_frame;
284 int end_samples_position = start_samples_position;
287 while (remaining_samples > 0)
294 if (original_samples >= remaining_samples)
297 end_samples_position += remaining_samples - 1;
298 remaining_samples = 0;
302 end_samples_frame += 1;
303 end_samples_position = 0;
304 remaining_samples -= original_samples;
314 start_samples_frame = end_samples_frame;
315 start_samples_position = end_samples_position + 1;
318 start_samples_frame += 1;
319 start_samples_position = 0;
360 frame.
Odd.
Frame = TargetFrameNumber;
370 if(TargetFrameNumber < 1 ||
frames.size() == 0)
374 else if (TargetFrameNumber > (int64_t)
frames.size())
376 TargetFrameNumber =
frames.size();
380 "FrameMapper::GetMappedFrame",
381 "TargetFrameNumber", TargetFrameNumber,
382 "frames.size()",
frames.size(),
383 "frames[...].Odd",
frames[TargetFrameNumber - 1].Odd.Frame,
384 "frames[...].Even",
frames[TargetFrameNumber - 1].Even.Frame);
387 return frames[TargetFrameNumber - 1];
391std::shared_ptr<Frame> FrameMapper::GetOrCreateFrame(int64_t number)
393 std::shared_ptr<Frame> new_frame;
401 "FrameMapper::GetOrCreateFrame (from reader)",
403 "samples_in_frame", samples_in_frame);
406 new_frame = reader->
GetFrame(number);
419 "FrameMapper::GetOrCreateFrame (create blank)",
421 "samples_in_frame", samples_in_frame);
427 new_frame->AddAudioSilence(samples_in_frame);
435 std::shared_ptr<Frame> final_frame = final_cache.
GetFrame(requested_frame);
436 if (final_frame)
return final_frame;
439 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
443 bool is_increasing =
true;
444 bool direction_flipped =
false;
447 const std::lock_guard<std::recursive_mutex> lock(directionMutex);
451 is_increasing = hint_increasing;
453 }
else if (previous_frame > 0 && std::llabs(requested_frame - previous_frame) == 1) {
455 is_increasing = (requested_frame > previous_frame);
456 }
else if (last_dir_initialized) {
458 is_increasing = last_is_increasing;
460 is_increasing =
true;
464 if (!last_dir_initialized) {
465 last_is_increasing = is_increasing;
466 last_dir_initialized =
true;
467 }
else if (last_is_increasing != is_increasing) {
468 direction_flipped =
true;
469 last_is_increasing = is_increasing;
473 float position = parent->
Position();
474 float start = parent->
Start();
475 if (parent_position != position || parent_start != start) {
487 final_frame = final_cache.
GetFrame(requested_frame);
488 if (final_frame)
return final_frame;
492 int minimum_frames = 1;
496 "FrameMapper::GetFrame (Loop through frames)",
497 "requested_frame", requested_frame,
498 "minimum_frames", minimum_frames);
501 for (int64_t frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
505 "FrameMapper::GetFrame (inside omp for loop)",
506 "frame_number", frame_number,
507 "minimum_frames", minimum_frames,
508 "requested_frame", requested_frame);
512 std::shared_ptr<Frame> mapped_frame = GetOrCreateFrame(mapped.
Odd.
Frame);
515 int channels_in_frame = mapped_frame->GetAudioChannelsCount();
516 int samples_in_frame =
Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, mapped_frame->SampleRate(), channels_in_frame);
525 info.
channels == mapped_frame->GetAudioChannelsCount() &&
527 mapped.
Samples.
total == mapped_frame->GetAudioSamplesCount() &&
528 mapped.
Samples.
total == samples_in_frame && is_increasing &&
531 mapped_frame->number == frame_number &&
535 final_cache.
Add(mapped_frame);
540 auto frame = std::make_shared<Frame>(
541 frame_number, 1, 1,
"#000000", samples_in_frame, channels_in_frame);
542 frame->SampleRate(mapped_frame->SampleRate());
543 frame->ChannelsLayout(mapped_frame->ChannelsLayout());
547 std::shared_ptr<Frame> odd_frame = mapped_frame;
549 if (odd_frame && odd_frame->has_image_data)
550 frame->AddImage(std::make_shared<QImage>(*odd_frame->GetImage()),
true);
553 std::shared_ptr<Frame> even_frame;
554 even_frame = GetOrCreateFrame(mapped.
Even.
Frame);
555 if (even_frame && even_frame->has_image_data)
556 frame->AddImage(std::make_shared<QImage>(*even_frame->GetImage()),
false);
560 bool reader_has_audio = frame->SampleRate() > 0 && frame->GetAudioChannelsCount() > 0;
563 bool need_resampling =
false;
569 need_resampling =
true;
577 if (direction_flipped || (previous_frame > 0 && std::llabs(requested_frame - previous_frame) > 1)) {
588 const int EXTRA_INPUT_SAMPLES = 64;
602 int samples_copied = 0;
607 int remaining_samples = copy_samples.
total - samples_copied;
608 int number_to_copy = 0;
611 std::shared_ptr<Frame> original_frame = mapped_frame;
612 if (starting_frame != original_frame->number) {
613 original_frame = GetOrCreateFrame(starting_frame);
616 int original_samples = original_frame->GetAudioSamplesCount();
619 for (
int channel = 0; channel < channels_in_frame; channel++)
624 number_to_copy = original_samples - copy_samples.
sample_start;
625 if (number_to_copy > remaining_samples)
626 number_to_copy = remaining_samples;
629 frame->AddAudio(
true, channel, samples_copied, original_frame->GetAudioSamples(channel) + copy_samples.
sample_start, number_to_copy, 1.0);
634 number_to_copy = original_samples;
635 if (number_to_copy > remaining_samples)
636 number_to_copy = remaining_samples;
639 frame->AddAudio(
true, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
645 if (number_to_copy > remaining_samples)
646 number_to_copy = remaining_samples;
649 frame->AddAudio(
false, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
654 samples_copied += number_to_copy;
659 frame->SetAudioDirection(is_increasing);
667 final_cache.
Add(frame);
672 return final_cache.
GetFrame(requested_frame);
683 for (
float map = 1; map <=
frames.size(); map++)
686 *out <<
"Target frame #: " << map
687 <<
" mapped to original frame #:\t("
689 << frame.
Even.
Frame <<
" even)" << std::endl;
691 *out <<
" - Audio samples mapped to frame "
726 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
767 root[
"type"] =
"FrameMapper";
786 if (!root[
"reader"].isNull())
792 catch (
const std::exception& e)
795 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
817 "FrameMapper::ChangeMapping",
818 "target_fps.num", target_fps.
num,
819 "target_fps.den", target_fps.
den,
820 "target_pulldown", target_pulldown,
821 "target_sample_rate", target_sample_rate,
822 "target_channels", target_channels,
823 "target_channel_layout", target_channel_layout);
829 target.
num = target_fps.
num;
830 target.
den = target_fps.
den;
836 pulldown = target_pulldown;
867 int total_frame_samples = 0;
868 int channels_in_frame = frame->GetAudioChannelsCount();
869 int sample_rate_in_frame = frame->SampleRate();
870 int samples_in_frame = frame->GetAudioSamplesCount();
871 ChannelLayout channel_layout_in_frame = frame->ChannelsLayout();
874 "FrameMapper::ResampleMappedAudio",
875 "frame->number", frame->number,
876 "original_frame_number", original_frame_number,
877 "channels_in_frame", channels_in_frame,
878 "samples_in_frame", samples_in_frame,
879 "sample_rate_in_frame", sample_rate_in_frame);
882 float* frame_samples_float = NULL;
884 frame_samples_float = frame->GetInterleavedAudioSamples(&samples_in_frame);
887 total_frame_samples = samples_in_frame * channels_in_frame;
890 int16_t* frame_samples = (int16_t*) av_malloc(
sizeof(int16_t)*total_frame_samples);
895 const int16_t max16 = 32767;
896 const int16_t min16 = -32768;
897 for (
int s = 0; s < total_frame_samples; s++) {
898 valF = frame_samples_float[s] * (1 << 15);
901 else if (valF < min16)
904 conv = int(valF + 32768.5) - 32768;
907 frame_samples[s] = conv;
911 delete[] frame_samples_float;
912 frame_samples_float = NULL;
917 audio_frame->nb_samples = total_frame_samples / channels_in_frame;
919 int buf_size = audio_frame->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * channels_in_frame;
920 int error_code = avcodec_fill_audio_frame(
921 audio_frame, channels_in_frame, AV_SAMPLE_FMT_S16,
922 (uint8_t *) frame_samples, buf_size, 1);
927 "FrameMapper::ResampleMappedAudio ERROR [" + av_err2string(error_code) +
"]",
928 "error_code", error_code);
929 throw ErrorEncodingVideo(
"Error while resampling audio in frame mapper", frame->number);
938 audio_converted->nb_samples = total_frame_samples;
939 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.
channels, total_frame_samples, AV_SAMPLE_FMT_S16, 0);
946 av_opt_set_int(avr,
"in_channel_layout", channel_layout_in_frame, 0);
948 av_opt_set_int(avr,
"in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
949 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
950 av_opt_set_int(avr,
"in_sample_rate", sample_rate_in_frame, 0);
952 av_opt_set_int(avr,
"in_channels", channels_in_frame, 0);
959 audio_converted->data,
960 audio_converted->linesize[0],
961 audio_converted->nb_samples,
963 audio_frame->linesize[0],
964 audio_frame->nb_samples);
967 int16_t* resampled_samples =
new int16_t[(nb_samples *
info.
channels)];
970 memcpy(resampled_samples, audio_converted->data[0], (nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) *
info.
channels));
973 av_freep(&audio_frame->data[0]);
975 av_freep(&audio_converted->data[0]);
977 frame_samples = NULL;
980 int channel_buffer_size = nb_samples;
984 "FrameMapper::ResampleMappedAudio (Audio successfully resampled)",
985 "nb_samples", nb_samples,
986 "total_frame_samples", total_frame_samples,
988 "channels_in_frame", channels_in_frame,
993 float *channel_buffer =
new float[channel_buffer_size];
996 for (
int channel_filter = 0; channel_filter <
info.
channels; channel_filter++)
999 for (
int z = 0; z < channel_buffer_size; z++)
1000 channel_buffer[z] = 0.0f;
1006 for (
int sample = 0; sample < (nb_samples *
info.
channels); sample++)
1009 if (channel_filter == channel)
1012 channel_buffer[position] = resampled_samples[sample] * (1.0f / (1 << 15));
1028 frame->AddAudio(
true, channel_filter, 0, channel_buffer, position, 1.0f);
1036 delete[] channel_buffer;
1037 channel_buffer = NULL;
1040 delete[] resampled_samples;
1041 resampled_samples = NULL;
1044 previous_frame = frame->number;
1048int64_t FrameMapper::AdjustFrameNumber(int64_t clip_frame_number) {
1051 float position = 0.0;
1056 start = parent->
Start();
1064 int64_t clip_start_position = round(position *
info.
fps.
ToDouble()) + 1;
1065 int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
1067 return frame_number;
1073 const std::lock_guard<std::recursive_mutex> lock(directionMutex);
1074 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.
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,...
bool TrimMemoryToOS(bool force) noexcept
Attempt to return unused heap memory to the operating system.
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)