149 lines
3.9 KiB
C++
149 lines
3.9 KiB
C++
#include "VideoOutputThread.h"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <thread>
|
|
|
|
namespace
|
|
{
|
|
int gFailures = 0;
|
|
|
|
void Expect(bool condition, const char* message)
|
|
{
|
|
if (condition)
|
|
return;
|
|
std::cerr << "FAIL: " << message << "\n";
|
|
++gFailures;
|
|
}
|
|
|
|
struct FakeExchangeMetrics
|
|
{
|
|
std::size_t scheduledCount = 0;
|
|
};
|
|
|
|
class FakeExchange
|
|
{
|
|
public:
|
|
FakeExchange()
|
|
{
|
|
mFrame.bytes = mBytes;
|
|
mFrame.rowBytes = 8;
|
|
mFrame.width = 2;
|
|
mFrame.height = 2;
|
|
mFrame.pixelFormat = VideoIOPixelFormat::Bgra8;
|
|
}
|
|
|
|
FakeExchangeMetrics Metrics() const
|
|
{
|
|
FakeExchangeMetrics metrics;
|
|
metrics.scheduledCount = mScheduled.load();
|
|
return metrics;
|
|
}
|
|
|
|
bool ConsumeCompletedForSchedule(SystemFrame& frame)
|
|
{
|
|
bool expected = true;
|
|
if (!mHasFrame.compare_exchange_strong(expected, false))
|
|
return false;
|
|
frame = mFrame;
|
|
mScheduled.fetch_add(1);
|
|
return true;
|
|
}
|
|
|
|
void ReleaseScheduledByBytes(void*)
|
|
{
|
|
mReleased.fetch_add(1);
|
|
}
|
|
|
|
std::atomic<std::size_t> mScheduled{ 0 };
|
|
std::atomic<std::size_t> mReleased{ 0 };
|
|
|
|
private:
|
|
std::atomic<bool> mHasFrame{ true };
|
|
unsigned char mBytes[16] = {};
|
|
SystemFrame mFrame;
|
|
};
|
|
|
|
class FakeOutputEdge final : public RenderCadenceCompositor::IVideoOutputEdge
|
|
{
|
|
public:
|
|
bool StartScheduledPlayback(std::string&) override { return true; }
|
|
bool ScheduleFrame(const VideoIOOutputFrame& frame) override
|
|
{
|
|
mLastWidth = frame.width;
|
|
mScheduled.fetch_add(1);
|
|
return mScheduleResult;
|
|
}
|
|
void Stop() override {}
|
|
void ReleaseResources() override {}
|
|
const VideoIOState& State() const override { return mState; }
|
|
RenderCadenceCompositor::VideoOutputEdgeMetrics Metrics() const override { return mMetrics; }
|
|
|
|
bool mScheduleResult = true;
|
|
std::atomic<std::size_t> mScheduled{ 0 };
|
|
unsigned mLastWidth = 0;
|
|
|
|
private:
|
|
VideoIOState mState;
|
|
RenderCadenceCompositor::VideoOutputEdgeMetrics mMetrics;
|
|
};
|
|
|
|
void TestSchedulesCompletedFrameThroughGenericEdge()
|
|
{
|
|
FakeExchange exchange;
|
|
FakeOutputEdge output;
|
|
RenderCadenceCompositor::VideoOutputThreadConfig config;
|
|
config.targetBufferedFrames = 1;
|
|
config.idleSleep = std::chrono::milliseconds(1);
|
|
|
|
RenderCadenceCompositor::VideoOutputThread<FakeExchange> thread(output, exchange, config);
|
|
Expect(thread.Start(), "video output thread starts");
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
thread.Stop();
|
|
|
|
const auto metrics = thread.Metrics();
|
|
Expect(output.mScheduled.load() == 1, "generic output edge receives one scheduled frame");
|
|
Expect(output.mLastWidth == 2, "scheduled frame data comes from exchange");
|
|
Expect(metrics.scheduledFrames == 1, "thread scheduled frame metric increments");
|
|
Expect(metrics.scheduleFailures == 0, "no schedule failure is reported");
|
|
Expect(exchange.mReleased.load() == 0, "successful schedule keeps frame scheduled");
|
|
}
|
|
|
|
void TestReleasesFrameWhenGenericEdgeRejectsSchedule()
|
|
{
|
|
FakeExchange exchange;
|
|
FakeOutputEdge output;
|
|
output.mScheduleResult = false;
|
|
RenderCadenceCompositor::VideoOutputThreadConfig config;
|
|
config.targetBufferedFrames = 1;
|
|
config.idleSleep = std::chrono::milliseconds(1);
|
|
|
|
RenderCadenceCompositor::VideoOutputThread<FakeExchange> thread(output, exchange, config);
|
|
Expect(thread.Start(), "video output thread starts for failure path");
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
thread.Stop();
|
|
|
|
const auto metrics = thread.Metrics();
|
|
Expect(output.mScheduled.load() == 1, "generic output edge receives failed schedule attempt");
|
|
Expect(metrics.scheduledFrames == 0, "failed schedule does not increment scheduled frames");
|
|
Expect(metrics.scheduleFailures == 1, "schedule failure metric increments");
|
|
Expect(exchange.mReleased.load() == 1, "failed schedule releases the frame back to exchange");
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
TestSchedulesCompletedFrameThroughGenericEdge();
|
|
TestReleasesFrameWhenGenericEdgeRejectsSchedule();
|
|
|
|
if (gFailures != 0)
|
|
{
|
|
std::cerr << gFailures << " VideoOutputThread test failure(s).\n";
|
|
return 1;
|
|
}
|
|
|
|
std::cout << "VideoOutputThread tests passed.\n";
|
|
return 0;
|
|
}
|