From 2d6db64b5215983b0c620c74f8d43740cb7d2334 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 6 Jun 2025 15:33:04 -0500 Subject: [PATCH] Incorporating VideoCacheThread into openshot-example executable, to experiment with caching during export. It works great! --- examples/Example.cpp | 94 ++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/examples/Example.cpp b/examples/Example.cpp index 6c74c0c2..2067fb56 100644 --- a/examples/Example.cpp +++ b/examples/Example.cpp @@ -1,12 +1,12 @@ /** * @file - * @brief Source file for Example Executable (example app for libopenshot) + * @brief Example application showing how to attach VideoCacheThread to an FFmpegReader * @author Jonathan Thomas * * @ref License */ -// Copyright (c) 2008-2019 OpenShot Studios, LLC +// Copyright (c) 2008-2025 OpenShot Studios, LLC // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -16,62 +16,80 @@ #include "Frame.h" #include "FFmpegReader.h" #include "FFmpegWriter.h" +#include "Timeline.h" +#include "Qt/VideoCacheThread.h" // <— your new header using namespace openshot; int main(int argc, char* argv[]) { - // Path to your video - const char* path = "/home/jonathan/Downloads/openshot-testing/sintel_trailer-720p.mp4"; - - FFmpegReader r(path); - r.Open(); - - const long int total_frames = r.info.video_length; - // Reader - std::stringstream writer_path; - writer_path << TEST_MEDIA_PATH << "sintel_trailer-720p.mp4"; + // 1) Open the FFmpegReader as usual + const char* input_path = "/home/jonathan/Downloads/openshot-testing/sintel_trailer-720p.mp4"; + FFmpegReader reader(input_path); + reader.Open(); - /* WRITER ---------------- */ - FFmpegWriter w("/home/jonathan/Downloads/performance-test.mp4"); - - // Set options - w.SetAudioOptions("aac", 48000, 192000); - w.SetVideoOptions("libx264", 1280, 720, Fraction(30,1), 5000000); - - // Open writer - w.Open(); + const int64_t total_frames = reader.info.video_length; + std::cout << "Total frames: " << total_frames << "\n"; - // --- Measure forward pass --- + + Timeline timeline(reader.info.width, reader.info.height, reader.info.fps, reader.info.sample_rate, reader.info.channels, reader.info.channel_layout); + Clip c1(&reader); + timeline.AddClip(&c1); + timeline.Open(); + timeline.DisplayInfo(); + + + // 2) Construct a VideoCacheThread around 'reader' and start its background loop + // (VideoCacheThread inherits juce::Thread) + std::shared_ptr cache = std::make_shared(); + cache->Reader(&timeline); // attaches the FFmpegReader and internally calls Play() + cache->StartThread(); // juce::Thread method, begins run() + + // 3) Set up the writer exactly as before + FFmpegWriter writer("/home/jonathan/Downloads/performance‐cachetest.mp4"); + writer.SetAudioOptions("aac", 48000, 192000); + writer.SetVideoOptions("libx264", 1280, 720, Fraction(30, 1), 5000000); + writer.Open(); + + // 4) Forward pass: for each frame 1…N, tell the cache thread to seek to that frame, + // then immediately call cache->GetFrame(frame), which will block only if that frame + // hasn’t been decoded into the cache yet. auto t0 = std::chrono::high_resolution_clock::now(); - for (long int frame = 1; frame <= total_frames; frame++) { - float percent = (float(frame) / total_frames) * 100.0f; - std::cout << "Forward: Requesting Frame #: " << frame - << " (" << percent << "%)\n"; - std::shared_ptr f = r.GetFrame(frame); - w.WriteFrame(f); + cache->setSpeed(1); + for (int64_t f = 1; f <= total_frames; ++f) { + float pct = (float(f) / total_frames) * 100.0f; + std::cout << "Forward: requesting frame " << f << " (" << pct << "%)\n"; + + cache->Seek(f); // signal “I need frame f now (and please prefetch f+1, f+2, …)” + std::shared_ptr framePtr = timeline.GetFrame(f); + writer.WriteFrame(framePtr); } auto t1 = std::chrono::high_resolution_clock::now(); auto forward_ms = std::chrono::duration_cast(t1 - t0).count(); - // --- Measure backward pass --- + // 5) Backward pass: same idea in reverse auto t2 = std::chrono::high_resolution_clock::now(); - for (long int frame = total_frames; frame >= 1; frame--) { - float percent = (float(total_frames - frame + 1) / total_frames) * 100.0f; - std::cout << "Backward: Requesting Frame #: " << frame - << " (" << percent << "%)\n"; - std::shared_ptr f = r.GetFrame(frame); - w.WriteFrame(f); + cache->setSpeed(-1); + for (int64_t f = total_frames; f >= 1; --f) { + float pct = (float(total_frames - f + 1) / total_frames) * 100.0f; + std::cout << "Backward: requesting frame " << f << " (" << pct << "%)\n"; + + cache->Seek(f); + std::shared_ptr framePtr = timeline.GetFrame(f); + writer.WriteFrame(framePtr); } auto t3 = std::chrono::high_resolution_clock::now(); auto backward_ms = std::chrono::duration_cast(t3 - t2).count(); - std::cout << "\nForward pass elapsed: " << forward_ms << " ms\n"; + std::cout << "\nForward pass elapsed: " << forward_ms << " ms\n"; std::cout << "Backward pass elapsed: " << backward_ms << " ms\n"; - r.Close(); - w.Close(); + // 6) Shut down the cache thread, close everything + cache->StopThread(10000); // politely tells run() to exit, waits up to 10s + reader.Close(); + writer.Close(); + timeline.Close(); return 0; }