From 42d7565ba1110b2e6d12f69284f2759cf2faebee Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 7 Nov 2012 17:45:13 -0600 Subject: [PATCH] Integrated more timeline code, such as compositing layers... but they don't work yet --- include/Timeline.h | 11 ++++++- src/Clip.cpp | 4 +-- src/Main.cpp | 35 ++++++++++------------ src/Timeline.cpp | 64 ++++++++++++++++++++++++++++++++++++---- tests/Timeline_Tests.cpp | 8 ++--- 5 files changed, 90 insertions(+), 32 deletions(-) diff --git a/include/Timeline.h b/include/Timeline.h index 1b427bac..686873e9 100644 --- a/include/Timeline.h +++ b/include/Timeline.h @@ -9,6 +9,7 @@ #include #include +#include "Magick++.h" #include "Clip.h" #include "FileReaderBase.h" #include "Fraction.h" @@ -40,19 +41,27 @@ namespace openshot { int width; /// clips; /// open_clips; /// new_frame, Clip* source_clip, int clip_frame_number); + /// Update the list of 'opened' clips void update_open_clips(Clip *clip, bool is_open); public: /// Default Constructor for the timeline (which sets the canvas width and height and FPS) - Timeline(int width, int height, Framerate fps); + Timeline(int width, int height, Framerate fps, int sample_rate, int channels); /// Add an openshot::Clip to the timeline void AddClip(Clip* clip); diff --git a/src/Clip.cpp b/src/Clip.cpp index db4f1cab..0f80139b 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -439,13 +439,13 @@ tr1::shared_ptr Clip::get_time_mapped_frame(tr1::shared_ptr frame, cout << "samples: "<< samples << endl; delete samples; samples = NULL; + } else // Use original frame - new_frame = frame; + return frame; // Return new time mapped frame return new_frame; - } // Apply basic image processing (scale, rotate, move, etc...) diff --git a/src/Main.cpp b/src/Main.cpp index 3fd624db..13f26517 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -16,23 +16,23 @@ void FrameReady(int number) int main() { - // Create a reader - DummyReader r1(Framerate(24,1), 720, 480, 22000, 2, 5.0); - - // Map frames - FrameMapper mapping(&r1, Framerate(60, 1), PULLDOWN_CLASSIC); - mapping.PrintMapping(); - - return 0; +// // Create a reader +// DummyReader r1(Framerate(24,1), 720, 480, 22000, 2, 5.0); +// +// // Map frames +// FrameMapper mapping(&r1, Framerate(60, 1), PULLDOWN_CLASSIC); +// mapping.PrintMapping(); +// +// return 0; // Create timeline - Timeline t(640, 360, Framerate(24,1)); + Timeline t(640, 360, Framerate(24,1), 44100, 2); // Add some clips - Clip c1(new FFmpegReader("/home/jonathan/Videos/sintel-1024-stereo.mp4")); - c1.Position(0.0); + Clip c1(new FFmpegReader("/home/jonathan/Videos/sintel_trailer-720p.mp4")); + c1.Position(1.0); // LINEAR Reverse //c1.time.AddPoint(1, 500, LINEAR); @@ -47,8 +47,8 @@ int main() //c1.time.AddPoint(500, 250, LINEAR); // LINEAR Slow Reverse X4 (smooth) - c1.time.AddPoint(1, 500, LINEAR); - c1.time.AddPoint(750, 250, LINEAR); + //c1.time.AddPoint(1, 500, LINEAR); + //c1.time.AddPoint(750, 250, LINEAR); // LINEAR Fast Reverse (sounds wavy, due to periodic repeated frames) //c1.time.AddPoint(1, 600, LINEAR); @@ -75,7 +75,7 @@ int main() // c1.time.AddPoint(1, 500, LINEAR); // c1.time.AddPoint(200, 200); // c1.time.AddPoint(500, 500, LINEAR); - c1.time.PrintValues(); +// c1.time.PrintValues(); // Add clips t.AddClip(&c1); @@ -99,17 +99,12 @@ int main() // Output stream info w.OutputStreamInfo(); - for (int frame = 1; frame <= 100; frame++) + for (int frame = 1; frame <= 30; frame++) { tr1::shared_ptr f = t.GetFrame(frame); if (f) { - //f->AddOverlayNumber(0); - //if (frame >= 1 && frame <= 22) - // f->DisplayWaveform(); - // Write frame - //cout << "queue frame " << frame << endl; cout << "queue frame " << frame << " (" << f->number << ", " << f << ")" << endl; w.WriteFrame(f); } diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 576716f9..f6a9f46d 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -3,8 +3,8 @@ using namespace openshot; // Default Constructor for the timeline (which sets the canvas width and height) -Timeline::Timeline(int width, int height, Framerate fps) : - width(width), height(height), fps(fps) +Timeline::Timeline(int width, int height, Framerate fps, int sample_rate, int channels) : + width(width), height(height), fps(fps), sample_rate(sample_rate), channels(channels) { // Init viewport size (curve based, because it can be animated) viewport_scale = Keyframe(100.0); @@ -36,6 +36,32 @@ float Timeline::calculate_time(int number, Framerate rate) return float(number - 1) / raw_fps; } +// Process a new layer of video or audio +void Timeline::add_layer(tr1::shared_ptr new_frame, Clip* source_clip, int clip_frame_number) +{ + // Get the clip's frame & image + tr1::shared_ptr source_frame = source_clip->GetFrame(clip_frame_number); + tr1::shared_ptr source_image = source_frame->GetImage(); + + // Replace image (needed if this is the 1st layer) + if (new_frame->GetImage()->columns() == 1) + new_frame->AddColor(width, height, "#000000"); + + // Apply image effects + source_image->rotate(source_clip->rotation.GetValue(clip_frame_number)); + source_image->opacity(1 - source_clip->alpha.GetValue(clip_frame_number)); + + // Copy audio from source frame + for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) + new_frame->AddAudio(channel, 0, source_frame->GetAudioSamples(channel), source_frame->GetAudioSamplesCount(), 1.0f); + + // Composite images together + tr1::shared_ptr new_image = new_frame->GetImage(); + new_image->composite(*source_image.get(), source_clip->location_x.GetInt(clip_frame_number), source_clip->location_y.GetInt(clip_frame_number), Magick::InCompositeOp); + + +} + // Update the list of 'opened' clips void Timeline::update_open_clips(Clip *clip, bool is_open) { @@ -88,6 +114,21 @@ void Timeline::Open() } +// Calculate the # of samples per video frame (for a specific frame number) +int Timeline::GetSamplesPerFrame(int frame_number) +{ + // Get the total # of samples for the previous frame, and the current frame (rounded) + double fps_value = fps.GetFraction().Reciprocal().ToDouble(); + double previous_samples = round((sample_rate * fps_value) * (frame_number - 1)); + double total_samples = round((sample_rate * fps_value) * frame_number); + + // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can + // be evenly divided into frames, so each frame can have have different # of samples. + double samples_per_frame = total_samples - previous_samples; + return samples_per_frame; +} + + // Get an openshot::Frame object for a specific frame number of this reader. tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClosed) { @@ -95,6 +136,9 @@ tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClose if (requested_frame < 1) requested_frame = 1; + // Create blank frame (which will become the requested frame) + tr1::shared_ptr new_frame = tr1::shared_ptr(new Frame(requested_frame, width, height, "#000000", GetSamplesPerFrame(requested_frame), channels)); + // Calculate time of frame float requested_time = calculate_time(requested_frame, fps); @@ -114,12 +158,22 @@ tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClose // Clip is visible if (does_clip_intersect) { - // Display the clip (DEBUG) - return clip->GetFrame(requested_frame); + // Determine the frame needed for this clip (based on the position on the timeline) + float time_diff = (requested_time - clip->Position()) + clip->Start(); + int clip_frame_number = round(time_diff * fps.GetFPS()) + 1; + cout << "CLIP #: " << clip_frame_number << endl; + + // Add clip's frame as layer + add_layer(new_frame, clip, clip_frame_number); + } else cout << "FRAME NOT IN CLIP DURATION: frame: " << requested_frame << ", pos: " << clip->Position() << ", end: " << clip->End() << endl; } + // Check for empty frame image (and fill with color) + if (new_frame->GetImage()->columns() == 1) + new_frame->AddColor(width, height, "#000000"); + // No clips found - return tr1::shared_ptr(); + return new_frame; } diff --git a/tests/Timeline_Tests.cpp b/tests/Timeline_Tests.cpp index dffa97bc..2618cba6 100644 --- a/tests/Timeline_Tests.cpp +++ b/tests/Timeline_Tests.cpp @@ -8,14 +8,14 @@ TEST(Timeline_Constructor) { // Create a default fraction (should be 1/1) Framerate fps(30000,1000); - Timeline t1(640, 480, fps); + Timeline t1(640, 480, fps, 44100, 2); // Check values CHECK_EQUAL(640, t1.Width()); CHECK_EQUAL(480, t1.Height()); // Create a default fraction (should be 1/1) - Timeline t2(300, 240, fps); + Timeline t2(300, 240, fps, 44100, 2); // Check values CHECK_EQUAL(300, t2.Width()); @@ -26,7 +26,7 @@ TEST(Timeline_Width_and_Height_Functions) { // Create a default fraction (should be 1/1) Framerate fps(30000,1000); - Timeline t1(640, 480, fps); + Timeline t1(640, 480, fps, 44100, 2); // Check values CHECK_EQUAL(640, t1.Width()); @@ -51,7 +51,7 @@ TEST(Timeline_Framerate) { // Create a default fraction (should be 1/1) Framerate fps(24,1); - Timeline t1(640, 480, fps); + Timeline t1(640, 480, fps, 44100, 2); // Check values CHECK_CLOSE(24.0f, t1.FrameRate().GetFPS(), 0.00001);