diff --git a/core/libs/prx/libSceVideodec2/Export.cpp b/core/libs/prx/libSceVideodec2/Export.cpp index 28bea7762..a4d91f714 100644 --- a/core/libs/prx/libSceVideodec2/Export.cpp +++ b/core/libs/prx/libSceVideodec2/Export.cpp @@ -16,6 +16,7 @@ extern "C" { #include #include +#include #include } @@ -24,6 +25,9 @@ namespace { constexpr std::uint64_t WorkMemoryBytes = 1u << 20u; constexpr std::uint32_t PitchAlignment = 256; constexpr std::uint32_t HeightAlignment = 32; +constexpr std::uint32_t CodecAvc = 1; +constexpr std::uint32_t CodecHevc = 974921; +constexpr std::uint32_t CodecVp9 = 2382845; struct ComputeMemoryInfo { std::uint64_t thisSize; @@ -174,9 +178,18 @@ std::shared_ptr FindDecoder(std::uint64_t handle) { return found != decoders.end() ? found->second : nullptr; } +AVCodecID CodecId(std::uint32_t codec) { + switch (codec) { + case CodecAvc: return AV_CODEC_ID_H264; + case CodecHevc: return AV_CODEC_ID_HEVC; + case CodecVp9: return AV_CODEC_ID_VP9; + default: throw std::runtime_error("Videodec2: unsupported codec"); + } +} + void OpenCodec(Decoder& decoder) { - const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); - if (codec == nullptr) throw std::runtime_error("Videodec2: H.264 decoder is unavailable"); + const AVCodec* codec = avcodec_find_decoder(CodecId(decoder.codec)); + if (codec == nullptr) throw std::runtime_error("Videodec2: decoder is unavailable"); decoder.context = avcodec_alloc_context3(codec); if (decoder.context == nullptr) throw std::runtime_error("Videodec2: cannot allocate codec context"); decoder.context->err_recognition = AV_EF_EXPLODE; @@ -184,7 +197,7 @@ void OpenCodec(Decoder& decoder) { decoder.context->thread_type = FF_THREAD_SLICE; if (avcodec_open2(decoder.context, codec, nullptr) < 0) { avcodec_free_context(&decoder.context); - throw std::runtime_error("Videodec2: cannot open H.264 decoder"); + throw std::runtime_error("Videodec2: cannot open decoder"); } } @@ -233,6 +246,8 @@ bool WriteNv12(Decoder& decoder, const AVFrame& frame, std::uint8_t* target, std std::uint8_t* planes[4] = {target, target + luma, nullptr, nullptr}; const int strides[4] = {static_cast(pitch), static_cast(pitch), 0, 0}; const auto format = static_cast(frame.format); + const AVPixFmtDescriptor* descriptor = av_pix_fmt_desc_get(format); + if (descriptor == nullptr || descriptor->comp[0].depth > 8) throw std::runtime_error("Videodec2: output deeper than 8 bits is not implemented"); if (format == AV_PIX_FMT_YUV420P || format == AV_PIX_FMT_YUVJ420P) { for (std::uint32_t row = 0; row < height; ++row) std::memcpy(planes[0] + static_cast(row) * pitch, frame.data[0] + static_cast(row) * frame.linesize[0], width); for (std::uint32_t row = 0; row < height / 2; ++row) { @@ -329,14 +344,14 @@ int APS5_VABI sceVideodec2QueryDecoderMemoryInfo_nid_postfix(const DecoderConfig int APS5_VABI sceVideodec2CreateDecoder_nid_postfix(const DecoderConfigInfo* config, const DecoderMemoryInfo* memory, std::uint64_t* handle) { if (!config || !memory || !handle) throw std::runtime_error("Videodec2: invalid argument pointer"); - if (config->codecType != 1) throw std::runtime_error("Videodec2: unsupported codec"); + CodecId(config->codecType); auto decoder = std::make_shared(); decoder->codec = config->codecType; OpenCodec(*decoder); std::lock_guard guard(lock); *handle = nextDecoder++; decoders.emplace(*handle, decoder); - std::fprintf(stderr, "[videodec2] decoder %llu: codec=%u max %dx%d (%s)\n", static_cast(*handle), config->codecType, config->maxFrameWidth, config->maxFrameHeight, "H.264 through FFmpeg"); + std::fprintf(stderr, "[videodec2] decoder %llu: codec=%u max %dx%d (%s)\n", static_cast(*handle), config->codecType, config->maxFrameWidth, config->maxFrameHeight, decoder->context->codec->long_name); return 0; } @@ -357,7 +372,8 @@ int APS5_VABI sceVideodec2Decode_nid_postfix(std::uint64_t handle, const InputDa const Picture picture{input->ptsData, input->dtsData, input->attachedData}; if (decoder.flushing) throw std::runtime_error("Videodec2: reset required after flushing"); if (!input->auData || input->auSize == 0 || input->auSize > static_cast(std::numeric_limits::max())) throw std::runtime_error("Videodec2: invalid access unit"); - const auto* unit = ToAnnexB(decoder, input->auData, static_cast(input->auSize)); + if (decoder.codec == CodecVp9) decoder.annexB.assign(input->auData, input->auData + input->auSize); + const auto* unit = decoder.codec == CodecVp9 ? &decoder.annexB : ToAnnexB(decoder, input->auData, static_cast(input->auSize)); AVPacket* packet = av_packet_alloc(); if (!packet) throw std::runtime_error("Videodec2: cannot allocate packet"); const auto freePacket = [](AVPacket* value) { av_packet_free(&value); }; @@ -410,6 +426,7 @@ int APS5_VABI sceVideodec2GetPictureInfo_nid_postfix(const OutputInfo* output, A std::lock_guard guard(decoder->mutex); const auto found = decoder->pictures.find(output->frameBuffer); if (found == decoder->pictures.end()) continue; + if (decoder->codec != CodecAvc) throw std::runtime_error("Videodec2: picture info is only implemented for H.264"); const auto& picture = found->second; first->isValid = true; first->ptsData = picture.pts; diff --git a/core/libs/tests/GuestVideodec2.cpp b/core/libs/tests/GuestVideodec2.cpp index 89e1d2328..a0fac8c59 100644 --- a/core/libs/tests/GuestVideodec2.cpp +++ b/core/libs/tests/GuestVideodec2.cpp @@ -194,6 +194,178 @@ constexpr std::array PictureHashes{ constexpr std::array DisplayOrderUnits{0, 2, 3, 1, 5, 4}; +constexpr std::uint32_t CodecHevc = 974921; +constexpr std::uint32_t CodecVp9 = 2382845; + +constexpr std::array HevcStream{ + 0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, + 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x1e, + 0x95, 0x90, 0x09, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, + 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x1e, 0xa0, 0x20, 0x82, 0x96, 0x59, 0x59, 0x2e, + 0x4c, 0xaf, 0x01, 0x68, 0x08, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0xf0, 0x40, + 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc1, 0x73, 0xd0, 0x89, 0x00, 0x00, 0x01, 0x28, 0x01, 0xaf, + 0x08, 0xe0, 0xbb, 0xcb, 0xc1, 0x71, 0x26, 0x0f, 0xbf, 0x6c, 0xcb, 0x4d, 0x2b, 0x2c, 0xf4, 0x08, + 0xe0, 0x17, 0xeb, 0x16, 0xcc, 0x07, 0xd8, 0xdd, 0x11, 0xf8, 0x5e, 0x73, 0x79, 0x57, 0xcb, 0x69, + 0xa3, 0x1c, 0x9c, 0x43, 0x14, 0xdb, 0x61, 0x0e, 0xc5, 0xc6, 0x9c, 0x9c, 0xf8, 0xf2, 0x07, 0x35, + 0xf1, 0x98, 0x6c, 0x1a, 0x86, 0x53, 0x5a, 0xcd, 0x4d, 0xda, 0xff, 0xfb, 0x01, 0xda, 0xc3, 0x61, + 0xb7, 0x8c, 0xae, 0xc9, 0x76, 0xe4, 0x05, 0xd6, 0xfd, 0xe4, 0xcd, 0x93, 0x2c, 0xb1, 0x41, 0x29, + 0xce, 0x22, 0xf7, 0x77, 0x64, 0x0f, 0x1f, 0xd2, 0xc3, 0x68, 0x32, 0x2f, 0x83, 0xd7, 0x53, 0xbe, + 0x23, 0xff, 0x8c, 0x46, 0x18, 0x33, 0x90, 0x28, 0xa3, 0xef, 0xd7, 0xb1, 0x46, 0x84, 0xa0, 0xc2, + 0xd4, 0xd9, 0xce, 0xdf, 0x0a, 0x05, 0xec, 0xad, 0x63, 0xa4, 0x78, 0x27, 0x80, 0x4e, 0x3b, 0x4c, + 0xa6, 0x42, 0xbc, 0xe3, 0x39, 0x4d, 0xc4, 0xd3, 0x44, 0xf1, 0x22, 0xd0, 0x64, 0x9f, 0xd0, 0x88, + 0x7f, 0x39, 0x6d, 0x06, 0xc7, 0x79, 0x75, 0x2a, 0x44, 0x3d, 0x21, 0x41, 0x53, 0x60, 0x8d, 0x08, + 0x9f, 0xd0, 0x34, 0xa6, 0x72, 0x70, 0x3b, 0xa9, 0xfa, 0x62, 0xb1, 0x19, 0x9b, 0x0b, 0xa2, 0xfc, + 0xb4, 0xb3, 0x2c, 0x58, 0xad, 0xc6, 0xf3, 0xea, 0xc4, 0xd0, 0xf9, 0xea, 0x9e, 0xa7, 0xa4, 0x87, + 0xb8, 0x19, 0x68, 0xf1, 0xc1, 0x6b, 0x87, 0xa5, 0x99, 0xa0, 0x69, 0x24, 0x28, 0x80, 0xba, 0xd2, + 0x57, 0xc6, 0xc0, 0x1e, 0x69, 0x1b, 0xce, 0x96, 0xcd, 0x85, 0x9d, 0x59, 0x32, 0x92, 0x71, 0xe7, + 0x15, 0xf2, 0x9b, 0x71, 0x4b, 0xd4, 0xb6, 0x99, 0xb0, 0x71, 0x9b, 0x9b, 0xdb, 0x33, 0x28, 0x86, + 0x5e, 0x44, 0xf3, 0x5d, 0x5d, 0x7b, 0x87, 0x6c, 0x99, 0x8a, 0x6d, 0xaf, 0xf8, 0xdd, 0x39, 0x14, + 0xc0, 0x10, 0x85, 0xaf, 0x3a, 0x77, 0x60, 0x8b, 0xb7, 0x5f, 0x67, 0x45, 0xb4, 0xa7, 0xe7, 0x44, + 0x77, 0x0a, 0xb9, 0xe1, 0xb2, 0xbe, 0xde, 0x1b, 0x5a, 0xb8, 0xc8, 0x9b, 0x1f, 0x15, 0x37, 0xdc, + 0xeb, 0x55, 0x12, 0x37, 0xa1, 0xab, 0x9c, 0xcd, 0xdd, 0x42, 0x5d, 0x90, 0x89, 0xc5, 0x99, 0x54, + 0x6e, 0x9a, 0xd2, 0x71, 0x07, 0xe3, 0x81, 0x20, 0xad, 0xd4, 0x7c, 0xad, 0x69, 0xaf, 0xc7, 0x2d, + 0x7c, 0x84, 0xf1, 0x5d, 0x43, 0x4a, 0x3c, 0xf6, 0xf8, 0x7e, 0x6a, 0xb2, 0xdd, 0x00, 0xda, 0x30, + 0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x30, 0x00, 0x00, 0x01, 0x02, 0x01, 0xd0, 0x19, 0x5f, 0x84, + 0x31, 0x84, 0x50, 0x62, 0xcd, 0x44, 0xa6, 0x4e, 0xbb, 0x2e, 0x87, 0xda, 0x21, 0x25, 0x1c, 0x13, + 0xfe, 0xc2, 0x97, 0xa3, 0xd5, 0xfd, 0xe2, 0x62, 0xd6, 0xe5, 0x50, 0x7a, 0xda, 0x98, 0x8d, 0xcf, + 0xbe, 0x40, 0x5d, 0x69, 0xb6, 0x5f, 0xdc, 0x32, 0x68, 0x05, 0x67, 0x70, 0xfd, 0xa0, 0xd3, 0xcd, + 0x88, 0xeb, 0x12, 0x2e, 0xac, 0xf5, 0x57, 0xa2, 0xc2, 0x81, 0x22, 0x6b, 0x8d, 0x69, 0x6d, 0xac, + 0x41, 0x05, 0x5b, 0x0b, 0x79, 0xe0, 0x86, 0x05, 0x86, 0x8b, 0x02, 0x51, 0x7d, 0x6e, 0x13, 0x63, + 0x36, 0xe6, 0x89, 0x22, 0x75, 0xd9, 0xb0, 0x39, 0x27, 0xcd, 0xac, 0x78, 0xe0, 0x3a, 0x6a, 0x17, + 0x15, 0x05, 0x83, 0x3a, 0xe9, 0x38, 0x7c, 0x75, 0x38, 0xf3, 0xd8, 0x42, 0x57, 0xa1, 0xef, 0x68, + 0xb8, 0xb5, 0x44, 0xfe, 0x4d, 0x66, 0xa1, 0x09, 0xc5, 0x62, 0xff, 0x87, 0x7e, 0xdb, 0x41, 0x6f, + 0x4d, 0x5f, 0x16, 0x49, 0xef, 0xca, 0xda, 0x67, 0x01, 0x29, 0x0b, 0xd6, 0xd8, 0xfc, 0x0b, 0x22, + 0xc8, 0x28, 0xff, 0xb4, 0xb3, 0xa9, 0xf8, 0x9b, 0x78, 0x7d, 0x33, 0xbf, 0xa6, 0x9b, 0x39, 0x64, + 0x57, 0x41, 0x1f, 0x27, 0x05, 0xfc, 0xcd, 0x44, 0xcd, 0xb2, 0xea, 0x79, 0xe0, 0x00, 0x00, 0x00, + 0x01, 0x46, 0x01, 0x50, 0x00, 0x00, 0x01, 0x02, 0x01, 0xe0, 0x44, 0x97, 0xe1, 0x8f, 0x40, 0x3d, + 0x73, 0xde, 0xdf, 0xf5, 0x1c, 0xff, 0xf6, 0xe2, 0x12, 0x16, 0x3d, 0xb6, 0x75, 0xad, 0x9e, 0xb5, + 0x2b, 0xe7, 0x4a, 0x40, 0xe9, 0x91, 0xd6, 0x86, 0x86, 0x2d, 0xcb, 0xe4, 0xb6, 0x58, 0x4a, 0x2d, + 0x7b, 0x1b, 0xdb, 0xc5, 0x48, 0x2d, 0x09, 0x4d, 0x32, 0x30, 0xde, 0xaa, 0x61, 0xc2, 0x27, 0x64, + 0x54, 0x71, 0x89, 0x32, 0x05, 0xc0, 0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x50, 0x00, 0x00, 0x01, + 0x00, 0x01, 0xe0, 0x24, 0xff, 0xfa, 0x2c, 0x6e, 0xdc, 0x78, 0x3d, 0x08, 0x6d, 0x3b, 0xcf, 0xcc, + 0xb6, 0x16, 0xeb, 0x9d, 0x5f, 0x46, 0x0b, 0x24, 0x0e, 0x97, 0x99, 0x06, 0x00, 0x00, 0x00, 0x01, + 0x46, 0x01, 0x30, 0x00, 0x00, 0x01, 0x02, 0x01, 0xd0, 0x28, 0x95, 0xd7, 0xdc, 0x41, 0xc0, 0x61, + 0x14, 0xd6, 0xee, 0xd4, 0xe8, 0x24, 0x3e, 0x7f, 0x82, 0x22, 0xb0, 0x20, 0xb9, 0xf1, 0x69, 0x9b, + 0xfe, 0xcc, 0xb7, 0x2e, 0xbd, 0xdb, 0x20, 0xfa, 0xe5, 0x97, 0xd9, 0x95, 0x1d, 0x7e, 0x49, 0x35, + 0x69, 0xdb, 0x40, 0xa3, 0x5c, 0x0f, 0x74, 0x6f, 0x00, 0x3d, 0x36, 0x05, 0xc0, 0xc4, 0x37, 0x15, + 0xf9, 0x01, 0x2d, 0xdf, 0xcb, 0x70, 0x3d, 0xfa, 0x47, 0xec, 0x80, 0x07, 0x48, 0x08, 0xc8, 0x54, + 0xd3, 0xe3, 0x00, 0xa3, 0xe4, 0xce, 0x40, 0x05, 0x0b, 0x51, 0x73, 0x56, 0x33, 0x82, 0xa8, 0x0a, + 0xc0, 0xd3, 0xc5, 0xf1, 0xeb, 0x32, 0x8e, 0x4e, 0xa7, 0x6f, 0xb2, 0x64, 0x43, 0x54, 0x71, 0xd4, + 0xaf, 0xc0, 0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x50, 0x00, 0x00, 0x01, 0x00, 0x01, 0xe0, 0x82, + 0x2f, 0x5f, 0xdc, 0x63, 0x70, 0x3f, 0xaf, 0x66, 0x75, 0x52, 0xe7, 0x7f, 0x62, 0xfb, 0x84, 0x9c, + 0xef, 0x60, 0x8f, 0xfa, 0x7e, 0x5f, 0x8b, 0x40, 0x15, 0x34, 0x69, 0xe2, 0xc4, 0xfc, 0xff, 0x53, + 0xb8, 0x1e, 0xc7, 0x20, 0x55, 0xdb, 0x5e, 0x05, 0xbd, 0x7a, 0x85, 0xb4, 0xb7, 0xc1, 0x8d, 0xdb, + 0x0f, 0xa6, 0xab, 0xb9, 0x00, 0x2a, 0xdb, 0x83, 0xa0, 0xb0, 0xc5, 0x76, 0x6e, 0xe1, 0xb8, 0x72, + 0xfd, 0x09, 0x61, 0x19, 0xe0, +}; + +constexpr std::array HevcHashes{ + 0x7291c31ee57c5065ull, 0x37b15adc47810098ull, 0xc84737b65a65b247ull, + 0xe39bc891f8286267ull, 0x734caf1ba961b3c2ull, 0x7deb44bd1ed30531ull, +}; + +constexpr std::array Vp9Stream{ + 0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x39, 0x30, 0x40, 0x00, 0x28, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x49, 0x83, 0x42, + 0x00, 0x03, 0xf0, 0x02, 0x76, 0x08, 0x38, 0x24, 0x1c, 0x18, 0x0e, 0x00, 0x01, 0xc0, 0x6f, 0xb1, + 0xfb, 0x17, 0xa6, 0xd2, 0x0f, 0x5f, 0xee, 0xf7, 0xec, 0xbf, 0xb4, 0x0c, 0x93, 0xbe, 0x87, 0x11, + 0x35, 0x1c, 0x51, 0x84, 0x8b, 0x70, 0xad, 0xe4, 0x00, 0x00, 0x79, 0x3b, 0xff, 0xff, 0xf5, 0x94, + 0xad, 0x57, 0x6e, 0xae, 0x8e, 0x1b, 0xfe, 0x64, 0xff, 0xc9, 0x64, 0xba, 0xca, 0x1d, 0x7f, 0x45, + 0x16, 0x95, 0x13, 0x8d, 0xd0, 0x61, 0x19, 0x80, 0xb7, 0xdf, 0xff, 0xff, 0xd6, 0x52, 0x1e, 0x7b, + 0x3c, 0x16, 0x3f, 0xff, 0xfb, 0x52, 0xe7, 0x26, 0xab, 0xd6, 0x3a, 0x6a, 0xff, 0xf8, 0x1b, 0x47, + 0xcd, 0xe2, 0xf5, 0x48, 0x65, 0x1e, 0x9f, 0x5c, 0x1a, 0x74, 0x0a, 0x99, 0xc0, 0x04, 0x41, 0x02, + 0xa9, 0x86, 0x36, 0x1f, 0xff, 0xf3, 0x4f, 0x71, 0xdf, 0x8a, 0xcd, 0x8a, 0x88, 0xdd, 0x37, 0xa2, + 0x27, 0x3a, 0x20, 0xb8, 0xf0, 0x4f, 0x67, 0x59, 0x8a, 0x82, 0xa2, 0xde, 0x08, 0xe3, 0xae, 0x87, + 0xff, 0xf0, 0x36, 0x8f, 0x9b, 0xc5, 0xea, 0x90, 0xca, 0x3d, 0x3e, 0xb8, 0x34, 0xe8, 0x15, 0x33, + 0x80, 0x08, 0x82, 0x05, 0x53, 0x0c, 0x6c, 0x3f, 0xff, 0xe6, 0x9e, 0xe3, 0xbf, 0x15, 0x9d, 0xec, + 0xdf, 0xe9, 0xce, 0x60, 0x88, 0x09, 0x48, 0x81, 0xa2, 0xb6, 0x27, 0x3e, 0xf9, 0xa9, 0x7e, 0xa7, + 0xe1, 0x9d, 0xdd, 0x3a, 0xa0, 0x39, 0x3f, 0x8a, 0xba, 0x73, 0x50, 0xde, 0xcf, 0x29, 0x5c, 0x2a, + 0x92, 0x01, 0xec, 0x06, 0xbe, 0x8b, 0x1e, 0x14, 0x34, 0x6b, 0xb1, 0x3a, 0x01, 0x47, 0x08, 0xe7, + 0xbe, 0xb0, 0xa7, 0x03, 0x77, 0xeb, 0xf0, 0xed, 0xd8, 0xa1, 0x4e, 0x7c, 0xd9, 0xe4, 0x7c, 0x9d, + 0x67, 0xfe, 0x36, 0xab, 0x5e, 0xa9, 0x5e, 0x81, 0x74, 0x68, 0xa1, 0x4b, 0x61, 0x81, 0xe2, 0xe4, + 0x4c, 0x6a, 0x4f, 0xe5, 0x65, 0x3f, 0x4c, 0x78, 0xea, 0x68, 0x5c, 0xf9, 0xc6, 0x58, 0x1f, 0x58, + 0xe0, 0x40, 0x4b, 0x88, 0x6d, 0x15, 0x94, 0x8c, 0x83, 0x4a, 0xa3, 0xf3, 0xd5, 0x80, 0x7f, 0x83, + 0x52, 0x7d, 0xbe, 0xa6, 0x9f, 0xb4, 0x89, 0xda, 0xb6, 0x5a, 0xdf, 0x1f, 0x78, 0xd8, 0xbf, 0xfe, + 0x7f, 0x52, 0xfa, 0xfd, 0xa4, 0xeb, 0xda, 0x91, 0x9c, 0xb6, 0xd0, 0xc3, 0xfd, 0x8e, 0x04, 0x04, + 0xb8, 0x86, 0xd1, 0x56, 0xa2, 0x24, 0xe6, 0xbc, 0x5b, 0xbf, 0x82, 0x36, 0xb5, 0xa7, 0x87, 0x73, + 0xd8, 0xbd, 0x30, 0x33, 0x3f, 0x7c, 0xbb, 0x4f, 0x70, 0xed, 0x3f, 0x8d, 0x2b, 0xe2, 0xce, 0x62, + 0x09, 0xfa, 0x04, 0x31, 0x67, 0xff, 0x09, 0xc1, 0x5c, 0x29, 0xf8, 0x77, 0xfa, 0xb6, 0xa7, 0xde, + 0xaa, 0xc1, 0x87, 0xd9, 0xb4, 0x10, 0x0d, 0xda, 0x5d, 0xf7, 0xdd, 0x7d, 0x08, 0x7a, 0xbe, 0x60, + 0xeb, 0x85, 0x00, 0x08, 0x21, 0x8e, 0xa0, 0x4d, 0xff, 0x73, 0x66, 0x55, 0xb8, 0x61, 0x69, 0xc5, + 0xf2, 0xef, 0x96, 0x27, 0x16, 0x2f, 0x0d, 0x93, 0xcd, 0xcd, 0xba, 0x2a, 0x80, 0x59, 0x90, 0x2b, + 0xc5, 0x43, 0x49, 0x25, 0x49, 0x01, 0x15, 0x9f, 0xf4, 0xdd, 0x02, 0x31, 0x8b, 0xf0, 0xbe, 0x28, + 0x87, 0xbd, 0x3f, 0xce, 0xb1, 0x6e, 0xd2, 0x7f, 0x77, 0x41, 0x09, 0x8a, 0x04, 0x17, 0x87, 0x57, + 0x1b, 0x72, 0x79, 0x69, 0x41, 0x7d, 0xc5, 0x78, 0xb7, 0x53, 0xe7, 0x80, 0xd2, 0x7f, 0x1f, 0xb9, + 0xd5, 0x0f, 0xff, 0xf5, 0xb8, 0xd0, 0xc5, 0x47, 0x30, 0x09, 0x36, 0x6e, 0xb1, 0xfd, 0xe1, 0xe9, + 0xf9, 0x81, 0x1d, 0x98, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x46, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x76, 0x21, + 0x2d, 0x37, 0xff, 0xff, 0xc3, 0x7f, 0xff, 0xff, 0xff, 0xd8, 0x59, 0x6b, 0xff, 0xa9, 0xff, 0xff, + 0xff, 0xda, 0x3d, 0xca, 0x5b, 0xc7, 0xff, 0xfd, 0x52, 0xf6, 0x2e, 0x02, 0xe0, 0x37, 0xe3, 0x5f, + 0xff, 0xea, 0x82, 0x5b, 0xff, 0xff, 0xfa, 0x7b, 0x85, 0xdf, 0x66, 0xbf, 0xff, 0xe6, 0xde, 0xef, + 0xed, 0x97, 0xff, 0xf9, 0x38, 0x4a, 0x7f, 0x61, 0xb5, 0xeb, 0x56, 0x73, 0xa7, 0xff, 0xff, 0x31, + 0x8b, 0xa1, 0xcf, 0xff, 0x5b, 0x90, 0x07, 0x17, 0xff, 0xd6, 0xe1, 0x74, 0x39, 0xff, 0xeb, 0x72, + 0x5d, 0x14, 0xff, 0xf5, 0xb8, 0x95, 0x54, 0x7f, 0xf7, 0x5d, 0x1e, 0x26, 0x68, 0x00, 0x7f, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, + 0x45, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x7d, 0xb8, 0xff, 0xff, 0xb4, 0x7f, 0xff, 0xff, 0xfe, + 0x4d, 0x81, 0xbf, 0xff, 0x98, 0x9f, 0xff, 0xff, 0xe4, 0xd8, 0x1b, 0xff, 0xf9, 0x89, 0xff, 0xff, + 0xfe, 0x4d, 0x81, 0xbf, 0xd6, 0xbf, 0xff, 0xf9, 0x36, 0x0c, 0x1f, 0xff, 0xfc, 0x10, 0x3e, 0xfe, + 0x1f, 0xa1, 0x76, 0x5c, 0x22, 0x5f, 0x3b, 0x56, 0x57, 0xa0, 0x3c, 0x43, 0xf0, 0xc8, 0x00, 0x00, + 0x00, 0x19, 0xbb, 0xff, 0xff, 0xd8, 0x43, 0x10, 0x26, 0x67, 0xff, 0xff, 0xfe, 0x4a, 0x07, 0x8f, + 0x80, 0x7f, 0xff, 0xff, 0x94, 0xd2, 0x59, 0x02, 0xff, 0xff, 0x0d, 0x89, 0x18, 0x02, 0xff, 0xff, + 0x0d, 0x84, 0xb2, 0x05, 0xff, 0xfe, 0x1b, 0x14, 0xdd, 0x42, 0xff, 0xff, 0x0d, 0x82, 0xc8, 0xb1, + 0xff, 0xf9, 0x5c, 0x32, 0xfe, 0x00, 0x78, 0x60, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x20, 0x46, 0x00, 0x00, 0x03, 0x60, + 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xc3, 0x7f, 0xff, 0xff, 0xff, 0xd8, 0x19, 0x3f, 0xff, 0xe6, + 0x1b, 0xfb, 0x80, 0x95, 0x82, 0x3b, 0xd9, 0x4c, 0x81, 0xce, 0x52, 0x2b, 0xff, 0xff, 0xa0, 0xad, + 0xc9, 0x0e, 0x62, 0xff, 0xff, 0xff, 0x60, 0xf8, 0x75, 0x5f, 0xff, 0xff, 0x31, 0x92, 0xaa, 0x8f, + 0xfe, 0xeb, 0xa4, 0x4e, 0x5f, 0xff, 0x5b, 0x89, 0x55, 0x47, 0xff, 0x75, 0xd4, 0xd9, 0xef, 0xff, + 0xe4, 0x51, 0x64, 0x58, 0xff, 0xee, 0xba, 0xfc, 0x8d, 0x8f, 0x00, 0x95, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x44, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xfb, 0x50, 0x3f, 0xff, 0xff, 0xff, 0xea, 0xc5, + 0x0d, 0xff, 0xff, 0xe0, 0x09, 0xfc, 0xad, 0x23, 0x5f, 0xf8, 0xa3, 0x64, 0x9b, 0x40, 0x76, 0x8d, + 0xd6, 0xd1, 0x83, 0x6f, 0x24, 0xd5, 0xff, 0xff, 0xd4, 0xa9, 0x45, 0xac, 0xda, 0xdc, 0xd2, 0xbf, + 0xff, 0xff, 0xfd, 0x58, 0xbf, 0x7f, 0x8a, 0x7f, 0xff, 0xff, 0xad, 0xb9, 0x2d, 0x3c, 0xbf, 0xff, + 0xd4, 0xca, 0x46, 0x7d, 0x7f, 0xff, 0xe1, 0x64, 0x96, 0x9e, 0x5f, 0xff, 0xea, 0x65, 0x4d, 0xe9, + 0x97, 0xff, 0xfa, 0x99, 0x16, 0x71, 0xaf, 0xff, 0xfc, 0x2c, 0x99, 0x7f, 0x5b, 0x8b, 0x50, 0x9c, + 0xdc, 0x43, 0xa8, 0xb7, 0xd5, 0x68, 0x6a, 0xbc, 0x78, 0xef, 0xc2, 0x3d, 0xfb, 0x3f, 0xcc, 0x51, + 0x40, 0x86, 0x54, 0x02, 0xa4, 0xb2, 0xbd, 0x79, 0x93, 0xa2, 0x4a, 0x35, 0x36, 0xc8, 0x7c, 0xdb, + 0xb0, 0x1d, 0xc1, 0xd0, 0x1d, 0x51, 0x9a, 0xe4, 0x19, 0xe6, 0x08, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x46, 0x00, + 0x00, 0x03, 0x60, 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xa9, 0xef, 0xff, 0xff, 0xff, 0xd8, 0x59, + 0x9f, 0xff, 0x53, 0xff, 0xff, 0xff, 0xb4, 0x78, 0x24, 0x37, 0xff, 0xfb, 0x89, 0x98, 0x26, 0xc0, + 0xe3, 0x5f, 0x50, 0x5c, 0x2f, 0xff, 0xff, 0xf1, 0xd2, 0xd2, 0x4d, 0xfd, 0xf2, 0x7f, 0xff, 0x9f, + 0xff, 0x17, 0x85, 0xd2, 0xab, 0x4b, 0xbe, 0x3d, 0xff, 0xff, 0xff, 0xdf, 0x55, 0xbf, 0xff, 0xff, + 0xff, 0xfe, 0x6e, 0xe3, 0xe4, 0x07, 0x53, 0xd2, 0x5f, 0xff, 0xff, 0xff, 0xbe, 0xab, 0x7f, 0xff, + 0xff, 0xff, 0xcd, 0xdc, 0x1a, 0xbf, 0xff, 0xfe, 0xfa, 0xad, 0xff, 0xff, 0xff, 0xcd, 0xdc, 0x4e, + 0x1d, 0x57, 0xff, 0xff, 0xe0, 0x44, 0xaa, 0xa3, 0xff, 0xd3, 0x89, 0x13, 0x97, 0xff, 0xe5, 0xa2, + 0x55, 0x51, 0xff, 0xe9, 0xc4, 0xba, 0x29, 0xff, 0xeb, 0x71, 0x2a, 0xa8, 0xff, 0xee, 0xb9, 0xc4, + 0x7f, 0xff, 0xfb, 0xea, 0xb7, 0xff, 0xff, 0xff, 0x37, 0x71, 0x29, 0xfa, 0x00, +}; + +constexpr std::array Vp9Hashes{ + 0x552fd3c320b37408ull, 0xb2fb78bac8d2d7dfull, 0x312469345c491c3dull, + 0x9aeb0fb400c95a75ull, 0xe02e07646faa48beull, 0x1399072031abb375ull, +}; + +constexpr std::array HevcMain10Stream{ + 0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, + 0xff, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x1e, + 0x95, 0x98, 0x09, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, + 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x1e, 0xa0, 0x20, 0x82, 0x93, 0x65, 0x95, 0x9a, + 0xe4, 0xca, 0xf0, 0x16, 0x80, 0x80, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x04, 0x00, + 0x00, 0x00, 0x01, 0x44, 0x01, 0xc1, 0x73, 0xd0, 0x89, 0x00, 0x00, 0x01, 0x28, 0x01, 0xaf, 0x08, + 0x60, 0xf2, 0xfb, 0x04, 0xef, 0x67, 0x67, 0xdc, 0xc7, 0x8e, 0xc7, 0x42, 0x63, 0xeb, 0x7d, 0x28, + 0x73, 0x95, 0x14, 0x0c, 0x71, 0x10, 0x8b, 0x65, 0x04, 0xbd, 0x03, 0xe6, 0x04, 0x35, 0xb9, 0x9b, + 0xec, 0x60, 0x2e, 0xea, 0xcf, 0xd0, 0xdd, 0x2d, 0x3a, 0x62, 0x33, 0x9d, 0x7a, 0x74, 0xb0, 0xeb, + 0x0c, 0x06, 0xe6, 0xa5, 0x30, 0xa7, 0xea, 0xed, 0xc2, 0xa5, 0x8e, 0x7c, 0xda, 0x1b, 0x9d, 0xe4, + 0xac, 0x2b, 0x38, 0xdb, 0x9c, 0xe8, 0x51, 0x34, 0x46, 0xd2, 0x04, 0x1a, 0x77, 0x87, 0x7a, 0x19, + 0x70, 0xf9, 0x4f, 0xcb, 0x03, 0x3c, 0x74, 0x25, 0xb9, 0x92, 0x3b, 0x95, 0xe9, 0xd8, 0xaa, 0x9b, + 0x6b, 0x9b, 0x1f, 0x70, 0x25, 0x2a, 0xe7, 0x6c, 0xc3, 0x7e, 0x55, 0x39, 0xf5, 0x06, 0x5a, 0x23, + 0xb1, 0x2e, 0xf6, 0x01, 0xac, 0x71, 0xb9, 0x20, 0xb4, 0xef, 0x1d, 0x42, 0x7e, 0x0e, 0x01, 0x1d, + 0xaf, 0x0a, 0x13, 0x04, 0x8e, 0x03, 0x4d, 0x1e, 0xc4, 0xe0, +}; + void check(bool condition, const std::string& message) { if (!condition) throw std::runtime_error(message); } @@ -290,6 +462,86 @@ void testFailures() { check(sceVideodec2DeleteDecoder_nid_postfix(handle) == 0, "delete failed"); } +std::vector> hevcUnits(const std::uint8_t* stream, std::size_t size) { + std::vector starts; + for (std::size_t index = 0; index + 4 < size; ++index) { + if (stream[index] == 0 && stream[index + 1] == 0 && stream[index + 2] == 0 && stream[index + 3] == 1 && ((stream[index + 4] >> 1u) & 0x3fu) == 35) starts.push_back(index); + } + starts.push_back(size); + std::vector> units; + for (std::size_t unit = 0; unit + 1 < starts.size(); ++unit) units.emplace_back(stream + starts[unit], stream + starts[unit + 1]); + return units; +} + +std::vector> ivfFrames() { + std::vector> frames; + for (std::size_t index = 32; index + 12 <= Vp9Stream.size();) { + const std::size_t length = Vp9Stream[index] | (Vp9Stream[index + 1] << 8u) | (Vp9Stream[index + 2] << 16u) | (static_cast(Vp9Stream[index + 3]) << 24u); + check(index + 12 + length <= Vp9Stream.size(), "truncated IVF frame"); + frames.emplace_back(Vp9Stream.begin() + static_cast(index + 12), Vp9Stream.begin() + static_cast(index + 12 + length)); + index += 12 + length; + } + return frames; +} + +void testClip(std::uint32_t codec, std::uint32_t profile, const std::vector>& units, const std::array& hashes) { + const DecoderConfigInfo config{sizeof(DecoderConfigInfo), 1, codec, profile, 0, static_cast(Width), static_cast(Height), 4, 1}; + DecoderMemoryInfo memory{sizeof(DecoderMemoryInfo)}; + check(sceVideodec2QueryDecoderMemoryInfo_nid_postfix(&config, &memory) == 0, "memory query failed"); + std::uint64_t handle = 0; + check(sceVideodec2CreateDecoder_nid_postfix(&config, &memory, &handle) == 0, "decoder creation failed"); + std::vector buffer(memory.maxFrameBufferSize); + std::size_t pictures = 0; + const auto take = [&](const OutputInfo& output, const FrameBuffer& frame) { + check(output.isValid == frame.isAccepted, "frame buffer acceptance disagrees with the output"); + if (!output.isValid) return false; + check(pictures < hashes.size(), "more pictures than access units"); + check(output.codecType == codec && !output.isErrorFrame && output.frameWidth == Width && output.frameHeight == Height && output.framePitch == 256 && output.frameBuffer == buffer.data(), "unexpected picture geometry"); + AvcPictureInfo info{sizeof(AvcPictureInfo)}; + checkThrows([&] { sceVideodec2GetPictureInfo_nid_postfix(&output, &info, nullptr); }); + check(hashNv12(buffer.data(), output.framePitch) == hashes[pictures], "picture " + std::to_string(pictures) + " differs from the reference"); + ++pictures; + return true; + }; + check(units.size() == hashes.size(), "unexpected access unit count"); + for (std::size_t unit = 0; unit < units.size(); ++unit) { + const InputData input{sizeof(InputData), units[unit].data(), units[unit].size(), 1000 + unit, unit, 0}; + FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size(), false}; + OutputInfo output{sizeof(OutputInfo)}; + check(sceVideodec2Decode_nid_postfix(handle, &input, &frame, &output) == 0, "decode failed"); + take(output, frame); + } + for (;;) { + FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size(), false}; + OutputInfo output{sizeof(OutputInfo)}; + check(sceVideodec2Flush_nid_postfix(handle, &frame, &output) == 0, "flush failed"); + if (!take(output, frame)) break; + } + check(pictures == hashes.size(), "missing pictures after flush"); + check(sceVideodec2DeleteDecoder_nid_postfix(handle) == 0, "delete failed"); +} + +void testTenBit() { + for (const auto codec : {CodecHevc, CodecVp9}) { + const DecoderConfigInfo config{sizeof(DecoderConfigInfo), 1, codec, 2, 0, static_cast(Width), static_cast(Height), 4, 1}; + DecoderMemoryInfo memory{sizeof(DecoderMemoryInfo)}; + check(sceVideodec2QueryDecoderMemoryInfo_nid_postfix(&config, &memory) == 0, "memory query failed"); + std::uint64_t handle = 0; + check(sceVideodec2CreateDecoder_nid_postfix(&config, &memory, &handle) == 0, "ten-bit decoder creation failed"); + if (codec == CodecHevc) { + std::vector buffer(memory.maxFrameBufferSize); + const InputData input{sizeof(InputData), HevcMain10Stream.data(), HevcMain10Stream.size(), 0, 0, 0}; + FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size(), false}; + OutputInfo output{sizeof(OutputInfo)}; + checkThrows([&] { + sceVideodec2Decode_nid_postfix(handle, &input, &frame, &output); + sceVideodec2Flush_nid_postfix(handle, &frame, &output); + }); + } + check(sceVideodec2DeleteDecoder_nid_postfix(handle) == 0, "delete failed"); + } +} + void testDecode(bool lengthPrefixed) { const DecoderConfigInfo config{sizeof(DecoderConfigInfo), 0, 1, 100, 0, static_cast(Width), static_cast(Height), 4, 1}; DecoderMemoryInfo memory{sizeof(DecoderMemoryInfo)}; @@ -343,6 +595,30 @@ int main() { testFailures(); testDecode(false); testDecode(true); + const auto hevc = hevcUnits(HevcStream.data(), HevcStream.size()); + testClip(CodecHevc, 1, hevc, HevcHashes); + std::vector> hevcLengthPrefixed; + for (const auto& unit : hevc) { + std::vector converted; + std::vector nals; + for (std::size_t index = 0; index + 3 < unit.size(); ++index) { + if (unit[index] == 0 && unit[index + 1] == 0 && unit[index + 2] == 1) { + nals.push_back(index + 3); + index += 2; + } + } + for (std::size_t nal = 0; nal < nals.size(); ++nal) { + auto end = nal + 1 < nals.size() ? nals[nal + 1] - 3 : unit.size(); + while (end > nals[nal] && unit[end - 1] == 0) --end; + const auto length = end - nals[nal]; + for (int shift = 24; shift >= 0; shift -= 8) converted.push_back(static_cast(length >> shift)); + converted.insert(converted.end(), unit.begin() + static_cast(nals[nal]), unit.begin() + static_cast(end)); + } + hevcLengthPrefixed.push_back(std::move(converted)); + } + testClip(CodecHevc, 1, hevcLengthPrefixed, HevcHashes); + testClip(CodecVp9, 0, ivfFrames(), Vp9Hashes); + testTenBit(); std::puts("Videodec2 tests passed"); return 0; } catch (const std::exception& error) { diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 81b674366..70771c285 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -352,3 +352,4 @@ Throughout the project, every function at every stage either **does exactly what - [Guest path resolution](../../core/libs/prx/libc/src/General.cpp) falls back to ASCII case-insensitive component lookup, following [shadPS4's host filesystem](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/file_sys/backends/host_fs.cpp). PPSA03747 requests `Skyrim.INI`, `Data/Video` and lowercase archive/plugin names that differ from the dump's filenames. Exact host names take precedence; multiple fallback matches throw instead of selecting an arbitrary file. Existing parent names are resolved when creating a new file, and guest mount aliases use the same ASCII comparison. Results are not cached, so renames and newly created files remain visible. Unicode case folding and PS5 behaviour for case-colliding host entries are unverified; host roots are used as supplied. - [Static scalar calls](../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) (`s_call_b64` and `s_swappc_b64`) require one call per ordinary even-aligned SGPR link pair (s0:s1 through s104:s105) and one later `s_setpc_b64` return reading that exact pair. Regions must nest or be disjoint; recursion, shared links, escaping link values (including overlapping tuples and resource descriptors), invalid targets, interleaved regions, cross-region branches or fall-through, relative SGPR reads and dynamic `s_setpc_b64` in a program with calls throw. The analysis checks all decoded instructions, including unreachable code. Arbitrary indirect calls and general call frames are not implemented. - [s_call_b64](../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp) saves `PC + 4` over `GetShaderBase` and branches to `PC + 4 + signext(SIMM16) * 4`, following [AMD RDNA2 ISA](https://gpuopen.com/download/rdna2-shader-instruction-set-architecture.pdf) sections 3.6.3, 12.2 and 12.3. Forward callees after `s_endpgm` are decoded. Native RDNA2/PS5 execution and title compatibility for these bounded scalar calls remain unverified; `s_rfe_b64` still throws in translation. +- [sceVideodec2CreateDecoder](../../core/libs/prx/libSceVideodec2/Export.cpp) (libSceVideodec2) decodes HEVC (codec type 974921) and VP9 (2382845) through FFmpeg, besides H.264. The tags come from SharpProspero's `Videodec2CodecType` and EVO-PLAYER-PS5's `sce_videodec2.h`, which records them from hardware runs. HEVC input is converted from length-prefixed NAL units to Annex B as for H.264, and VP9 frames are passed through. Output is the same 8-bit NV12 for every codec; a decoder for HEVC Main10 or VP9 Profile 2 is created, but a frame deeper than 8 bits throws, since the console's 10-bit output layout is unknown, and `QueryDecoderMemoryInfo` reports the 8-bit frame size for it. `sceVideodec2GetPictureInfo` and `sceVideodec2GetAvcPictureInfo` throw for HEVC and VP9 pictures, which use other layouts (`SceVideodec2HevcPictureInfo`, `SceVideodec2Vp9PictureInfo`), and `sceVideodec2GetHevcPictureInfo` and `sceVideodec2GetVp9PictureInfo` are not exported