AnyPS5/core/libs/prx/libSceVdecsw/Export.cpp

390 lines
15 KiB
C++

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <vector>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr int VdecswErrorOutputPending = static_cast<int>(0x81510115u);
constexpr int VdecswErrorInputQueueEmpty = static_cast<int>(0x81510116u);
constexpr int VdecswErrorDecodePending = static_cast<int>(0x81510117u);
constexpr std::size_t MaxQueuedPictures = 4;
struct InputData {
std::uint64_t thisSize;
const std::uint8_t* auData;
std::uint64_t auSize;
std::uint64_t ptsData;
std::uint64_t dtsData;
std::uint64_t attachedData;
};
struct InputResult {
std::uint64_t thisSize;
const void* decodedAu;
std::uint32_t outputFrameCount;
std::uint32_t reserved;
};
struct FrameBuffer {
std::uint64_t thisSize;
void* frameBuffer;
std::uint64_t frameBufferSize;
};
struct OutputInfo {
std::uint64_t thisSize;
bool isValid;
bool isLastFrame;
bool isErrorFrame;
std::uint8_t pictureCount;
std::uint32_t codecType;
std::uint32_t frameWidth;
std::uint32_t framePitch;
std::uint32_t frameHeight;
bool isDiscardedFrame;
void* frameBuffer;
std::uint64_t frameBufferSize;
std::uint32_t frameFormat;
std::uint32_t framePitchInBytes;
};
struct AvcPictureInfo {
std::uint64_t thisSize;
bool isValid;
std::uint64_t ptsData;
std::uint64_t dtsData;
std::uint64_t attachedData;
std::uint8_t idrPictureFlag;
std::uint8_t profileIdc;
std::uint8_t levelIdc;
std::uint32_t picWidthInMbsMinus1;
std::uint32_t picHeightInMapUnitsMinus1;
std::uint8_t frameMbsOnlyFlag;
std::uint8_t frameCroppingFlag;
std::uint32_t frameCropLeftOffset;
std::uint32_t frameCropRightOffset;
std::uint32_t frameCropTopOffset;
std::uint32_t frameCropBottomOffset;
std::uint8_t vui[44];
};
static_assert(sizeof(InputData) == 0x30 && sizeof(InputResult) == 0x18 && sizeof(FrameBuffer) == 0x18);
static_assert(sizeof(OutputInfo) == 0x38 && offsetof(OutputInfo, isLastFrame) == 9 && offsetof(OutputInfo, pictureCount) == 0xb && offsetof(OutputInfo, isDiscardedFrame) == 0x1c);
static_assert(sizeof(AvcPictureInfo) == 0x78 && offsetof(AvcPictureInfo, picWidthInMbsMinus1) == 0x2c && offsetof(AvcPictureInfo, frameCropBottomOffset) == 0x44);
struct Videodec2FrameBuffer {
std::uint64_t thisSize;
void* frameBuffer;
std::uint64_t frameBufferSize;
bool isAccepted;
};
struct Videodec2OutputInfo {
std::uint64_t thisSize;
bool isValid;
bool isErrorFrame;
std::uint8_t pictureCount;
std::uint32_t codecType;
std::uint32_t frameWidth;
std::uint32_t framePitch;
std::uint32_t frameHeight;
void* frameBuffer;
std::uint64_t frameBufferSize;
std::uint32_t frameFormat;
std::uint32_t framePitchInBytes;
};
struct Videodec2AvcPictureInfo {
std::uint64_t thisSize;
bool isValid;
std::uint64_t ptsData;
std::uint64_t dtsData;
std::uint64_t attachedData;
std::uint8_t idrPictureFlag;
std::uint8_t profileIdc;
std::uint8_t levelIdc;
std::uint32_t picWidthInLumaSamples;
std::uint32_t picHeightInLumaSamples;
};
struct DecoderMemoryInfo {
std::uint64_t thisSize;
std::uint64_t cpuMemorySize;
void* cpuMemory;
std::uint64_t gpuMemorySize;
void* gpuMemory;
std::uint64_t cpuGpuMemorySize;
void* cpuGpuMemory;
std::uint64_t maxFrameBufferSize;
std::uint32_t frameBufferAlignment;
};
}
extern "C" {
int APS5_VABI sceVideodec2QueryComputeMemoryInfo_nid_postfix(void* info);
int APS5_VABI sceVideodec2AllocateComputeQueue_nid_postfix(const void* config, const void* memory, std::uint64_t* queue);
int APS5_VABI sceVideodec2ReleaseComputeQueue_nid_postfix(std::uint64_t queue);
int APS5_VABI sceVideodec2QueryDecoderMemoryInfo_nid_postfix(const void* config, void* memory);
int APS5_VABI sceVideodec2CreateDecoder_nid_postfix(const void* config, const void* memory, std::uint64_t* handle);
int APS5_VABI sceVideodec2DeleteDecoder_nid_postfix(std::uint64_t handle);
int APS5_VABI sceVideodec2Decode_nid_postfix(std::uint64_t handle, const InputData* input, Videodec2FrameBuffer* frame, Videodec2OutputInfo* output);
int APS5_VABI sceVideodec2Flush_nid_postfix(std::uint64_t handle, Videodec2FrameBuffer* frame, Videodec2OutputInfo* output);
int APS5_VABI sceVideodec2Reset_nid_postfix(std::uint64_t handle);
int APS5_VABI sceVideodec2GetAvcPictureInfo_nid_postfix(const Videodec2OutputInfo* output, Videodec2AvcPictureInfo* first, Videodec2AvcPictureInfo* second);
}
namespace {
struct PendingInput {
InputData input;
std::vector<std::uint8_t> data;
};
struct ConsumedInput {
const void* au;
std::uint32_t frames;
};
struct Picture {
std::vector<std::uint8_t> pixels;
Videodec2OutputInfo output;
AvcPictureInfo info;
};
struct Decoder {
std::mutex mutex;
std::uint64_t frameBytes = 0;
std::deque<PendingInput> inputs;
std::deque<ConsumedInput> consumed;
std::deque<FrameBuffer> outputs;
std::deque<Picture> pictures;
std::map<const void*, AvcPictureInfo> delivered;
bool finalized = false;
bool drained = false;
};
std::mutex decodersMutex;
std::map<std::uint64_t, std::shared_ptr<Decoder>> decoders;
std::shared_ptr<Decoder> findDecoder(std::uint64_t handle) {
std::lock_guard lock(decodersMutex);
const auto found = decoders.find(handle);
if (found == decoders.end()) throw std::runtime_error("Vdecsw: invalid decoder handle");
return found->second;
}
AvcPictureInfo pictureInfo(const Videodec2OutputInfo& output) {
Videodec2AvcPictureInfo source{sizeof(Videodec2AvcPictureInfo)};
sceVideodec2GetAvcPictureInfo_nid_postfix(&output, &source, nullptr);
if (!source.isValid) throw std::runtime_error("Vdecsw: decoded picture has no picture info");
const auto widthInMbs = (output.frameWidth + 15u) / 16u;
const auto heightInMbs = (output.frameHeight + 15u) / 16u;
AvcPictureInfo info{};
info.thisSize = sizeof(AvcPictureInfo);
info.isValid = true;
info.ptsData = source.ptsData;
info.dtsData = source.dtsData;
info.attachedData = source.attachedData;
info.idrPictureFlag = source.idrPictureFlag;
info.profileIdc = source.profileIdc;
info.levelIdc = source.levelIdc;
info.picWidthInMbsMinus1 = widthInMbs - 1u;
info.picHeightInMapUnitsMinus1 = heightInMbs - 1u;
info.frameMbsOnlyFlag = 1;
info.frameCroppingFlag = widthInMbs * 16u != output.frameWidth || heightInMbs * 16u != output.frameHeight;
info.frameCropRightOffset = (widthInMbs * 16u - output.frameWidth) / 2u;
info.frameCropBottomOffset = (heightInMbs * 16u - output.frameHeight) / 2u;
return info;
}
void advance(std::uint64_t handle, Decoder& decoder) {
while (decoder.pictures.size() < MaxQueuedPictures) {
std::vector<std::uint8_t> pixels(decoder.frameBytes);
Videodec2FrameBuffer frame{sizeof(Videodec2FrameBuffer), pixels.data(), pixels.size(), false};
Videodec2OutputInfo output{sizeof(Videodec2OutputInfo)};
if (!decoder.inputs.empty()) {
const auto* au = static_cast<const void*>(decoder.inputs.front().input.auData);
auto input = decoder.inputs.front().input;
input.auData = decoder.inputs.front().data.data();
sceVideodec2Decode_nid_postfix(handle, &input, &frame, &output);
decoder.inputs.pop_front();
decoder.consumed.push_back({au, output.isValid ? 1u : 0u});
} else if (decoder.finalized && !decoder.drained) {
sceVideodec2Flush_nid_postfix(handle, &frame, &output);
decoder.drained = !output.isValid;
} else {
return;
}
if (output.isValid) {
const auto info = pictureInfo(output);
decoder.pictures.push_back({std::move(pixels), output, info});
}
}
}
}
extern "C" {
int APS5_VABI sceVdecswQueryComputeMemoryInfo(void* info) {
return sceVideodec2QueryComputeMemoryInfo_nid_postfix(info);
}
int APS5_VABI sceVdecswAllocateComputeQueue(const void* config, const void* memory, std::uint64_t* queue) {
return sceVideodec2AllocateComputeQueue_nid_postfix(config, memory, queue);
}
int APS5_VABI sceVdecswReleaseComputeQueue(std::uint64_t queue) {
return sceVideodec2ReleaseComputeQueue_nid_postfix(queue);
}
int APS5_VABI sceVdecswQueryDecoderMemoryInfo(const void* config, void* memory) {
return sceVideodec2QueryDecoderMemoryInfo_nid_postfix(config, memory);
}
int APS5_VABI sceVdecswCreateDecoder(const void* config, const DecoderMemoryInfo* memory, std::uint64_t* handle) {
if (handle == nullptr || memory == nullptr || memory->thisSize < sizeof(DecoderMemoryInfo) || memory->maxFrameBufferSize == 0) throw std::runtime_error("Vdecsw: invalid decoder creation arguments");
const auto result = sceVideodec2CreateDecoder_nid_postfix(config, memory, handle);
if (result != 0) return result;
auto decoder = std::make_shared<Decoder>();
decoder->frameBytes = memory->maxFrameBufferSize;
std::lock_guard lock(decodersMutex);
decoders.emplace(*handle, std::move(decoder));
return 0;
}
int APS5_VABI sceVdecswDeleteDecoder(std::uint64_t handle) {
{
std::lock_guard lock(decodersMutex);
if (decoders.erase(handle) == 0) throw std::runtime_error("Vdecsw: invalid decoder handle");
}
return sceVideodec2DeleteDecoder_nid_postfix(handle);
}
int APS5_VABI sceVdecswSetDecodeInput(std::uint64_t handle, const InputData* input) {
if (input == nullptr || input->thisSize < sizeof(InputData)) throw std::runtime_error("Vdecsw: invalid decode input");
const auto decoder = findDecoder(handle);
std::lock_guard lock(decoder->mutex);
if (decoder->finalized) throw std::runtime_error("Vdecsw: decode input after the sequence was finalized");
if (input->auData == nullptr || input->auSize == 0) throw std::runtime_error("Vdecsw: empty access unit");
decoder->inputs.push_back({*input, std::vector<std::uint8_t>(input->auData, input->auData + input->auSize)});
advance(handle, *decoder);
return 0;
}
int APS5_VABI sceVdecswTrySyncDecodeInput(std::uint64_t handle, InputResult* result) {
if (result == nullptr || result->thisSize < sizeof(InputResult)) throw std::runtime_error("Vdecsw: invalid decode input result");
const auto decoder = findDecoder(handle);
std::lock_guard lock(decoder->mutex);
advance(handle, *decoder);
result->decodedAu = nullptr;
result->outputFrameCount = 0;
result->reserved = 0;
if (!decoder->consumed.empty()) {
result->decodedAu = decoder->consumed.front().au;
result->outputFrameCount = decoder->consumed.front().frames;
decoder->consumed.pop_front();
return 0;
}
return decoder->inputs.empty() ? VdecswErrorInputQueueEmpty : VdecswErrorDecodePending;
}
int APS5_VABI sceVdecswSetDecodeOutput(std::uint64_t handle, const FrameBuffer* frame) {
if (frame == nullptr || frame->thisSize < sizeof(FrameBuffer) || frame->frameBuffer == nullptr) throw std::runtime_error("Vdecsw: invalid decode output");
const auto decoder = findDecoder(handle);
std::lock_guard lock(decoder->mutex);
decoder->outputs.push_back(*frame);
return 0;
}
int APS5_VABI sceVdecswTrySyncDecodeOutput(std::uint64_t handle, OutputInfo* output) {
if (output == nullptr || output->thisSize < sizeof(OutputInfo)) throw std::runtime_error("Vdecsw: invalid decode output info");
const auto decoder = findDecoder(handle);
std::lock_guard lock(decoder->mutex);
advance(handle, *decoder);
if (decoder->outputs.empty()) throw std::runtime_error("Vdecsw: output sync without an output buffer");
const bool ended = decoder->finalized && decoder->drained;
if (decoder->pictures.size() < (ended ? 1u : 2u)) {
if (!ended) return VdecswErrorOutputPending;
std::memset(reinterpret_cast<std::uint8_t*>(output) + sizeof(output->thisSize), 0, sizeof(OutputInfo) - sizeof(output->thisSize));
output->isLastFrame = true;
decoder->outputs.pop_front();
return 0;
}
const auto target = decoder->outputs.front();
auto picture = std::move(decoder->pictures.front());
decoder->pictures.pop_front();
if (target.frameBufferSize < picture.output.frameBufferSize) throw std::runtime_error("Vdecsw: output buffer is smaller than the decoded picture");
std::memcpy(target.frameBuffer, picture.pixels.data(), picture.output.frameBufferSize);
decoder->outputs.pop_front();
output->isValid = true;
output->isLastFrame = ended && decoder->pictures.empty();
output->isErrorFrame = picture.output.isErrorFrame;
output->pictureCount = picture.output.pictureCount;
output->codecType = picture.output.codecType;
output->frameWidth = picture.output.frameWidth;
output->framePitch = picture.output.framePitch;
output->frameHeight = picture.output.frameHeight;
output->isDiscardedFrame = false;
output->frameBuffer = target.frameBuffer;
output->frameBufferSize = picture.output.frameBufferSize;
output->frameFormat = picture.output.frameFormat;
output->framePitchInBytes = picture.output.framePitchInBytes;
decoder->delivered[target.frameBuffer] = picture.info;
return 0;
}
int APS5_VABI sceVdecswGetAvcPictureInfo(const OutputInfo* output, AvcPictureInfo* first, AvcPictureInfo* second) {
if (output == nullptr || first == nullptr || first->thisSize < sizeof(std::uint64_t)) throw std::runtime_error("Vdecsw: invalid argument pointer");
if (output->pictureCount > 1) throw std::runtime_error("Vdecsw: second field picture info is not implemented");
if (output->pictureCount == 0) return 0;
static_cast<void>(second);
std::vector<std::shared_ptr<Decoder>> all;
{
std::lock_guard lock(decodersMutex);
for (const auto& [handle, decoder] : decoders) all.push_back(decoder);
}
for (const auto& decoder : all) {
std::lock_guard lock(decoder->mutex);
const auto found = decoder->delivered.find(output->frameBuffer);
if (found == decoder->delivered.end()) continue;
const auto bytes = std::min<std::uint64_t>(first->thisSize, sizeof(AvcPictureInfo)) - sizeof(std::uint64_t);
std::memcpy(reinterpret_cast<std::uint8_t*>(first) + sizeof(std::uint64_t), reinterpret_cast<const std::uint8_t*>(&found->second) + sizeof(std::uint64_t), bytes);
return 0;
}
throw std::runtime_error("Vdecsw: picture info requested for a frame buffer no decoder returned");
}
int APS5_VABI sceVdecswFinalizeDecodeSequence(std::uint64_t handle) {
const auto decoder = findDecoder(handle);
std::lock_guard lock(decoder->mutex);
decoder->finalized = true;
advance(handle, *decoder);
return 0;
}
int APS5_VABI sceVdecswResetDecoder(std::uint64_t handle) {
const auto decoder = findDecoder(handle);
std::lock_guard lock(decoder->mutex);
decoder->inputs.clear();
decoder->consumed.clear();
decoder->outputs.clear();
decoder->pictures.clear();
decoder->delivered.clear();
decoder->finalized = false;
decoder->drained = false;
return sceVideodec2Reset_nid_postfix(handle);
}
}