#include "prx/libSceVdecsw/include/Api.hpp" #include #include #include #include #include #include #include #include #include "prx/libc/include/General.hpp" #include "Vp9Fixture.hpp" #include "H264Fixture.hpp" #include "HevcFixture.hpp" using namespace SceVdecsw; static_assert(sizeof(SceVideodec2::Vp9PictureInfo) == 0x58 && offsetof(SceVideodec2::Vp9PictureInfo, ptsData) == 0x10 && offsetof(SceVideodec2::Vp9PictureInfo, profile) == 0x28 && offsetof(SceVideodec2::Vp9PictureInfo, bitDepth) == 0x38 && offsetof(SceVideodec2::Vp9PictureInfo, colorSpace) == 0x3c && offsetof(SceVideodec2::Vp9PictureInfo, renderHeight) == 0x50); static_assert(sizeof(HevcPictureInfo) == 0xb8 && offsetof(HevcPictureInfo, idrPictureFlag) == 0x93 && offsetof(HevcPictureInfo, frameCroppingFlag) == 0xa5); static_assert(sizeof(SceVdecsw::DecoderConfigInfo) == 0x50 && sizeof(OutputInfo) == 0x38 && offsetof(OutputInfo, isLastFrame) == 9 && offsetof(OutputInfo, pictureCount) == 0xb && sizeof(AvcPictureInfo) == 0x78); namespace { using namespace SceVdecsw; using namespace H264Fixture; using namespace HevcFixture; using namespace Vp9Fixture; constexpr int OutputPending = static_cast(0x81510115u); constexpr int InputQueueEmpty = static_cast(0x81510116u); constexpr std::uint32_t CodecAvc = 1; constexpr std::uint32_t CodecHevc = 974921; constexpr std::uint32_t CodecVp9 = 2382845; constexpr std::array HevcDisplayOrderUnits{0, 3, 2, 1, 5, 4}; void check(bool condition, const std::string& message) { if (!condition) throw std::runtime_error(message); } struct Session { std::uint64_t queue = 0; std::uint64_t decoder = 0; SceVideodec2::DecoderMemoryInfo memory{sizeof(SceVideodec2::DecoderMemoryInfo)}; explicit Session(std::uint32_t codec = CodecAvc, std::uint32_t profile = 100, std::uint32_t level = 42) { SceVideodec2::ComputeMemoryInfo compute{sizeof(SceVideodec2::ComputeMemoryInfo)}; check(sceVdecswQueryComputeMemoryInfo(&compute) == 0 && compute.cpuGpuMemorySize != 0, "compute memory query failed"); const SceVideodec2::ComputeConfigInfo computeConfig{sizeof(SceVideodec2::ComputeConfigInfo), 3, 3, true}; check(sceVdecswAllocateComputeQueue(&computeConfig, &compute, &queue) == 0, "compute queue allocation failed"); SceVdecsw::DecoderConfigInfo config{{sizeof(SceVdecsw::DecoderConfigInfo), 1, codec, profile, level, static_cast(Width), static_cast(Height), 4, 3}, queue, 0x3f, 0, true, {}}; check(sceVdecswQueryDecoderMemoryInfo(&config, &memory) == 0 && memory.maxFrameBufferSize != 0, "decoder memory query failed"); check(sceVdecswCreateDecoder(&config, &memory, &decoder) == 0, "decoder creation failed"); } ~Session() { sceVdecswDeleteDecoder(decoder); sceVdecswReleaseComputeQueue(queue); } }; void testDecode() { Session session; const auto units = accessUnits(false); check(units.size() == PictureHashes.size(), "unexpected access unit count"); std::array, 2> buffers{std::vector(session.memory.maxFrameBufferSize), std::vector(session.memory.maxFrameBufferSize)}; InputResult result{sizeof(InputResult)}; check(sceVdecswTrySyncDecodeInput(session.decoder, &result) == InputQueueEmpty && result.decodedAu == nullptr, "an idle decoder did not report an empty input queue"); std::size_t pictures = 0; bool outputSet = false; bool ended = false; const auto drain = [&] { for (;;) { if (!outputSet) { const FrameBuffer frame{sizeof(FrameBuffer), buffers[pictures % 2].data(), buffers[pictures % 2].size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "output buffer was rejected"); outputSet = true; } OutputInfo output{sizeof(OutputInfo)}; const auto status = sceVdecswTrySyncDecodeOutput(session.decoder, &output); if (status == OutputPending) return; check(status == 0, "output sync failed"); outputSet = false; if (!output.isValid) { check(output.isLastFrame && output.pictureCount == 0, "an output without a picture is not the end of the sequence"); ended = true; return; } check(pictures < PictureHashes.size(), "more pictures than access units"); check(output.isLastFrame == (pictures + 1 == PictureHashes.size()), "only the last picture of the finalized sequence is marked last"); check(!output.isErrorFrame && !output.isDiscardedFrame && output.pictureCount == 1 && output.frameWidth == Width && output.frameHeight == CodedHeight && output.frameBuffer == buffers[pictures % 2].data(), "unexpected picture geometry"); AvcPictureInfo info{sizeof(AvcPictureInfo)}; check(sceVdecswGetAvcPictureInfo(&output, &info, nullptr) == 0 && info.isValid, "picture info missing"); const auto unit = DisplayOrderUnits[pictures]; check(info.ptsData == 1000 + unit && info.dtsData == unit && info.attachedData == 0xa0 + unit && info.idrPictureFlag == (pictures == 0 ? 1 : 0) && info.profileIdc == 100, "pictures out of display order"); check(info.picWidthInMbsMinus1 == Width / 16 - 1 && info.picHeightInMapUnitsMinus1 == CodedHeight / 16 - 1 && info.frameMbsOnlyFlag == 1 && info.frameCroppingFlag == 1 && info.frameCropLeftOffset == 0 && info.frameCropRightOffset == 0 && info.frameCropTopOffset == 0 && info.frameCropBottomOffset == (CodedHeight - Height) / 2, "picture geometry in the picture info is wrong"); check(hashNv12(static_cast(output.frameBuffer), output.framePitch, output.frameHeight) == PictureHashes[pictures], "picture " + std::to_string(pictures) + " differs from the reference"); ++pictures; } }; std::uint32_t produced = 0; for (std::size_t unit = 0; unit < units.size(); ++unit) { const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), units[unit].data(), units[unit].size(), 1000 + unit, unit, 0xa0 + unit}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "decode input was rejected"); InputResult consumed{sizeof(InputResult)}; check(sceVdecswTrySyncDecodeInput(session.decoder, &consumed) == 0 && consumed.decodedAu == units[unit].data(), "an access unit was not consumed"); produced += consumed.outputFrameCount; drain(); } check(pictures < PictureHashes.size(), "the last picture was returned before the sequence was finalized"); check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "finalize failed"); while (!ended) drain(); check(pictures == PictureHashes.size(), "missing pictures after finalizing the sequence"); check(produced <= PictureHashes.size(), "more pictures counted than decoded"); check(sceVdecswTrySyncDecodeInput(session.decoder, &result) == InputQueueEmpty, "a finalized decoder did not report an empty input queue"); } void testHevc() { Session session(CodecHevc, 1, 30); const auto units = hevcUnits(HevcStream.data(), HevcStream.size()); check(units.size() == HevcHashes.size(), "unexpected HEVC access unit count"); std::vector buffer(session.memory.maxFrameBufferSize); std::size_t pictures = 0; bool ended = false; const auto drain = [&] { for (;;) { const FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "HEVC output buffer was rejected"); OutputInfo output{sizeof(OutputInfo)}; const auto status = sceVdecswTrySyncDecodeOutput(session.decoder, &output); if (status == OutputPending) return; check(status == 0, "HEVC output sync failed"); if (!output.isValid) { ended = true; return; } check(pictures < HevcHashes.size() && output.codecType == CodecHevc && output.pictureCount == 1 && output.frameWidth == Width && output.frameHeight == Height, "unexpected HEVC picture geometry"); HevcPictureInfo info{sizeof(HevcPictureInfo)}; check(sceVdecswGetHevcPictureInfo(&output, &info) == 0 && info.isValid, "HEVC picture info missing"); const auto unit = HevcDisplayOrderUnits[pictures]; check(info.ptsData == 1000 + unit && info.dtsData == unit && info.attachedData == 0xa0 + unit, "HEVC pictures out of display order"); check(info.idrPictureFlag == (pictures == 0 ? 1 : 0) && info.irapPictureFlag == (pictures == 0 ? 1 : 0), "HEVC random access flags differ"); check(info.generalProfileIdc == 1 && info.generalLevelIdc == 30 && info.bitDepthLumaMinus8 == 0 && info.bitDepthChromaMinus8 == 0, "HEVC profile, level or bit depth differs"); check(info.picWidthInLumaSamples == Width && info.picHeightInLumaSamples == Height && info.conformanceWindowFlag == 0 && info.frameCroppingFlag == 0, "HEVC picture geometry in the picture info is wrong"); AvcPictureInfo avc{sizeof(AvcPictureInfo)}; bool avcThrows = false; try { sceVdecswGetAvcPictureInfo(&output, &avc, nullptr); } catch (const std::exception&) { avcThrows = true; } check(avcThrows, "AVC picture info was returned for an HEVC picture"); check(hashNv12(buffer.data(), output.framePitch, output.frameHeight) == HevcHashes[pictures], "HEVC picture " + std::to_string(pictures) + " differs from the reference"); ++pictures; } }; for (std::size_t unit = 0; unit < units.size(); ++unit) { const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), units[unit].data(), units[unit].size(), 1000 + unit, unit, 0xa0 + unit}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "HEVC decode input was rejected"); InputResult consumed{sizeof(InputResult)}; check(sceVdecswTrySyncDecodeInput(session.decoder, &consumed) == 0 && consumed.decodedAu == units[unit].data(), "an HEVC access unit was not consumed"); drain(); } check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "HEVC finalize failed"); while (!ended) drain(); check(pictures == HevcHashes.size(), "missing HEVC pictures after finalizing the sequence"); } void testHevcCrop() { Session session(CodecHevc, 1, 30); std::vector buffer(session.memory.maxFrameBufferSize); const FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "cropped HEVC output buffer was rejected"); const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), HevcCroppedStream.data(), HevcCroppedStream.size(), 1000, 0, 0xa0}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "cropped HEVC decode input was rejected"); InputResult consumed{sizeof(InputResult)}; check(sceVdecswSyncDecodeInput(session.decoder, &consumed) == 0, "the cropped HEVC access unit was not consumed"); check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "cropped HEVC finalize failed"); OutputInfo output{sizeof(OutputInfo)}; check(sceVdecswSyncDecodeOutput(session.decoder, &output) == 0 && output.isValid && output.frameWidth == 64 && output.frameHeight == 40, "unexpected cropped HEVC picture geometry"); HevcPictureInfo info{sizeof(HevcPictureInfo)}; check(sceVdecswGetHevcPictureInfo(&output, &info) == 0 && info.isValid, "cropped HEVC picture info missing"); check(info.picWidthInLumaSamples == 64 && info.picHeightInLumaSamples == 40, "cropped HEVC coded size differs"); check(info.conformanceWindowFlag == 1 && info.confWinLeftOffset == 0 && info.confWinRightOffset == 0 && info.confWinTopOffset == 0 && info.confWinBottomOffset == 2, "HEVC conformance window differs"); check(info.frameCroppingFlag == 1 && info.frameCropLeftOffset == 0 && info.frameCropRightOffset == 0 && info.frameCropTopOffset == 0 && info.frameCropBottomOffset == 2, "HEVC frame cropping differs"); } void testWithoutOutput() { Session session; const auto units = accessUnits(false); const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), units[0].data(), units[0].size(), 1000, 0, 0xa0}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "decode input was rejected"); InputResult result{sizeof(InputResult)}; check(sceVdecswTrySyncDecodeInput(session.decoder, &result) == 0 && result.decodedAu == units[0].data(), "an input was not decoded before an output buffer was set"); std::vector buffer(session.memory.maxFrameBufferSize); const FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "output buffer was rejected"); OutputInfo output{sizeof(OutputInfo)}; check(sceVdecswTrySyncDecodeOutput(session.decoder, &output) == OutputPending, "the only picture of an open sequence was returned before it was known to be the last"); check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "finalize failed"); check(sceVdecswTrySyncDecodeOutput(session.decoder, &output) == 0 && output.isValid && output.isLastFrame, "the only picture of a finalized sequence is not marked last"); check(sceVdecswResetDecoder(session.decoder) == 0, "reset failed"); check(sceVdecswTrySyncDecodeInput(session.decoder, &result) == InputQueueEmpty, "a reset decoder kept its inputs"); } void testSyncWrappers() { Session session; InputResult idle{sizeof(InputResult)}; check(sceVdecswSyncDecodeInput(session.decoder, &idle) == InputQueueEmpty, "sync input on an idle decoder did not report an empty queue"); const auto units = accessUnits(false); const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), units[0].data(), units[0].size(), 1000, 0, 0xa0}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "decode input was rejected"); InputResult result{sizeof(InputResult)}; check(sceVdecswSyncDecodeInput(session.decoder, &result) == 0 && result.decodedAu == units[0].data(), "sync input did not consume the access unit"); std::vector buffer(session.memory.maxFrameBufferSize); const FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "output buffer was rejected"); check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "finalize failed"); OutputInfo output{sizeof(OutputInfo)}; check(sceVdecswSyncDecodeOutput(session.decoder, &output) == 0 && output.isValid && output.isLastFrame, "sync output did not return the finalized picture"); HevcPictureInfo hevc{sizeof(HevcPictureInfo)}; bool hevcThrows = false; try { sceVdecswGetHevcPictureInfo(&output, &hevc); } catch (const std::exception&) { hevcThrows = true; } check(hevcThrows, "HEVC picture info was returned for an H.264 picture"); } struct Vp9Session { std::uint64_t queue = 0; std::uint64_t decoder = 0; SceVideodec2::DecoderMemoryInfo memory{sizeof(SceVideodec2::DecoderMemoryInfo)}; Vp9Session() { SceVideodec2::ComputeMemoryInfo compute{sizeof(SceVideodec2::ComputeMemoryInfo)}; check(sceVdecswQueryComputeMemoryInfo(&compute) == 0 && compute.cpuGpuMemorySize != 0, "compute memory query failed"); const SceVideodec2::ComputeConfigInfo computeConfig{sizeof(SceVideodec2::ComputeConfigInfo), 3, 3, true}; check(sceVdecswAllocateComputeQueue(&computeConfig, &compute, &queue) == 0, "compute queue allocation failed"); SceVdecsw::DecoderConfigInfo config{{sizeof(SceVdecsw::DecoderConfigInfo), 1, CodecVp9, 0, 0, static_cast(Width), static_cast(Height), 4, 3}, queue, 0x3f, 0, true, {}}; check(sceVdecswQueryDecoderMemoryInfo(&config, &memory) == 0 && memory.maxFrameBufferSize != 0, "VP9 decoder memory query failed"); check(sceVdecswCreateDecoder(&config, &memory, &decoder) == 0, "VP9 decoder creation failed"); } ~Vp9Session() { sceVdecswDeleteDecoder(decoder); sceVdecswReleaseComputeQueue(queue); } }; template bool throws(TAction action) { try { action(); } catch (const std::exception&) { return true; } return false; } SceVideodec2::Vp9PictureInfo sentinelVp9Info(std::uint64_t size = sizeof(SceVideodec2::Vp9PictureInfo)) { SceVideodec2::Vp9PictureInfo info; std::memset(&info, 0xcc, sizeof(info)); info.thisSize = size; return info; } bool sameBytes(const SceVideodec2::Vp9PictureInfo& left, const SceVideodec2::Vp9PictureInfo& right) { return std::memcmp(&left, &right, sizeof(SceVideodec2::Vp9PictureInfo)) == 0; } void checkVp9Info(const SceVideodec2::Vp9PictureInfo& info, std::size_t picture) { const std::string name = "VP9 picture " + std::to_string(picture); check(info.thisSize == sizeof(SceVideodec2::Vp9PictureInfo) && info.isValid, name + " has no picture info"); check(info.ptsData == 1000 + picture && info.dtsData == picture && info.attachedData == 0xa0 + picture, name + " timestamps differ"); check(info.profile == 0 && info.bitDepth == 8 && info.colorRange == 0 && info.colorSpace == 0 && info.numFrames == 1 && info.intraOnly == 0, name + " coded header fields differ"); check(info.keyFrameFlag == (picture == 0 ? 1u : 0u), name + " key frame flag differs"); check(info.frameWidth == Width && info.frameHeight == Height && info.renderWidth == Width && info.renderHeight == Height, name + " dimensions differ"); } void testVp9PictureInfo() { Vp9Session session; const auto units = ivfFrames(); check(units.size() == Vp9Hashes.size(), "unexpected VP9 access unit count"); std::array, Vp9Hashes.size()> buffers; for (auto& buffer : buffers) buffer.resize(session.memory.maxFrameBufferSize); std::array outputs{}; std::size_t pictures = 0; bool outputSet = false; bool ended = false; const auto drain = [&] { for (;;) { auto& target = buffers[pictures % buffers.size()]; if (!outputSet) { const FrameBuffer frame{sizeof(FrameBuffer), target.data(), target.size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "VP9 output buffer was rejected"); outputSet = true; } OutputInfo output{sizeof(OutputInfo)}; const auto status = sceVdecswTrySyncDecodeOutput(session.decoder, &output); if (status == OutputPending) return; check(status == 0, "VP9 output sync failed"); outputSet = false; if (!output.isValid) { ended = true; return; } check(pictures < Vp9Hashes.size(), "more VP9 pictures than access units"); check(output.codecType == CodecVp9 && output.pictureCount == 1 && output.frameWidth == Width && output.frameHeight == Height && output.frameBuffer == target.data(), "unexpected VP9 picture geometry"); auto info = sentinelVp9Info(); check(sceVdecswGetVp9PictureInfo(&output, &info) == 0, "VP9 picture info query failed"); checkVp9Info(info, pictures); check(hashNv12(target.data(), output.framePitch, output.frameHeight) == Vp9Hashes[pictures], "VP9 picture " + std::to_string(pictures) + " differs from the reference"); outputs[pictures] = output; ++pictures; } }; for (std::size_t unit = 0; unit < units.size(); ++unit) { const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), units[unit].data(), units[unit].size(), 1000 + unit, unit, 0xa0 + unit}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "VP9 decode input was rejected"); InputResult consumed{sizeof(InputResult)}; check(sceVdecswTrySyncDecodeInput(session.decoder, &consumed) == 0 && consumed.decodedAu == units[unit].data(), "a VP9 access unit was not consumed"); drain(); } check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "VP9 finalize failed"); while (!ended) drain(); check(pictures == Vp9Hashes.size(), "missing VP9 pictures after finalizing the sequence"); for (std::size_t picture = 0; picture < outputs.size(); ++picture) { auto info = sentinelVp9Info(); check(sceVdecswGetVp9PictureInfo(&outputs[picture], &info) == 0, "VP9 picture info of an earlier picture was lost"); checkVp9Info(info, picture); } const auto unchanged = sentinelVp9Info(); auto info = unchanged; check(throws([&] { sceVdecswGetVp9PictureInfo(nullptr, &info); }) && sameBytes(info, unchanged), "a null output info was accepted"); check(throws([&] { sceVdecswGetVp9PictureInfo(&outputs[0], nullptr); }), "a null VP9 picture info was accepted"); auto tooSmall = sentinelVp9Info(7); const auto tooSmallBefore = tooSmall; check(throws([&] { sceVdecswGetVp9PictureInfo(&outputs[0], &tooSmall); }) && sameBytes(tooSmall, tooSmallBefore), "a VP9 picture info smaller than its size field was accepted"); auto doubled = outputs[0]; doubled.pictureCount = 2; check(throws([&] { sceVdecswGetVp9PictureInfo(&doubled, &info); }) && sameBytes(info, unchanged), "an output with two pictures was accepted"); auto empty = outputs[0]; empty.pictureCount = 0; check(sceVdecswGetVp9PictureInfo(&empty, &info) == 0 && sameBytes(info, unchanged), "an output without a picture wrote a VP9 picture info"); std::uint8_t foreign = 0; auto unknown = outputs[0]; unknown.frameBuffer = &foreign; check(throws([&] { sceVdecswGetVp9PictureInfo(&unknown, &info); }) && sameBytes(info, unchanged), "a frame buffer no decoder returned was accepted"); unknown.frameBuffer = nullptr; check(throws([&] { sceVdecswGetVp9PictureInfo(&unknown, &info); }) && sameBytes(info, unchanged), "a null frame buffer was accepted"); const auto head = offsetof(SceVideodec2::Vp9PictureInfo, profile); auto partial = sentinelVp9Info(head); check(sceVdecswGetVp9PictureInfo(&outputs[2], &partial) == 0, "a VP9 picture info with a short size field was rejected"); check(partial.thisSize == head && partial.isValid && partial.ptsData == 1002 && partial.dtsData == 2 && partial.attachedData == 0xa2, "the leading VP9 picture info fields were not written"); check(partial.profile == unchanged.profile && partial.frameWidth == unchanged.frameWidth && partial.renderHeight == unchanged.renderHeight, "a VP9 picture info with a short size field was written beyond it"); struct Oversized { SceVideodec2::Vp9PictureInfo info; std::uint8_t tail[64]; } oversized; std::memset(&oversized, 0xcc, sizeof(oversized)); oversized.info.thisSize = sizeof(Oversized); check(sceVdecswGetVp9PictureInfo(&outputs[3], &oversized.info) == 0, "a VP9 picture info with a long size field was rejected"); check(oversized.info.thisSize == sizeof(Oversized), "a long VP9 picture info size field was changed"); oversized.info.thisSize = sizeof(SceVideodec2::Vp9PictureInfo); checkVp9Info(oversized.info, 3); for (const auto byte : oversized.tail) check(byte == 0xcc, "a VP9 picture info with a long size field was written beyond the structure"); AvcPictureInfo avc{sizeof(AvcPictureInfo)}; check(throws([&] { sceVdecswGetAvcPictureInfo(&outputs[0], &avc, nullptr); }), "AVC picture info was returned for a VP9 picture"); check(sceVdecswResetDecoder(session.decoder) == 0, "VP9 reset failed"); check(throws([&] { sceVdecswGetVp9PictureInfo(&outputs[0], &info); }) && sameBytes(info, unchanged), "VP9 picture info survived a decoder reset"); } void testVp9PictureInfoOfAvcPicture() { Session session; const auto units = accessUnits(false); const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), units[0].data(), units[0].size(), 1000, 0, 0xa0}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "decode input was rejected"); InputResult result{sizeof(InputResult)}; check(sceVdecswSyncDecodeInput(session.decoder, &result) == 0, "the access unit was not consumed"); std::vector buffer(session.memory.maxFrameBufferSize); const FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "output buffer was rejected"); check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "finalize failed"); OutputInfo output{sizeof(OutputInfo)}; check(sceVdecswSyncDecodeOutput(session.decoder, &output) == 0 && output.isValid && output.pictureCount == 1, "sync output did not return the finalized picture"); const auto unchanged = sentinelVp9Info(); auto info = unchanged; check(throws([&] { sceVdecswGetVp9PictureInfo(&output, &info); }) && sameBytes(info, unchanged), "VP9 picture info was returned for an H.264 picture"); } } void testHevcMain10() { Session session(CodecHevc, 2, 30); std::vector buffer(session.memory.maxFrameBufferSize); const FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size()}; check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "Main 10 HEVC output buffer was rejected"); const SceVideodec2::InputData input{sizeof(SceVideodec2::InputData), HevcMain10Stream.data(), HevcMain10Stream.size(), 1000, 0, 0xa0}; check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "Main 10 HEVC decode input was rejected"); InputResult consumed{sizeof(InputResult)}; check(sceVdecswSyncDecodeInput(session.decoder, &consumed) == 0, "the Main 10 HEVC access unit was not consumed"); check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "Main 10 HEVC finalize failed"); OutputInfo output{sizeof(OutputInfo)}; check(sceVdecswSyncDecodeOutput(session.decoder, &output) == 0 && output.isValid, "the Main 10 HEVC picture was not output"); HevcPictureInfo info{sizeof(HevcPictureInfo)}; check(sceVdecswGetHevcPictureInfo(&output, &info) == 0 && info.isValid, "Main 10 HEVC picture info missing"); check(info.generalProfileIdc == 2 && info.bitDepthLumaMinus8 == 2 && info.bitDepthChromaMinus8 == 2, "Main 10 HEVC profile or bit depth differs"); } int main() { try { testDecode(); testHevc(); testHevcCrop(); testHevcMain10(); testWithoutOutput(); testSyncWrappers(); testVp9PictureInfo(); testVp9PictureInfoOfAvcPicture(); std::puts("Vdecsw tests passed"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "%s\n", error.what()); return 1; } }