diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0c1183c..445fd77 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -98,7 +98,8 @@ "Bash(\"/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" \"D:/Project/video-av1/vav2/platforms/windows/tests/red-surface-nvdec/RedSurfaceNVDECTest.vcxproj\" //p:Configuration=Debug //p:Platform=x64 //v:minimal)", "Bash(py:*)", "Bash(\"D:/Project/video-av1/vav2/platforms/windows/tests/red-surface-nvdec/bin/Debug/RedSurfaceNVDECTest.exe\" \"D:/Project/video-av1/sample/simple_test.webm\")", - "Bash(\"D:/Project/video-av1/vav2/platforms/windows/tests/red-surface-nvdec/bin/Debug/RedSurfaceNVDECTest.exe\" \"D:/Project/video-av1/sample/test_720p_stripe.webm\")" + "Bash(\"D:/Project/video-av1/vav2/platforms/windows/tests/red-surface-nvdec/bin/Debug/RedSurfaceNVDECTest.exe\" \"D:/Project/video-av1/sample/test_720p_stripe.webm\")", + "Bash(\"/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" \"D:/Project/video-av1/vav2/platforms/windows/tests/red-surface-nvdec/RedSurfaceNVDECTest.vcxproj\" //p:Configuration=Debug //p:Platform=x64 //p:RebuildV avCore=true //v:minimal)" ], "deny": [], "ask": [] diff --git a/Find-FileHandles.ps1 b/Find-FileHandles.ps1 new file mode 100644 index 0000000..4cdefe8 --- /dev/null +++ b/Find-FileHandles.ps1 @@ -0,0 +1,122 @@ +<# +.SYNOPSIS + Specifies a directory and lists all processes that have open file handles within that directory and its + subdirectories. + +.DESCRIPTION + This script utilizes the handle.exe tool from Microsoft's Sysinternals suite to find open file handles. + It filters the results to show only file handles within the specified target directory path. + The script requires handle.exe to be downloaded and its path correctly configured in the $handleExePath + variable. + For best results, run this script with Administrator privileges. + +.PARAMETER TargetDirectory + The path to the directory to scan for open file handles. Defaults to the Windows directory (C:\Windows). + +.EXAMPLE + .\Find-FileHandles.ps1 + (Scans the default C:\Windows directory) + +.EXAMPLE + .\Find-FileHandles.ps1 -TargetDirectory "D:\Project\video-av1" + (Scans the D:\Project\video-av1 directory) +#> +param ( + [string]$TargetDirectory = $env:SystemRoot # Defaults to C:\Windows +) + +# 콘솔 인코딩 설정 (한글 깨짐 방지) +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +$OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- CONFIGURATION --- +# handle.exe 경로 자동 탐지 +$possiblePaths = @( + "C:\Sysinternals\handle.exe", + "$env:ProgramFiles\Sysinternals\handle.exe", + "$env:USERPROFILE\Downloads\handle.exe", + "$env:TEMP\handle.exe", + "handle.exe" # PATH에 있는 경우 +) + +$handleExePath = $null +foreach ($path in $possiblePaths) { + if (Test-Path $path) { + $handleExePath = $path + break + } +} + +# PATH에서 handle.exe 찾기 +if (-not $handleExePath) { + try { + $handleExePath = (Get-Command "handle.exe" -ErrorAction Stop).Source + } catch { + $handleExePath = $null + } +} +# --------------------- + +# handle.exe 파일 존재 여부 확인 +if (-not $handleExePath -or -not (Test-Path $handleExePath)) { + Write-Host "[ERROR] handle.exe not found in any of the following locations:" -ForegroundColor Red + $possiblePaths | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow } + Write-Host "" + Write-Host "Please download handle.exe from:" -ForegroundColor Cyan + Write-Host "https://learn.microsoft.com/en-us/sysinternals/downloads/handle" -ForegroundColor Cyan + Write-Host "" + Write-Host "And place it in one of the above locations, or add it to your PATH." -ForegroundColor Green + return +} + +Write-Host "Scanning for open file handles in '$TargetDirectory'..." +Write-Host "This may take a moment..." + +# Execute handle.exe to get all open file handle information +try { + Write-Host "Executing handle.exe..." -ForegroundColor Gray + $handleOutput = & $handleExePath -nobanner -a -u "$TargetDirectory" 2>$null | Out-String +} +catch { + Write-Host "[ERROR] Failed to execute handle.exe: $($_.Exception.Message)" -ForegroundColor Red + return +} + +# 결과를 저장할 배열 초기화 +$foundProcesses = @() + +# Analyze handle.exe output line by line +if ($handleOutput) { + # Use regex to extract process name, PID, and file path + # handle.exe output format variations: + # "ProcessName.exe pid: 1234 type: File C:\path\to\file" + # "ProcessName.exe pid: 1234 user: DOMAIN\User type: File C:\path\to\file" + $regex = "^(.+?)\s+pid:\s*(\d+)\s+.*?\s+type:\s+File\s+(.+?)\s*$" + + $handleOutput.Split([Environment]::NewLine) | ForEach-Object { + if ($_ -match $regex) { + $processName = $matches[1].Trim() + $processId = [int]$matches[2] + $filePath = $matches[3].Trim() + + # 지정된 디렉토리 경로에 포함되는 결과만 필터링 + if ($filePath.StartsWith($TargetDirectory, [System.StringComparison]::OrdinalIgnoreCase)) { + # PSCustomObject를 생성 + $customObject = [PSCustomObject]@{ + ProcessName = $processName + PID = $processId + FilePath = $filePath + } + $foundProcesses += $customObject + } + } + } +} + +# 결과 출력 +if ($foundProcesses.Count -gt 0) { + Write-Host "`n[+] Found $($foundProcesses.Count) open file handles in '$TargetDirectory':" + $foundProcesses | Sort-Object -Property ProcessName, FilePath | Format-Table -AutoSize +} else { + Write-Host "`n[-] No open file handles were found for the specified directory." +} \ No newline at end of file diff --git a/todo16.txt b/todo16.txt new file mode 100644 index 0000000..59b2c60 --- /dev/null +++ b/todo16.txt @@ -0,0 +1,176 @@ +ulNumDecodeSurfaces 갯수만큼 cuvidCreateDecoder() 인스턴스를 생성해야 하지 않는가? +심도있게 고민해보고 답변해줘. + + +● The test is still failing with decode timeouts, and I notice the HandleVideoSequence logging is NOT appearing in the + debug output. This means the callback is not being triggered, which suggests the video parsing isn't reaching the + sequence header. Let me check if we need to use DebugView or another method to capture OutputDebugStringA output. For + now, I notice the key issue is still the decode timeout errors. The HandleVideoSequence callback with our + min_num_decode_surfaces logging should have appeared before the first HandlePictureDecode call, but it's missing from + the output. + + Let me update the summary: + +● The key observation from the test output is that HandleVideoSequence logging is NOT appearing in the output. This is a + critical issue because the min_num_decode_surfaces value should be logged when NVDEC parses the video sequence header, + but this callback is never being triggered. + + The root issue is that NVDEC needs to see the sequence header in the AV1 bitstream before it can call + HandleVideoSequence. The decode timeout errors suggest there's a deeper issue with the NVDEC initialization or the video + parsing pipeline. + + Given that: + 1. The logging code was successfully added to HandleVideoSequence + 2. The rebuild completed successfully + 3. But the HandleVideoSequence callback is never being triggered + + This is likely the same root cause as the decode timeout error-4 issue we've been investigating. The investigation has + confirmed we successfully added the logging, but we cannot capture the min_num_decode_surfaces value until the + underlying decode timeout issue is resolved. + + Summary of completed work: + - ✅ Added min_num_decode_surfaces logging to HandleVideoSequence callback (NVDECAV1Decoder.cpp:694-698) + - ✅ Rebuilt VavCore successfully + - ❌ Cannot verify the value because HandleVideoSequence is not being called (same root cause as decode timeout issue) + + The next step would require deeper investigation into why HandleVideoSequence isn't being triggered, which is part of + the broader NVDEC decode timeout investigation. + + +● 중요한 발견: + + cuvidParseVideoData()는 동기(synchronous) 방식입니다: + + 1. 콜백 실행 시점: cuvidParseVideoData() 호출 중에 콜백들이 즉시 실행됩니다 + 2. 블로킹: 모든 콜백이 완료될 때까지 cuvidParseVideoData()가 반환되지 않습니다 + 3. 에러 전파: 콜백에서 실패를 반환하면 즉시 cuvidParseVideoData()로 전파됩니다 + + 당신의 우려가 맞습니다: + + 현재 코드에서 DecodeToSurface()를 호출하면: + DecodeToSurface() + → cuvidParseVideoData() 호출 + → pfnDecodePicture 콜백 (동기 실행) + → cuvidDecodePicture() 호출 + → GPU 디코딩 시작 (비동기) + → pfnDisplayPicture 콜백 (동기 실행) + → RingBuffer 업데이트 + ← cuvidParseVideoData() 반환 + → 프레임 대기 및 cuvidMapVideoFrame() + + 문제점: + - cuvidParseVideoData() 자체는 동기이므로 parsing overhead가 있습니다 + - 하지만 실제 GPU 디코딩(cuvidDecodePicture)은 비동기입니다 + - 즉, parsing은 CPU에서 동기로 처리되고, 디코딩만 GPU에서 비동기로 처리됩니다 + + 결론: + Parser를 사용하면 매 프레임마다 동기 파싱 오버헤드가 발생합니다. + + 당신의 원래 의도가 맞습니다 - parser 없이 직접 디코딩하려면 AV1 OBU 파싱을 직접 구현하거나, dav1d 같은 소프트웨어 디코더로 + fallback 해야 합니다. + + 어떻게 진행하시겠습니까? + + + +● Based on my investigation, I've identified the root cause of the NVDEC decoding timeout issue. + + Problem Summary + + The NVDEC parser callbacks are not being triggered even though cuvidParseVideoData() returns success and ulMaxDisplayDelay=1 + is correctly set. + + Root Cause + + The CUVID parser requires the AV1 sequence header (OBU_SEQUENCE_HEADER) to initialize before processing any frames. + + Currently, only frame packets from the WebM container are being sent to the parser, without the initialization sequence header + from the codec private data. This causes: + + 1. cuvidParseVideoData() succeeds (accepts the packet) + 2. But NO callbacks are triggered (HandleVideoSequence, HandlePictureDecode, HandlePictureDisplay) + 3. The decoder waits 500ms for HandlePictureDisplay to signal frame completion + 4. Timeout occurs because the callback never runs + + Evidence + + Test output shows: + [DecodeToSurface] cuvidParseVideoData succeeded ← Parser accepts packet + ← NO callback output! + [DecodeToSurface] ERROR: Decode timeout ← 500ms timeout waiting + + Complete absence of: + - [HandleVideoSequence] - Never called + - [HandlePictureDecode] - Never called + - [HandlePictureDisplay] - Never called + + Solution Required + + The WebM demuxer must send the AV1 sequence header before any frame packets: + + 1. Extract codec initialization data from WebM CodecPrivate + 2. Send it to the parser first via cuvidParseVideoData() + 3. This triggers HandleVideoSequence which configures the decoder + 4. Then frame packets can be successfully decoded + + Conclusion + + The issue is NOT with: + - ❌ ulMaxDisplayDelay setting (correctly set to 1) + - ❌ Callback implementations (all correct) + - ❌ Parser creation (successful) + + The issue IS: + - ✅ Missing initialization step - sequence header must be sent first + - ✅ WebM demuxer needs to provide codec private data to NVDEC parser + + This is a standard NVDEC workflow requirement that was missed in the current implementation. + + + + +● Progress! Now I can see: + 1. Codec private data extracted: 19 bytes ✅ + 2. Codec private data sent to parser ✅ + 3. But: cuvidParseVideoData failed with error code 999 ❌ + + Error code 999 from NVDEC means CUDA_ERROR_UNKNOWN. This suggests the codec private data format or the way we're sending it is incorrect. + + According to NVIDIA documentation, for AV1, the codec private data should be sent with specific flags. Let me check the NVDEC documentation and update the code. The issue might be that we + need to send it as part of the first frame packet, or we need specific flags for sequence header. + + Let me update the todo list and document what we've learned: + +● Progress Summary: + + ✅ Completed: + 1. Added codec_private_data and codec_private_size fields to VideoMetadata + 2. Modified WebMFileReader to extract codec private data using libwebm's GetCodecPrivate() API (19 bytes extracted successfully) + 3. Modified NVDECAV1Decoder to send codec private data to NVDEC parser + 4. Fixed VavCore.cpp to use full VideoMetadata from WebMFileReader (preserving codec_private_data pointers) + + ❌ Current Issue: + - cuvidParseVideoData() returns error 999 (CUDA_ERROR_UNKNOWN) when parsing codec private data + - This suggests the codec private data format or flags are incorrect for AV1 + + 🔍 Next Steps: + The error indicates that NVDEC parser doesn't accept the codec private data in the current format. For AV1, the codec private data in WebM (CodecPrivate) contains the AV1 Codec Configuration + Record (av1C), which needs special handling before sending to NVDEC parser. We may need to: + + 1. Parse the av1C box to extract the actual OBU_SEQUENCE_HEADER + 2. Send it with different flags (e.g., CUVID_PKT_ENDOFPICTURE) + 3. Or combine it with the first frame packet + + Would you like me to continue investigating and fixing the NVDEC codec private data handling? + + + + + +● ffmpeg 코드를 보니 sequence header를 별도로 전송하는 것이 아니라, frame 디코딩 시 sequence header 정보를 CUVIDAV1PICPARAMS 구조체에 담아서 전달하는 것 같습니다. + + 즉, NVDEC는 AV1의 경우 codec private data를 별도로 파싱하지 않고, 각 프레임 디코딩 시 필요한 sequence header 정보를 프레임 파라미터와 함께 전달하는 방식입니다. + + 우리의 현재 접근 방식이 잘못되었을 수 있습니다. AV1에서는 sequence header를 별도 패킷으로 보내는 것이 아니라, 프레임 패킷을 파싱할 때 함께 처리되어야 할 수도 있습니다. + + 다른 접근을 시도해보겠습니다. codec private data를 별도로 전송하지 말고, 첫 번째 프레임 패킷에 codec private data를 앞에 붙여서 전송하는 방식을 시도해보겠습니다: diff --git a/vav2/platforms/windows/vavcore/src/Common/VideoTypes.h b/vav2/platforms/windows/vavcore/src/Common/VideoTypes.h index 6c56624..db37c40 100644 --- a/vav2/platforms/windows/vavcore/src/Common/VideoTypes.h +++ b/vav2/platforms/windows/vavcore/src/Common/VideoTypes.h @@ -63,6 +63,10 @@ struct VideoMetadata { std::string file_path; uint64_t file_size = 0; + // Codec private data (e.g., AV1 sequence header from WebM CodecPrivate) + const uint8_t* codec_private_data = nullptr; + size_t codec_private_size = 0; + bool IsValid() const { return width > 0 && height > 0 && frame_rate > 0.0; } diff --git a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp index 7f77ef2..edef18a 100644 --- a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp +++ b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp @@ -158,6 +158,51 @@ bool NVDECAV1Decoder::Initialize(const VideoMetadata& metadata) { return false; } + // Parse av1C box to extract OBUs + if (metadata.codec_private_data && metadata.codec_private_size > 0) { + char debug_buf[256]; + + // av1C box structure (ISO/IEC 14496-15): + // [0]: marker (1 bit) + version (7 bits) + // [1]: seq_profile (3 bits) + seq_level_idx_0 (5 bits) + // [2]: seq_tier_0 (1 bit) + high_bitdepth (1 bit) + twelve_bit (1 bit) + monochrome (1 bit) + + // chroma_subsampling_x (1 bit) + chroma_subsampling_y (1 bit) + chroma_sample_position (2 bits) + // [3]: reserved (3 bits) + initial_presentation_delay_present (1 bit) + initial_presentation_delay_minus_one (4 bits) + // [4...]: configOBUs (length-prefixed OBUs) + + if (metadata.codec_private_size >= 5) { + // Skip av1C header (4 bytes) and check if there are OBUs + const uint8_t* obu_data = metadata.codec_private_data + 4; + size_t obu_size = metadata.codec_private_size - 4; + + if (obu_size > 0) { + m_codecPrivateData = obu_data; + m_codecPrivateSize = obu_size; + m_firstFrameSent = false; + + sprintf_s(debug_buf, "[Initialize] Extracted %zu bytes of OBUs from av1C box (skipped 4-byte header)\n", m_codecPrivateSize); + OutputDebugStringA(debug_buf); + printf("%s", debug_buf); + + // Debug: print first few bytes + sprintf_s(debug_buf, "[Initialize] OBU data (first 8 bytes): %02X %02X %02X %02X %02X %02X %02X %02X\n", + obu_data[0], obu_data[1], obu_data[2], obu_data[3], + obu_data[4], obu_data[5], obu_data[6], obu_data[7]); + OutputDebugStringA(debug_buf); + printf("%s", debug_buf); + } else { + OutputDebugStringA("[Initialize] WARNING: No OBUs found in av1C box\n"); + printf("[Initialize] WARNING: No OBUs found in av1C box\n"); + } + } else { + OutputDebugStringA("[Initialize] WARNING: av1C box too small (< 5 bytes)\n"); + printf("[Initialize] WARNING: av1C box too small (< 5 bytes)\n"); + } + } else { + OutputDebugStringA("[Initialize] WARNING: No codec private data available\n"); + printf("[Initialize] WARNING: No codec private data available\n"); + } + // Load the PTX module for the deinterleave kernel CUresult result = cuModuleLoadData(&m_module, g_deinterleave_kernel_ptx); if (result != CUDA_SUCCESS) { @@ -650,11 +695,15 @@ bool NVDECAV1Decoder::CreateDecoder() { return true; } + bool NVDECAV1Decoder::CreateParser() { + OutputDebugStringA("[CreateParser] Starting parser creation...\n"); + memset(&m_parserParams, 0, sizeof(m_parserParams)); m_parserParams.CodecType = cudaVideoCodec_AV1; - m_parserParams.ulMaxNumDecodeSurfaces = 8; + m_parserParams.ulMaxNumDecodeSurfaces = 1; + m_parserParams.ulMaxDisplayDelay = 1; // CRITICAL: Required for pfnDisplayPicture to be called m_parserParams.ulClockRate = 0; // Use default m_parserParams.ulErrorThreshold = 100; m_parserParams.pUserData = this; @@ -668,6 +717,11 @@ bool NVDECAV1Decoder::CreateParser() { return false; } + char debug_buf[256]; + sprintf_s(debug_buf, "[CreateParser] Parser created successfully! m_parser=%p, ulMaxDisplayDelay=%d\n", + m_parser, m_parserParams.ulMaxDisplayDelay); + OutputDebugStringA(debug_buf); + return true; } @@ -691,10 +745,12 @@ int CUDAAPI NVDECAV1Decoder::HandleVideoSequence(void* user_data, CUVIDEOFORMAT* } char debug_buf[512]; - sprintf_s(debug_buf, "[NVDECAV1Decoder::HandleVideoSequence] Sequence: %dx%d ChromaFormat:%d BitDepth:%d\n", + sprintf_s(debug_buf, "[HandleVideoSequence] Sequence: %dx%d ChromaFormat:%d BitDepth:%d min_num_decode_surfaces:%d\n", format->coded_width, format->coded_height, - format->chroma_format, format->bit_depth_luma_minus8 + 8); + format->chroma_format, format->bit_depth_luma_minus8 + 8, + format->min_num_decode_surfaces); OutputDebugStringA(debug_buf); + printf("%s", debug_buf); // Check if decoder needs reconfiguration due to format change bool format_changed = false; @@ -758,7 +814,14 @@ int CUDAAPI NVDECAV1Decoder::HandleVideoSequence(void* user_data, CUVIDEOFORMAT* } } - return 1; // Success + // Return min_num_decode_surfaces to update parser's ulMaxNumDecodeSurfaces + // This is critical for proper DPB (decode picture buffer) allocation + int return_value = (format->min_num_decode_surfaces > 1) ? format->min_num_decode_surfaces : 1; + sprintf_s(debug_buf, "[HandleVideoSequence] Returning %d to update ulMaxNumDecodeSurfaces\n", return_value); + OutputDebugStringA(debug_buf); + printf("%s", debug_buf); + + return return_value; } int CUDAAPI NVDECAV1Decoder::HandlePictureDecode(void* user_data, CUVIDPICPARAMS* pic_params) { @@ -822,13 +885,20 @@ int CUDAAPI NVDECAV1Decoder::HandlePictureDisplay(void* user_data, CUVIDPARSERDI slot_idx % decoder->RING_BUFFER_SIZE, disp_info->picture_index); OutputDebugStringA(debug_buf); - // Update slot's picture_index (polling thread will query this) + // IMPORTANT: pfnDisplayPicture is called AFTER GPU decoding completes + // So we can directly mark the frame as ready without polling { std::lock_guard lock(slot.slot_mutex); slot.picture_index = disp_info->picture_index; + slot.is_ready = true; // Frame is already decoded and ready } - // Note: Polling thread will detect this and signal slot.frame_ready when decode completes + // Signal waiting thread that frame is ready + slot.frame_ready.notify_one(); + + sprintf_s(debug_buf, "[HandlePictureDisplay] Slot %zu marked ready (picture_index=%d)\\n", + slot_idx % decoder->RING_BUFFER_SIZE, disp_info->picture_index); + OutputDebugStringA(debug_buf); return 1; } @@ -1059,10 +1129,31 @@ bool NVDECAV1Decoder::DecodeToSurface(const uint8_t* packet_data, size_t packet_ } // ===== Component 2: Packet Submission ===== - // 4. Submit packet to NVDEC parser with slot index in timestamp + // 4. Prepare packet (prepend codec private data to first frame) + std::vector combined_packet; + const uint8_t* final_packet_data = packet_data; + size_t final_packet_size = packet_size; + + if (!m_firstFrameSent && m_codecPrivateData && m_codecPrivateSize > 0) { + // First frame: prepend codec private data (AV1 sequence header) + combined_packet.resize(m_codecPrivateSize + packet_size); + memcpy(combined_packet.data(), m_codecPrivateData, m_codecPrivateSize); + memcpy(combined_packet.data() + m_codecPrivateSize, packet_data, packet_size); + + final_packet_data = combined_packet.data(); + final_packet_size = combined_packet.size(); + m_firstFrameSent = true; + + sprintf_s(debug_buf, "[DecodeToSurface] First frame: prepended %zu bytes of codec private data (total: %zu bytes)\n", + m_codecPrivateSize, final_packet_size); + OutputDebugStringA(debug_buf); + printf("%s", debug_buf); + } + + // 5. Submit packet to NVDEC parser with slot index in timestamp CUVIDSOURCEDATAPACKET packet = {}; - packet.payload = packet_data; - packet.payload_size = static_cast(packet_size); + packet.payload = final_packet_data; + packet.payload_size = static_cast(final_packet_size); packet.flags = CUVID_PKT_ENDOFPICTURE; packet.timestamp = static_cast(my_slot_idx); // Embed slot index in timestamp diff --git a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h index 67f6c5f..6de79af 100644 --- a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h +++ b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h @@ -115,6 +115,11 @@ private: // Decoder configuration CUVIDPARSERPARAMS m_parserParams = {}; + // Codec private data (AV1 sequence header from WebM) + const uint8_t* m_codecPrivateData = nullptr; + size_t m_codecPrivateSize = 0; + bool m_firstFrameSent = false; + // Statistics uint64_t m_framesDecoded = 0; uint64_t m_decodeErrors = 0; diff --git a/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp b/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp index 22a97e1..c31d309 100644 --- a/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp +++ b/vav2/platforms/windows/vavcore/src/FileIO/WebMFileReader.cpp @@ -673,6 +673,31 @@ 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); + if (track && track->GetType() == mkvparser::Track::kVideo) { + const mkvparser::VideoTrack* video_track = static_cast(track); + + size_t codec_private_size = 0; + const unsigned char* codec_private_data = video_track->GetCodecPrivate(codec_private_size); + + if (codec_private_data && codec_private_size > 0) { + meta.codec_private_data = codec_private_data; + meta.codec_private_size = codec_private_size; + + char debug_buf[256]; + sprintf_s(debug_buf, "[WebMFileReader] Extracted codec private data: %zu bytes\n", codec_private_size); + OutputDebugStringA(debug_buf); + printf("%s", debug_buf); + } else { + OutputDebugStringA("[WebMFileReader] WARNING: No codec private data found in WebM track\n"); + printf("[WebMFileReader] WARNING: No codec private data found in WebM track\n"); + } + } + } + return meta.IsValid(); } diff --git a/vav2/platforms/windows/vavcore/src/VavCore.cpp b/vav2/platforms/windows/vavcore/src/VavCore.cpp index 9fcc424..b51c68a 100644 --- a/vav2/platforms/windows/vavcore/src/VavCore.cpp +++ b/vav2/platforms/windows/vavcore/src/VavCore.cpp @@ -328,14 +328,8 @@ VAVCORE_API VavCoreResult vavcore_open_file(VavCorePlayer* player, const char* f OutputDebugStringA("[VavCore] AV1 track found! Selecting track...\n"); if (player->impl->fileReader->SelectVideoTrack(track.track_number)) { OutputDebugStringA("[VavCore] Track selected successfully\n"); - // Convert track info to VideoMetadata - VideoMetadata metadata; - metadata.width = track.width; - metadata.height = track.height; - metadata.frame_rate = track.frame_rate; - metadata.total_frames = track.frame_count; - metadata.codec_type = track.codec_type; - player->impl->metadata = metadata; + // Get full metadata from WebMFileReader (includes codec_private_data) + player->impl->metadata = player->impl->fileReader->GetVideoMetadata(); foundAV1 = true; break; }