diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index 201d7477..96de8cb7 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -77,7 +77,7 @@ namespace openshot * * // Create a reader for a video * FFmpegReader r("MyAwesomeVideo.webm"); - * r.Open(); // Open the reader + * r.Open(); // Open thetarget_ reader * * // Create a writer (which will create a WebM video) * FFmpegWriter w("/home/jonathan/NewVideo.webm"); @@ -173,7 +173,6 @@ namespace openshot int audio_input_frame_size; int initial_audio_input_frame_size; int audio_input_position; - AudioResampler *resampler; AVAudioResampleContext *avr; AVAudioResampleContext *avr_planar; diff --git a/include/FrameMapper.h b/include/FrameMapper.h index 9dd6e85a..2000ddb5 100644 --- a/include/FrameMapper.h +++ b/include/FrameMapper.h @@ -132,10 +132,14 @@ namespace openshot bool is_open; bool field_toggle; // Internal odd / even toggle (used when building the mapping) Fraction original; // The original frame rate - Fraction target; // The target frame rate + Fraction target; // The target frame rate + int sample_rate; // The target sample rate + int channels; // The target channels + ChannelLayout channel_layout; // The layout of audio channels PulldownType pulldown; // The pull-down technique ReaderBase *reader; // The source video reader Cache final_cache; // Cache of actual Frame objects + bool is_dirty; // When this is true, the next call to GetFrame will re-init the mapping // Internal methods used by init void AddField(int frame); @@ -153,7 +157,7 @@ namespace openshot vector frames; // List of all frames /// Default constructor for FrameMapper class - FrameMapper(ReaderBase *reader, Fraction target, PulldownType pulldown); + FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); /// Close the internal reader void Close(); @@ -178,24 +182,15 @@ namespace openshot Json::Value JsonValue(); ///< Generate Json::JsonValue for this object void SetJsonValue(Json::Value root) throw(InvalidFile); ///< Load Json::JsonValue into this object - /// Get the target framerate - Fraction TargetFPS() { return target; }; - - /// Get the source framerate - Fraction SourceFPS() { return original; }; - - /// Set the target framerate - void TargetFPS(Fraction new_fps) { target = new_fps; }; - - /// Set the source framerate - void SourceFPS(Fraction new_fps) { original = new_fps; }; - /// Open the internal reader void Open() throw(InvalidFile); /// Print all of the original frames and which new frames they map to void PrintMapping(); + /// Change frame rate or audio mapping details + void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); + }; } diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 80ee8c04..e91aa1bf 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -35,7 +35,7 @@ using namespace openshot; FFmpegWriter::FFmpegWriter(string path) throw (InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory) : path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL), audio_outbuf(NULL), audio_outbuf_size(0), audio_input_frame_size(0), audio_input_position(0), - initial_audio_input_frame_size(0), resampler(NULL), img_convert_ctx(NULL), cache_size(8), num_of_rescalers(32), + initial_audio_input_frame_size(0), img_convert_ctx(NULL), cache_size(8), num_of_rescalers(32), rescaler_position(0), video_codec(NULL), audio_codec(NULL), is_writing(false), write_video_count(0), write_audio_count(0), original_sample_rate(0), original_channels(0), avr(NULL), avr_planar(NULL), is_open(false), prepare_streams(false), write_header(false), write_trailer(false) @@ -696,8 +696,6 @@ void FFmpegWriter::close_audio(AVFormatContext *oc, AVStream *st) delete[] samples; delete[] audio_outbuf; - delete resampler; - // Deallocate resample buffer avresample_close(avr); avresample_free(&avr); @@ -1022,12 +1020,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) // write all queued frames' audio to the video file void FFmpegWriter::write_audio_packets(bool final) { - // Create a resampler (only once) - if (!resampler) - resampler = new AudioResampler(); - AudioResampler *new_sampler = resampler; - - #pragma omp task firstprivate(new_sampler) + #pragma omp task firstprivate(final) { // Init audio buffers / variables int total_frame_samples = 0; @@ -1056,7 +1049,7 @@ void FFmpegWriter::write_audio_packets(bool final) // 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, new_sampler, &samples_in_frame); + frame_samples_float = frame->GetInterleavedAudioSamples(sample_rate_in_frame, NULL, &samples_in_frame); // Calculate total samples diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index f4eacb33..8164f197 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -30,8 +30,9 @@ using namespace std; using namespace openshot; -FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType pulldown) : - reader(reader), target(target), pulldown(pulldown), final_cache(820 * 1024) +FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) : + reader(reader), target(target), pulldown(target_pulldown), sample_rate(target_sample_rate), channels(target_channels), channel_layout(target_channel_layout), + final_cache(820 * 1024), is_dirty(true) { // Set the original frame rate from the reader original = Fraction(reader->info.fps.num, reader->info.fps.den); @@ -80,6 +81,12 @@ void FrameMapper::Init() fields.clear(); frames.clear(); + // Mark as not dirty + is_dirty = false; + + // Clear cache + final_cache.Clear(); + // Some framerates are handled special, and some use a generic Keyframe curve to // map the framerates. These are the special framerates: if ((original.ToInt() == 24 || original.ToInt() == 25 || original.ToInt() == 30) && @@ -290,6 +297,11 @@ MappedFrame FrameMapper::GetMappedFrame(int TargetFrameNumber) throw(OutOfBounds // Get an openshot::Frame object for a specific frame number of this reader. tr1::shared_ptr FrameMapper::GetFrame(int requested_frame) throw(ReaderClosed) { + // Check if mappings are dirty (and need to be recalculated) + if (is_dirty) + // Recalculate mappings + Init(); + // Check final cache, and just return the frame (if it's available) if (final_cache.Exists(requested_frame)) return final_cache.GetFrame(requested_frame); @@ -297,6 +309,14 @@ tr1::shared_ptr FrameMapper::GetFrame(int requested_frame) throw(ReaderCl // Get the mapped frame MappedFrame mapped = GetMappedFrame(requested_frame); + // Return the original frame if no mapping is needed + if (reader->info.fps.num == target.num && reader->info.fps.den == target.den && + reader->info.sample_rate == sample_rate && reader->info.channels == channels && + reader->info.channel_layout == channel_layout) + // Return original frame (performance optimization) + return reader->GetFrame(mapped.Odd.Frame); + + // Init some basic properties about this frame int samples_in_frame = Frame::GetSamplesPerFrame(requested_frame, target, info.sample_rate); @@ -484,3 +504,18 @@ void FrameMapper::SetJsonValue(Json::Value root) throw(InvalidFile) { Open(); } } + +// Change frame rate or audio mapping details +void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) +{ + // Mark as dirty + is_dirty = true; + + // Update mapping details + target = target_fps; + pulldown = target_pulldown; + sample_rate = target_sample_rate; + channels = target_channels; + channel_layout = target_channel_layout; +} + diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 8a0a641b..dafccabe 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -37,7 +37,7 @@ TEST(FrameMapper_Get_Valid_Frame) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping between 24 fps and 29.97 fps using classic pulldown - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); try { @@ -58,7 +58,7 @@ TEST(FrameMapper_Invalid_Frame_Too_Small) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); // Check invalid frame number CHECK_THROW(mapping.GetMappedFrame(0), OutOfBoundsFrame); @@ -71,7 +71,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Classic) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); @@ -88,7 +88,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Advanced) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_ADVANCED); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -108,7 +108,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_None) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_NONE); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -125,7 +125,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Classic) DummyReader r(Fraction(30000, 1001), 720, 480, 22000, 2, 5.0); // Create mapping between 29.97 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -145,7 +145,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Advanced) DummyReader r(Fraction(30000, 1001), 720, 480, 22000, 2, 5.0); // Create mapping between 29.97 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -165,7 +165,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_None) DummyReader r(Fraction(30000, 1001), 720, 480, 22000, 2, 5.0); // Create mapping between 29.97 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5);