From 087cb55ba8f514b62019d0ad14885a02dbfa6890 Mon Sep 17 00:00:00 2001 From: ened Date: Sun, 5 Oct 2025 12:39:33 +0900 Subject: [PATCH] WIP --- .../platforms/windows/vavcore/VavCore.vcxproj | 4 +- .../vavcore/src/Decoder/NVDECAV1Decoder.cpp | 89 ++++++++++++++++--- .../vavcore/src/Decoder/NVDECAV1Decoder.h | 2 +- .../vavcore/src/FileIO/WebMFileReader.cpp | 43 ++++++++- 4 files changed, 120 insertions(+), 18 deletions(-) diff --git a/vav2/platforms/windows/vavcore/VavCore.vcxproj b/vav2/platforms/windows/vavcore/VavCore.vcxproj index f3ff127..7670e2b 100644 --- a/vav2/platforms/windows/vavcore/VavCore.vcxproj +++ b/vav2/platforms/windows/vavcore/VavCore.vcxproj @@ -117,8 +117,8 @@ - - + + diff --git a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp index 61ac2f6..527cf1a 100644 --- a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp +++ b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp @@ -126,9 +126,12 @@ bool NVDECAV1Decoder::Initialize(const VideoMetadata& metadata) { return false; } - // Check NVDEC availability - if (!IsNVDECAvailable(metadata.width, metadata.height)) { - LogError("NVDEC not available on this system"); + // Check NVDEC availability with ColorSpace compatibility check + if (!IsNVDECAvailable(metadata.width, metadata.height, metadata.color_space)) { + char debug_buf[256]; + sprintf_s(debug_buf, "NVDEC not available for this video format (%dx%d, ColorSpace=%d)", + metadata.width, metadata.height, static_cast(metadata.color_space)); + LogError(debug_buf); return false; } @@ -523,7 +526,7 @@ std::string NVDECAV1Decoder::GetVersion() const { return "NVDEC AV1 (CUDA Driver: " + std::to_string(driver_version) + ")"; } -bool NVDECAV1Decoder::IsNVDECAvailable(uint32_t width, uint32_t height) const { +bool NVDECAV1Decoder::IsNVDECAvailable(uint32_t width, uint32_t height, ColorSpace color_space) const { char debug_buf[256]; // Check if CUDA driver is available @@ -578,11 +581,36 @@ bool NVDECAV1Decoder::IsNVDECAvailable(uint32_t width, uint32_t height) const { } OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] Step 4: CUDA context created\n"); - OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] Step 5: Checking AV1 decode capabilities...\n"); + // Map ColorSpace to NVDEC ChromaFormat + cudaVideoChromaFormat chroma_format; + const char* color_space_name; + switch (color_space) { + case ColorSpace::YUV420P: + chroma_format = cudaVideoChromaFormat_420; + color_space_name = "YUV420"; + break; + case ColorSpace::YUV422P: + chroma_format = cudaVideoChromaFormat_422; + color_space_name = "YUV422"; + break; + case ColorSpace::YUV444P: + chroma_format = cudaVideoChromaFormat_444; + color_space_name = "YUV444"; + break; + default: + sprintf_s(debug_buf, "[NVDECAV1Decoder::IsNVDECAvailable] FAILED: Unsupported ColorSpace %d\n", static_cast(color_space)); + OutputDebugStringA(debug_buf); + cuCtxDestroy(cuContext); + return false; + } + + sprintf_s(debug_buf, "[NVDECAV1Decoder::IsNVDECAvailable] Step 5: Checking %s decode capabilities...\n", color_space_name); + OutputDebugStringA(debug_buf); + CUVIDDECODECAPS decode_caps; memset(&decode_caps, 0, sizeof(decode_caps)); decode_caps.eCodecType = cudaVideoCodec_AV1; - decode_caps.eChromaFormat = cudaVideoChromaFormat_420; + decode_caps.eChromaFormat = chroma_format; decode_caps.nBitDepthMinus8 = 0; result = cuvidGetDecoderCaps(&decode_caps); @@ -595,12 +623,48 @@ bool NVDECAV1Decoder::IsNVDECAvailable(uint32_t width, uint32_t height) const { return false; } - sprintf_s(debug_buf, "[NVDECAV1Decoder::IsNVDECAvailable] Step 5: AV1 decode caps - bIsSupported=%d, nMaxWidth=%d, nMaxHeight=%d\n", - decode_caps.bIsSupported, decode_caps.nMaxWidth, decode_caps.nMaxHeight); + sprintf_s(debug_buf, "[NVDECAV1Decoder::IsNVDECAvailable] Step 5: AV1 %s decode caps - bIsSupported=%d, nMaxWidth=%d, nMaxHeight=%d\n", + color_space_name, decode_caps.bIsSupported, decode_caps.nMaxWidth, decode_caps.nMaxHeight); OutputDebugStringA(debug_buf); if (!decode_caps.bIsSupported) { - OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] FAILED: AV1 decoding is NOT supported by this GPU\n"); + sprintf_s(debug_buf, "[NVDECAV1Decoder::IsNVDECAvailable] FAILED: AV1 %s decoding is NOT supported by this GPU\n", color_space_name); + OutputDebugStringA(debug_buf); + return false; + } + + // Check supported output formats + sprintf_s(debug_buf, "[NVDECAV1Decoder::IsNVDECAvailable] Step 6: Output format mask = 0x%X\n", decode_caps.nOutputFormatMask); + OutputDebugStringA(debug_buf); + + bool has_supported_output = false; + if (decode_caps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV12)) { + OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] - NV12 supported\n"); + has_supported_output = true; + } + if (decode_caps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_P016)) { + OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] - P016 (10-bit 4:2:0) supported\n"); + has_supported_output = true; + } + if (decode_caps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_YUV444)) { + OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] - YUV444 (8-bit 4:4:4) supported\n"); + has_supported_output = true; + } + if (decode_caps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_YUV444_16Bit)) { + OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] - YUV444_16Bit (10-bit 4:4:4) supported\n"); + has_supported_output = true; + } + if (decode_caps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV16)) { + OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] - NV16 (8-bit 4:2:2) supported\n"); + has_supported_output = true; + } + if (decode_caps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_P216)) { + OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] - P216 (10-bit 4:2:2) supported\n"); + has_supported_output = true; + } + + if (!has_supported_output) { + OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] FAILED: No supported output format found\n"); return false; } @@ -614,7 +678,8 @@ bool NVDECAV1Decoder::IsNVDECAvailable(uint32_t width, uint32_t height) const { return false; } - OutputDebugStringA("[NVDECAV1Decoder::IsNVDECAvailable] SUCCESS: NVDEC AV1 is available!\n"); + sprintf_s(debug_buf, "[NVDECAV1Decoder::IsNVDECAvailable] SUCCESS: NVDEC AV1 %s is available!\n", color_space_name); + OutputDebugStringA(debug_buf); return true; } @@ -734,7 +799,7 @@ bool NVDECAV1Decoder::CreateParser() { m_parserParams.CodecType = cudaVideoCodec_AV1; m_parserParams.ulMaxNumDecodeSurfaces = 1; // Dummy value - will be updated by HandleVideoSequence - m_parserParams.ulMaxDisplayDelay = 1; // CRITICAL: Required for pfnDisplayPicture to be called + m_parserParams.ulMaxDisplayDelay = 1; // NVDEC requires >=1 for pfnDisplayPicture to be called m_parserParams.ulClockRate = 0; // Use default m_parserParams.ulErrorThreshold = 100; m_parserParams.pUserData = this; @@ -1224,7 +1289,7 @@ bool NVDECAV1Decoder::DecodeToSurface(const uint8_t* packet_data, size_t packet_ slot_idx, my_slot.is_ready); OutputDebugStringA(debug_buf); - if (!my_slot.frame_ready.wait_for(lock, std::chrono::milliseconds(5000), + if (!my_slot.frame_ready.wait_for(lock, std::chrono::milliseconds(10000), [&my_slot]() { return my_slot.is_ready; })) { // Timeout - decode took too long sprintf_s(debug_buf, "[DecodeToSurface] ERROR: Decode timeout for slot_idx=%zu (is_ready=%d)\n", diff --git a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h index 52ec795..1b319f8 100644 --- a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h +++ b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h @@ -72,7 +72,7 @@ public: } // NVDEC-specific methods - bool IsNVDECAvailable(uint32_t width, uint32_t height) const; + bool IsNVDECAvailable(uint32_t width, uint32_t height, ColorSpace color_space = ColorSpace::YUV420P) const; bool InitializeCUDA(); // Static method for availability check (used in registration) diff --git a/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp b/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp index c31d309..3817563 100644 --- a/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp +++ b/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp @@ -654,7 +654,44 @@ bool WebMFileReader::ExtractVideoMetadata() { meta.frame_rate = it->frame_rate; meta.codec_type = it->codec_type; meta.codec_name = it->codec_name; + + // Extract color space from VideoTrack Colour element meta.color_space = ColorSpace::YUV420P; // Default value + const mkvparser::Tracks* tracks = m_state->segment->GetTracks(); + if (tracks) { + const mkvparser::Track* track = tracks->GetTrackByNumber(m_state->selected_track_number); + if (track && track->GetType() == mkvparser::Track::kVideo) { + const mkvparser::VideoTrack* video_track = static_cast(track); + const mkvparser::Colour* colour = video_track->GetColour(); + + if (colour) { + // Detect color space from chroma subsampling + // libwebm Colour reference: https://www.matroska.org/technical/elements.html + // chroma_subsampling_horz/vert: 0=4:4:4, 1=4:2:2/4:2:0 + long long horz = colour->chroma_subsampling_horz; + long long vert = colour->chroma_subsampling_vert; + + if (horz == 0 && vert == 0) { + // No subsampling = YUV444 + meta.color_space = ColorSpace::YUV444P; + OutputDebugStringA("[WebMFileReader] Detected ColorSpace: YUV444P (4:4:4)\n"); + } else if (horz == 1 && vert == 0) { + // Horizontal subsampling only = YUV422 + meta.color_space = ColorSpace::YUV422P; + OutputDebugStringA("[WebMFileReader] Detected ColorSpace: YUV422P (4:2:2)\n"); + } else if (horz == 1 && vert == 1) { + // Both horizontal and vertical subsampling = YUV420 + meta.color_space = ColorSpace::YUV420P; + OutputDebugStringA("[WebMFileReader] Detected ColorSpace: YUV420P (4:2:0)\n"); + } else { + // Unknown or unspecified - keep default YUV420P + OutputDebugStringA("[WebMFileReader] Unknown chroma subsampling, using default YUV420P\n"); + } + } else { + OutputDebugStringA("[WebMFileReader] No Colour element found, using default YUV420P\n"); + } + } + } // Get duration from segment info const mkvparser::SegmentInfo* info = m_state->segment->GetInfo(); @@ -674,9 +711,9 @@ bool WebMFileReader::ExtractVideoMetadata() { meta.file_path = m_state->file_path; // Extract codec private data (AV1 sequence header) from track - const mkvparser::Tracks* tracks = m_state->segment->GetTracks(); - if (tracks) { - const mkvparser::Track* track = tracks->GetTrackByNumber(m_state->selected_track_number); + const mkvparser::Tracks* tracks2 = m_state->segment->GetTracks(); + if (tracks2) { + const mkvparser::Track* track = tracks2->GetTrackByNumber(m_state->selected_track_number); if (track && track->GetType() == mkvparser::Track::kVideo) { const mkvparser::VideoTrack* video_track = static_cast(track);