Files
video-shader-toys/tests/VideoIODeviceFakeTests.cpp
2026-05-12 01:21:42 +10:00

187 lines
6.1 KiB
C++

#include "VideoIOTypes.h"
#include <array>
#include <iostream>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
class FakeVideoIODevice : public VideoIODevice
{
public:
void ReleaseResources() override {}
bool DiscoverDevicesAndModes(const VideoFormatSelection&, std::string&) override
{
mState.inputFrameSize = { 1920, 1080 };
mState.outputFrameSize = { 1920, 1080 };
mState.inputDisplayModeName = "fake 1080p";
mState.outputModelName = "Fake Video IO";
mState.hasInputDevice = true;
return true;
}
bool SelectPreferredFormats(const VideoFormatSelection&, bool, std::string&) override
{
mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8;
mState.outputPixelFormat = VideoIOPixelFormat::Bgra8;
mState.inputFrameRowBytes = VideoIORowBytes(mState.inputPixelFormat, mState.inputFrameSize.width);
mState.outputFrameRowBytes = VideoIORowBytes(mState.outputPixelFormat, mState.outputFrameSize.width);
mState.captureTextureWidth = PackedTextureWidthFromRowBytes(mState.inputFrameRowBytes);
mState.outputPackTextureWidth = mState.outputFrameSize.width;
return true;
}
bool ConfigureInput(InputFrameCallback callback, const VideoFormat&, std::string&) override
{
mInputCallback = callback;
return true;
}
bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat&, bool, std::string&) override
{
mOutputCallback = callback;
return true;
}
bool PrepareOutputSchedule() override
{
mPreparedOutputSchedule = true;
return true;
}
bool StartInputStreams() override
{
mInputStreamsStarted = true;
mState.hasInputSource = true;
VideoIOFrame input;
input.bytes = mInputBytes.data();
input.rowBytes = static_cast<long>(mState.inputFrameRowBytes);
input.width = mState.inputFrameSize.width;
input.height = mState.inputFrameSize.height;
input.pixelFormat = mState.inputPixelFormat;
if (mInputCallback)
mInputCallback(input);
return true;
}
bool StartScheduledPlayback() override
{
mScheduledPlaybackStarted = true;
if (mOutputCallback)
mOutputCallback(VideoIOCompletion{ VideoIOCompletionResult::Completed });
return true;
}
bool Start() override
{
return PrepareOutputSchedule() && StartInputStreams() && StartScheduledPlayback();
}
bool Stop() override { return true; }
const VideoIOState& State() const override { return mState; }
VideoIOState& MutableState() override { return mState; }
bool BeginOutputFrame(VideoIOOutputFrame& frame) override
{
frame.bytes = mOutputBytes.data();
frame.rowBytes = static_cast<long>(mState.outputFrameRowBytes);
frame.width = mState.outputFrameSize.width;
frame.height = mState.outputFrameSize.height;
frame.pixelFormat = mState.outputPixelFormat;
return true;
}
void EndOutputFrame(VideoIOOutputFrame&) override {}
bool ScheduleOutputFrame(const VideoIOOutputFrame&) override
{
++mScheduledFrames;
return true;
}
VideoPlayoutRecoveryDecision AccountForCompletionResult(VideoIOCompletionResult result, uint64_t readyQueueDepth) override
{
mLastCompletion = result;
mLastReadyQueueDepth = readyQueueDepth;
VideoPlayoutRecoveryDecision decision;
decision.result = result;
decision.readyQueueDepth = readyQueueDepth;
return decision;
}
unsigned ScheduledFrames() const { return mScheduledFrames; }
bool PreparedOutputSchedule() const { return mPreparedOutputSchedule; }
bool InputStreamsStarted() const { return mInputStreamsStarted; }
bool ScheduledPlaybackStarted() const { return mScheduledPlaybackStarted; }
VideoIOCompletionResult LastCompletion() const { return mLastCompletion; }
uint64_t LastReadyQueueDepth() const { return mLastReadyQueueDepth; }
private:
VideoIOState mState;
InputFrameCallback mInputCallback;
OutputFrameCallback mOutputCallback;
std::array<unsigned char, 3840> mInputBytes = {};
std::array<unsigned char, 7680> mOutputBytes = {};
unsigned mScheduledFrames = 0;
bool mPreparedOutputSchedule = false;
bool mInputStreamsStarted = false;
bool mScheduledPlaybackStarted = false;
VideoIOCompletionResult mLastCompletion = VideoIOCompletionResult::Unknown;
uint64_t mLastReadyQueueDepth = 0;
};
}
int main()
{
FakeVideoIODevice device;
VideoFormatSelection selection;
std::string error;
bool inputSeen = false;
bool outputSeen = false;
Expect(device.DiscoverDevicesAndModes(selection, error), "fake discovery succeeds");
Expect(device.SelectPreferredFormats(selection, false, error), "fake format selection succeeds");
Expect(device.ConfigureInput([&](const VideoIOFrame& frame) {
inputSeen = frame.bytes != nullptr && frame.width == 1920 && frame.pixelFormat == VideoIOPixelFormat::Uyvy8;
}, selection.input, error), "fake input config succeeds");
Expect(device.ConfigureOutput([&](const VideoIOCompletion& completion) {
outputSeen = completion.result == VideoIOCompletionResult::Completed;
}, selection.output, false, error), "fake output config succeeds");
Expect(device.Start(), "fake device starts");
VideoIOOutputFrame outputFrame;
Expect(device.BeginOutputFrame(outputFrame), "fake output frame can be acquired");
device.EndOutputFrame(outputFrame);
device.AccountForCompletionResult(VideoIOCompletionResult::Completed, 2);
Expect(device.ScheduleOutputFrame(outputFrame), "fake output frame can be scheduled");
Expect(inputSeen, "fake input callback emits generic frame");
Expect(outputSeen, "fake output callback emits generic completion");
Expect(device.PreparedOutputSchedule(), "fake output schedule was prepared");
Expect(device.InputStreamsStarted(), "fake input streams started");
Expect(device.ScheduledPlaybackStarted(), "fake scheduled playback started");
Expect(device.ScheduledFrames() == 1, "fake backend schedules one frame");
Expect(device.LastCompletion() == VideoIOCompletionResult::Completed, "fake backend records generic completion");
Expect(device.LastReadyQueueDepth() == 2, "fake backend records ready queue depth");
if (gFailures != 0)
{
std::cerr << gFailures << " VideoIODevice fake test failure(s).\n";
return 1;
}
std::cout << "VideoIODevice fake tests passed.\n";
return 0;
}