This commit is contained in:
2025-10-05 12:39:33 +09:00
parent 7c2973f17f
commit 087cb55ba8
4 changed files with 120 additions and 18 deletions

View File

@@ -117,8 +117,8 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="include\VavCore\VavCore.h" />
<ClInclude Include="include\VavCore\VideoTypes.h" />
<ClInclude Include="include\VavCore\IVideoDecoder.h" />
<ClInclude Include="src\Common\VideoTypes.h" />
<ClInclude Include="src\Decoder\IVideoDecoder.h" />
<ClInclude Include="src\pch.h" />
<ClInclude Include="src\Common\AdaptiveTypes.h" />
<ClInclude Include="src\Decoder\AV1Decoder.h" />

View File

@@ -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<int>(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<int>(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",

View File

@@ -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)

View File

@@ -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<const mkvparser::VideoTrack*>(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<const mkvparser::VideoTrack*>(track);