From 701c91b402f47fdbb36d4f7c4b4bf1d0db08888b Mon Sep 17 00:00:00 2001 From: rdenno Date: Sat, 31 Oct 2026 12:14:21 -0400 Subject: nx2: vdecsvc picks H.264 when the configure carries no codec name and the stream is H.264 (Yoshi demo) Co-Authored-By: Claude Opus 5.5 --- src/video_core/host1x/nx2_video_decoder.cpp | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'src/video_core/host1x/nx2_video_decoder.cpp') diff --git a/src/video_core/host1x/nx2_video_decoder.cpp b/src/video_core/host1x/nx2_video_decoder.cpp index 9b34bd84af..87265ee6f6 100644 --- a/src/video_core/host1x/nx2_video_decoder.cpp +++ b/src/video_core/host1x/nx2_video_decoder.cpp @@ -47,6 +47,45 @@ std::optional CodecFromMime(std::string_view mime) { } // namespace +bool IsH264Stream(std::span data) { + if (data.size() >= 4) { + // Annex-B 3-byte start code: 00 00 01 + if (data[0] == 0 && data[1] == 0 && data[2] == 1) { + const u8 nal_type = data[3] & 0x1F; + if ((data[3] & 0x80) == 0 && nal_type >= 1 && nal_type <= 23) { + return true; + } + } + } + if (data.size() >= 5) { + // Annex-B 4-byte start code: 00 00 00 01 + if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 1) { + const u8 nal_type = data[4] & 0x1F; + if ((data[4] & 0x80) == 0 && nal_type >= 1 && nal_type <= 23) { + return true; + } + } + // avcC / MP4 length-prefixed NAL unit: [u32 be length] + const u32 nalu_len = (static_cast(data[0]) << 24) | + (static_cast(data[1]) << 16) | + (static_cast(data[2]) << 8) | + static_cast(data[3]); + if (nalu_len > 0 && nalu_len <= data.size() - 4) { + const u8 nal_type = data[4] & 0x1F; + if ((data[4] & 0x80) == 0 && nal_type >= 1 && nal_type <= 23) { + return true; + } + } + } + if (data.size() >= 7) { + // avcC configuration record: version=1, profile, compat, level, lengthSizeMinusOne | 0xFC, numSPS | 0xE0 + if (data[0] == 1 && (data[4] & 0xFC) == 0xFC && (data[5] & 0xE0) == 0xE0 && (data[5] & 0x1F) > 0) { + return true; + } + } + return false; +} + struct Nx2VideoDecoder::Impl { std::string name; std::optional decoder; @@ -123,6 +162,31 @@ void Nx2VideoDecoder::Decode(std::span data, u64 pts, std::vectorpacket.assign(data.size() + AV_INPUT_BUFFER_PADDING_SIZE, 0); std::memcpy(impl->packet.data(), data.data(), data.size()); + + // Convert MP4/avcC length-prefixed NALUs to Annex-B start codes for FFmpeg if needed. + if (impl->name.find("avc") != std::string::npos || impl->name.find("264") != std::string::npos) { + const bool is_annex_b = data.size() >= 4 && + ((data[0] == 0 && data[1] == 0 && data[2] == 1) || + (data.size() >= 5 && data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 1)); + if (!is_annex_b && data.size() >= 5) { + size_t offset = 0; + while (offset + 4 < data.size()) { + const u32 nalu_len = (static_cast(impl->packet[offset]) << 24) | + (static_cast(impl->packet[offset + 1]) << 16) | + (static_cast(impl->packet[offset + 2]) << 8) | + static_cast(impl->packet[offset + 3]); + if (nalu_len == 0 || offset + 4 + nalu_len > data.size()) { + break; + } + impl->packet[offset] = 0; + impl->packet[offset + 1] = 0; + impl->packet[offset + 2] = 0; + impl->packet[offset + 3] = 1; + offset += 4 + nalu_len; + } + } + } + FFmpeg::Packet packet{std::span{impl->packet.data(), data.size()}}; packet.GetPacket()->pts = static_cast(pts); // Hidden VP9 frames never come out: keep the set from growing over a long movie. -- cgit v1.2.3 From 873cae5569e9f0069a73f24ab5e4ffd0799b02e7 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 3 Nov 2026 05:47:42 +0600 Subject: nx2: video decoder: copy NV12 frames from a GPU decoder without swscale FFmpeg 6.1's swscale leaves the chroma plane of an NV12 to NV12 copy at zero, so movies decoded on the host GPU (Mario Kart World's pre-race movie, Hyrule Warriors' cutscenes) showed green. CPU-decoded frames (YUV420P) were not affected. --- src/video_core/host1x/nx2_video_decoder.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/video_core/host1x/nx2_video_decoder.cpp') diff --git a/src/video_core/host1x/nx2_video_decoder.cpp b/src/video_core/host1x/nx2_video_decoder.cpp index 87265ee6f6..86869ad5e2 100644 --- a/src/video_core/host1x/nx2_video_decoder.cpp +++ b/src/video_core/host1x/nx2_video_decoder.cpp @@ -226,6 +226,20 @@ void Nx2VideoDecoder::Render(const Nx2Picture& picture, Nx2PictureFormat format, const AVPixelFormat target = format == Nx2PictureFormat::Nv12 ? AV_PIX_FMT_NV12 : format == Nx2PictureFormat::Rgba8 ? AV_PIX_FMT_RGBA : AV_PIX_FMT_BGRA; + // Frames copied back from a host GPU decoder are already NV12. Copy them as they are: + // FFmpeg 6.1's swscale leaves the chroma plane of an NV12 to NV12 copy at zero, which shows + // the movies green. + if (target == AV_PIX_FMT_NV12 && f->format == AV_PIX_FMT_NV12 && + static_cast(f->width) == width && static_cast(f->height) == height) { + for (u32 y = 0; y < height; ++y) { + std::memcpy(out.data() + y * pitch, f->data[0] + y * f->linesize[0], width); + } + for (u32 y = 0; y < (height + 1) / 2; ++y) { + std::memcpy(out.data() + chroma_offset + y * pitch, f->data[1] + y * f->linesize[1], + width); + } + return; + } impl->scaler = sws_getCachedContext(impl->scaler, f->width, f->height, static_cast(f->format), static_cast(width), static_cast(height), target, -- cgit v1.2.3