mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-10-14 03:30:59 -07:00
138 lines
3.4 KiB
C++
138 lines
3.4 KiB
C++
/**
|
|
* @file
|
|
* @brief Unit tests for openshot::QtPlayer
|
|
* @author OpenShot Studios, LLC
|
|
*
|
|
* @ref License
|
|
*/
|
|
|
|
// Copyright (c) 2008-2025 OpenShot Studios, LLC
|
|
//
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
#include "openshot_catch.h"
|
|
|
|
#include <memory>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
#include "DummyReader.h"
|
|
#include "QtPlayer.h"
|
|
#include "Qt/VideoRenderer.h"
|
|
|
|
class QWidget;
|
|
|
|
namespace {
|
|
class TestRenderer : public openshot::RendererBase
|
|
{
|
|
public:
|
|
uintptr_t last_widget = 0;
|
|
|
|
void OverrideWidget(uintptr_t qwidget_address) override
|
|
{
|
|
last_widget = qwidget_address;
|
|
}
|
|
|
|
protected:
|
|
void render(std::shared_ptr<QImage> image) override
|
|
{
|
|
(void) image;
|
|
}
|
|
};
|
|
|
|
class SlowRenderer : public openshot::RendererBase
|
|
{
|
|
public:
|
|
std::atomic<int> renders{0};
|
|
void OverrideWidget(uintptr_t) override {}
|
|
|
|
protected:
|
|
void render(std::shared_ptr<QImage> image) override
|
|
{
|
|
if (image)
|
|
++renders;
|
|
// Longer than the player's render timeout: the next frame may be
|
|
// submitted while this renderer still owns the previous one.
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(120));
|
|
}
|
|
};
|
|
|
|
class SlowReader : public openshot::DummyReader
|
|
{
|
|
public:
|
|
std::atomic<bool> decoding{false};
|
|
SlowReader() : DummyReader(openshot::Fraction(30, 1), 16, 16, 44100, 2, 30) {}
|
|
|
|
std::shared_ptr<openshot::Frame> GetFrame(int64_t frame_number) override
|
|
{
|
|
decoding = true;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
|
auto frame = DummyReader::GetFrame(frame_number);
|
|
decoding = false;
|
|
return frame;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
TEST_CASE("QtPlayer_GetRendererQObject_ReturnsVideoRendererAddress", "[libopenshot][qtplayer]")
|
|
{
|
|
auto renderer = std::make_unique<VideoRenderer>();
|
|
openshot::QtPlayer player(renderer.get());
|
|
|
|
auto addr = player.GetRendererQObject();
|
|
CHECK(addr == reinterpret_cast<uintptr_t>(renderer.get()));
|
|
}
|
|
|
|
TEST_CASE("QtPlayer_SetQWidget_Overload_ForwardsPointer", "[libopenshot][qtplayer]")
|
|
{
|
|
TestRenderer renderer;
|
|
openshot::QtPlayer player(&renderer);
|
|
|
|
char dummy = 0;
|
|
auto *widget = reinterpret_cast<QWidget*>(&dummy);
|
|
player.SetQWidget(widget);
|
|
|
|
CHECK(renderer.last_widget == reinterpret_cast<uintptr_t>(widget));
|
|
}
|
|
|
|
TEST_CASE("QtPlayer keeps frames alive during slow rendering and seeks", "[libopenshot][qtplayer][threading]")
|
|
{
|
|
SlowRenderer renderer;
|
|
openshot::DummyReader reader(openshot::Fraction(30, 1), 16, 16, 44100, 2, 30);
|
|
reader.Open();
|
|
openshot::QtPlayer player(&renderer);
|
|
player.Reader(&reader);
|
|
player.Play();
|
|
|
|
std::thread seeker([&player] {
|
|
for (int i = 0; i < 80; ++i) {
|
|
player.Seek(1 + (i % 60), false);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
|
}
|
|
});
|
|
seeker.join();
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(350));
|
|
player.Stop();
|
|
CHECK(renderer.renders.load() > 0);
|
|
}
|
|
|
|
TEST_CASE("QtPlayer seek does not wait for a slow frame decode", "[libopenshot][qtplayer][threading]")
|
|
{
|
|
TestRenderer renderer;
|
|
SlowReader reader;
|
|
reader.Open();
|
|
openshot::QtPlayer player(&renderer);
|
|
player.Reader(&reader);
|
|
player.Play();
|
|
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
|
|
while (!reader.decoding && std::chrono::steady_clock::now() < deadline)
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
REQUIRE(reader.decoding.load());
|
|
const auto start = std::chrono::steady_clock::now();
|
|
player.Seek(20, false);
|
|
const auto elapsed = std::chrono::steady_clock::now() - start;
|
|
CHECK(elapsed < std::chrono::milliseconds(100));
|
|
player.Stop();
|
|
}
|