IAudioClient::Initialize报错AUDCLNT_E_UNSUPPORTED_FORMAT(0x88890008)的解决办法

音频流初始化失败:AUDCLNT_E_UNSUPPORTED_FORMAT解决策略
本文讲述了在使用IAudioClient初始化音频流时遇到AUDCLNT_E_UNSUPPORTED_FORMAT错误的解决方案,重点在于排查WAVEFORMATEX格式问题,调整代码顺序以确保格式支持,并提供相关文档链接。

IAudioClient::Initialize

初始化音频流的时候发生报错AUDCLNT_E_UNSUPPORTED_FORMAT(0x88890008),先上产生了错误的代码:

    WAVEFORMATEX* wfex;
    HRESULT res;
    DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
    flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;

    res = Device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr,
        (void**)&ac);
    if (FAILED(res))
        throw HRError("Failed to activate client context",res);

    res = AudioClient->GetMixFormat(&wfex);
    if (FAILED(res))
        throw HRError("Failed to get mix format",res);

    InitFormat(wfex);

    CoTaskMemFree(wfex);
    
    if (!m_isInputDevice)
        flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
    else
        flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
    
    res = ac->Initialize(AUDCLNT_SHAREMODE_SHARED, flags,
        BUFFER_TIME_100NS, 0, wfex, nullptr);
    if (FAILED(res))
        throw HRError("Failed to get initialize audio client",res);
    

解决思路:

首先查阅微软官方文档对该错误的解释:

AUDCLNT_E_UNSUPPORTED_FORMAT

The audio engine (shared mode) or audio endpoint device (exclusive mode) does not support the specified format.

并尝试使用IAudioClient::IsFormatSupported排查问题:

 WAVEFORMATEX* wfex2;
 res = ac->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, wfex, &wfex2);

发现上述代码的报错相同,开始排查WAVEFORMATEX的输入参数是否正确

通过跟代码发现wfex在GetMixFormat()赋值后,wFormatTag被CoTaskMemFree()修改了

GetMixFormat()赋值后的wfex
CoTaskMemFree()执行后的wfex

应该把CoTaskMemFree()放到Initialize()之后,程序正常。

最后贴上正常的代码:

    WAVEFORMATEX* wfex;
    HRESULT res;
    DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
    flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;

    res = Device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr,
        (void**)&ac);
    if (FAILED(res))
        throw HRError("Failed to activate client context",res);

    res = AudioClient->GetMixFormat(&wfex);
    if (FAILED(res))
        throw HRError("Failed to get mix format",res);

    InitFormat(wfex);

    if (!m_isInputDevice)
        flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
    else
        flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
    
    WAVEFORMATEX* wfex2;
    res = ac->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, wfex, &wfex2);
    res = ac->Initialize(AUDCLNT_SHAREMODE_SHARED, flags,
        BUFFER_TIME_100NS, 0, wfex, nullptr);
    if (FAILED(res))
        throw HRError("Failed to get initialize audio client",res);
    
    CoTaskMemFree(wfex);

参考文档:

https://docs.microsoft.com/zh-cn/windows/win32/api/audioclient/nf-audioclient-iaudioclient-initialize

https://docs.microsoft.com/zh-cn/windows/win32/api/audioclient/nf-audioclient-iaudioclient-isformatsupported

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <Windows.h> #include <process.h> #include <conio.h> #include <thread> #include <atomic> #include <mutex> #include <string> #include <chrono> #include <vector> #include <queue> #include <condition_variable> // OpenCV 头文件 #include <opencv2/opencv.hpp> // 海康威视 MVS SDK 头文件 #include "MvCameraControl.h" #pragma comment(lib, "MvCameraControl.lib") // 链接海康 SDK 库 #pragma comment(lib, "opencv_world480.lib") // 请根据实际 OpenCV 版本修改此行 // ==================== 配置参数 ==================== const char* const DESKTOP_TEST_DIR = "C:\\Users\\guolin12\\Desktop\\test"; const double RECORD_DURATION_SECONDS = 10.0; const size_t MAX_QUEUE_SIZE = 30; // ======================================== // 自定义图像帧结构体 // ======================================== struct ImageFrame { std::vector<uint8_t> data; MV_FRAME_OUT_INFO_EX info; std::chrono::steady_clock::time_point timestamp; }; // ======================================== // 全局变量 // ======================================== std::atomic<bool> g_bExit(false); std::atomic<bool> g_bIsRecording(false); std::atomic<unsigned int> g_pixelFormat(PixelType_Gvsp_Mono8); std::mutex g_queueMutex; std::queue<ImageFrame> g_frameQueue; std::condition_variable g_cvQueue; int g_width = 0, g_height = 0; float g_fps = 25.0f; // ======================================== // 工具函数 // ======================================== std::string GetTempPathStr() { char path[MAX_PATH] = { 0 }; GetTempPathA(MAX_PATH, path); return std::string(path); } bool CreateDirectoryRecursive(const std::string& dir) { if (CreateDirectoryA(dir.c_str(), nullptr) || GetLastError() == ERROR_ALREADY_EXISTS) return true; size_t pos = dir.find_last_of("\\/"); if (pos != std::string::npos) { std::string parent = dir.substr(0, pos); if (CreateDirectoryRecursive(parent)) { return CreateDirectoryA(dir.c_str(), nullptr) || GetLastError() == ERROR_ALREADY_EXISTS; } } return false; } bool IsDirectoryWritable(const std::string& dir) { if (!CreateDirectoryRecursive(dir)) return false; std::string testFile = dir + "\\__test_write__.tmp"; FILE* f = nullptr; errno_t err = fopen_s(&f, testFile.c_str(), "w"); if (err == 0 && f) { fclose(f); DeleteFileA(testFile.c_str()); return true; } return false; } std::string GetOutputDirectory() { std::string target(DESKTOP_TEST_DIR); if (IsDirectoryWritable(target)) { printf("✅ Using output directory: %s\n", target.c_str()); return target; } else { printf("❌ Cannot write to %s\n", target.c_str()); std::string temp = GetTempPathStr(); if (IsDirectoryWritable(temp)) { printf("💡 Falling back to: %s\n", temp.c_str()); return temp; } printf("❌ All paths unwritable!\n"); return ""; } } void PrintDeviceInfo(MV_CC_DEVICE_INFO* pstDevInfo) { if (pstDevInfo->nTLayerType == MV_GIGE_DEVICE) { unsigned int ip = pstDevInfo->SpecialInfo.stGigEInfo.nCurrentIp; printf("IP: %d.%d.%d.%d\n", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); printf("Model: %s\n", pstDevInfo->SpecialInfo.stGigEInfo.chModelName); } else if (pstDevInfo->nTLayerType == MV_USB_DEVICE) { printf("Model: %s\n", pstDevInfo->SpecialInfo.stUsb3VInfo.chModelName); printf("Serial: %s\n", pstDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber); } } // 支持的格式优先级:RGB8 > BayerRG8 > Mono8 static const unsigned int SUPPORTED_PIXEL_FORMATS[] = { PixelType_Gvsp_RGB8_Packed, PixelType_Gvsp_BayerRG8, PixelType_Gvsp_Mono8 }; static const int NUM_SUPPORTED_FORMATS = 3; int SetPreferredPixelFormat(void* handle, unsigned int& selectedFormat) { for (int i = 0; i < NUM_SUPPORTED_FORMATS; ++i) { unsigned int fmt = SUPPORTED_PIXEL_FORMATS[i]; int nRet = MV_CC_SetEnumValue(handle, "PixelFormat", fmt); if (nRet == MV_OK) { const char* name = ""; switch (fmt) { case PixelType_Gvsp_Mono8: name = "Mono8"; break; case PixelType_Gvsp_BayerRG8: name = "BayerRG8"; break; case PixelType_Gvsp_RGB8_Packed: name = "RGB8"; break; default: name = "Unknown"; } printf("✅ Set PixelFormat to %s\n", name); selectedFormat = fmt; return MV_OK; } } printf("❌ No supported pixel format available!\n"); return 0; } bool AlignImageSize(void* handle, int& width, int& height) { MVCC_INTVALUE stValue = { 0 }; if (MV_CC_GetIntValue(handle, "Width", &stValue) != MV_OK) { printf("❌ Failed to get Width\n"); return false; } width = (stValue.nCurValue / 32) * 32; MV_CC_SetIntValue(handle, "Width", width); printf("🔧 Set Width to %d\n", width); if (MV_CC_GetIntValue(handle, "Height", &stValue) != MV_OK) { printf("❌ Failed to get Height\n"); return false; } height = (stValue.nCurValue / 2) * 2; MV_CC_SetIntValue(handle, "Height", height); printf("🔧 Set Height to %d\n", height); return true; } cv::Mat ConvertToBGR(const uint8_t* pData, int width, int height, unsigned int pixelFormat) { cv::Mat bgr; switch (pixelFormat) { case PixelType_Gvsp_Mono8: { cv::Mat gray(height, width, CV_8UC1, const_cast<uint8_t*>(pData)); cv::cvtColor(gray, bgr, cv::COLOR_GRAY2BGR); break; } case PixelType_Gvsp_BayerRG8: { cv::Mat bayer(height, width, CV_8UC1, const_cast<uint8_t*>(pData)); cv::cvtColor(bayer, bgr, cv::COLOR_BayerRG2BGR); break; } case PixelType_Gvsp_RGB8_Packed: { cv::Mat rgb(height, width, CV_8UC3, const_cast<uint8_t*>(pData)); cv::cvtColor(rgb, bgr, cv::COLOR_RGB2BGR); break; } default: printf("⚠️ Unsupported pixel format: 0x%x\n", pixelFormat); bgr = cv::Mat::zeros(height, width, CV_8UC3); break; } return bgr; } // ======================================== // 录像线程函数 // ======================================== void RecordFromQueue(const std::string& filePath, int width, int height, int targetFrameCount, float outputFps) { printf("🎥 Recording started: %s\n", filePath.c_str()); printf("🎯 Target frames: %d, Output FPS: %.3f → Duration: %.2f sec\n", targetFrameCount, outputFps, targetFrameCount / outputFps); cv::VideoWriter writer(filePath, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), outputFps, cv::Size(width, height), true); if (!writer.isOpened()) { printf("❌ Failed to open VideoWriter: %s\n", filePath.c_str()); g_bIsRecording = false; return; } int recorded = 0; auto start = std::chrono::steady_clock::now(); try { while (g_bIsRecording && !g_bExit && recorded < targetFrameCount) { std::unique_lock<std::mutex> lock(g_queueMutex); auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(3); if (g_frameQueue.empty()) { if (g_cvQueue.wait_until(lock, timeout) == std::cv_status::timeout) { printf("📹 Timeout waiting for frame during recording.\n"); break; } } if (!g_frameQueue.empty()) { ImageFrame frame = std::move(g_frameQueue.front()); g_frameQueue.pop(); lock.unlock(); cv::Mat bgr = ConvertToBGR(frame.data.data(), frame.info.nWidth, frame.info.nHeight, frame.info.enPixelType); if (bgr.empty()) continue; writer.write(bgr); recorded++; } } } catch (const std::exception& e) { printf("❌ Exception during recording: %s\n", e.what()); } catch (...) { printf("❌ Unknown error during recording.\n"); } writer.release(); auto durationMs = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start).count(); printf("⏹️ Saved video: %s\n", filePath.c_str()); printf("📊 Wrote %d frames at %.3f FPS → Play time: %.2f sec\n", recorded, outputFps, recorded / outputFps); printf("⏱️ Actual process time: %.3f sec\n", durationMs / 1000.0); g_bIsRecording = false; } // ======================================== // 主工作线程 // ======================================== unsigned int __stdcall WorkThread(void* pUser) { void* handle = pUser; cv::namedWindow("Live View", cv::WINDOW_AUTOSIZE); MV_FRAME_OUT stImageInfo = { 0 }; int nRet = MV_OK; // 获取并对齐图像尺寸 AlignImageSize(handle, g_width, g_height); // 👉 最大可能的数据大小(按 RGB 保守估计) size_t maxRotatedDataSize = g_width * g_height * 3; std::vector<uint8_t> rotatedBuffer(maxRotatedDataSize); MVCC_FLOATVALUE stFloat = { 0 }; if (MV_CC_GetFloatValue(handle, "ResultingFrameRate", &stFloat) == MV_OK) { g_fps = stFloat.fCurValue; printf("🎯 Actual Camera FPS: %.3f\n", g_fps); } else { printf("⚠️ Could not get FPS, using default %.2f\n", g_fps); } std::string outputDir = GetOutputDirectory(); while (!g_bExit) { nRet = MV_CC_GetImageBuffer(handle, &stImageInfo, 1000); if (nRet == MV_OK) { MV_FRAME_OUT_INFO_EX* pInfo = &stImageInfo.stFrameInfo; // 👉 计算源图像数据大小 auto GetImageSize = [](int w, int h, unsigned int fmt) -> size_t { switch (fmt) { case PixelType_Gvsp_Mono8: case PixelType_Gvsp_BayerGR8: case PixelType_Gvsp_BayerRG8: case PixelType_Gvsp_BayerGB8: case PixelType_Gvsp_BayerBG8: return static_cast<size_t>(w) * h; case PixelType_Gvsp_RGB8_Packed: return static_cast<size_t>(w) * h * 3; case PixelType_Gvsp_RGBA8_Packed: return static_cast<size_t>(w) * h * 4; default: return static_cast<size_t>(w) * h; } }; size_t srcLen = GetImageSize(pInfo->nWidth, pInfo->nHeight, pInfo->enPixelType); // 👉 初始化旋转参数 MV_CC_ROTATE_IMAGE_PARAM rotateParam ; rotateParam.enPixelType = static_cast<MvGvspPixelType>(pInfo->enPixelType); rotateParam.nWidth = pInfo->nWidth; rotateParam.nHeight = pInfo->nHeight; rotateParam.pSrcData = stImageInfo.pBufAddr; rotateParam.nSrcDataLen = static_cast<unsigned int>(srcLen); rotateParam.pDstBuf = rotatedBuffer.data(); rotateParam.nDstBufSize = static_cast<unsigned int>(rotatedBuffer.size()); rotateParam.enRotationAngle = MV_IMAGE_ROTATE_90; // 90度旋转:新宽=原高,新高=原宽 // 👉 执行旋转 nRet = MV_CC_RotateImage(handle, &rotateParam); if (nRet != MV_OK) { printf("❌ MV_CC_RotateImage failed! [0x%x]\n", nRet); MV_CC_FreeImageBuffer(handle, &stImageInfo); continue; } // ✅ 关键:使用 SDK 返回的实际输出尺寸和长度 int outWidth = rotateParam.nWidth; int outHeight = rotateParam.nHeight; size_t dataSize = static_cast<size_t>(rotateParam.nDstBufLen); // ✅ 安全检查 if (dataSize == 0 || dataSize > rotatedBuffer.size()) { printf("❌ Invalid rotated image size: %zu bytes\n", dataSize); MV_CC_FreeImageBuffer(handle, &stImageInfo); continue; } // ✅ 提取有效数据 std::vector<uint8_t> imgData(rotatedBuffer.begin(), rotatedBuffer.begin() + dataSize); // ✅ 转换为 BGR 显示 cv::Mat bgr = ConvertToBGR(imgData.data(), outWidth, outHeight, pInfo->enPixelType); if (!bgr.empty()) { std::string status = g_bIsRecording ? "🔴 RECORDING..." : "✅ LIVE STREAM"; cv::putText(bgr, status, cv::Point(20, 40), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2); cv::imshow("Live View", bgr); } // 👉 输入控制 int key = cv::waitKey(1); if (key == 27 || key == 'q' || key == 'Q') { g_bExit = true; } else if ((key == 'r' || key == 'R') && !g_bIsRecording) { if (outputDir.empty()) { printf("❌ No valid output directory!\n"); } else { g_bIsRecording = true; int targetFrameCount = static_cast<int>(RECORD_DURATION_SECONDS * g_fps); auto now = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000; std::time_t t = std::chrono::system_clock::to_time_t(now); struct tm tm; localtime_s(&tm, &t); char filePath[512]; sprintf_s(filePath, "%s\\rec_%04d%02d%02d_%02d%02d%02d_%03dms.avi", outputDir.c_str(), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)ms.count()); try { std::thread t(RecordFromQueue, std::string(filePath), outWidth, outHeight, targetFrameCount, g_fps); t.detach(); // 非阻塞录制 } catch (const std::exception& e) { printf("❌ Failed to start thread: %s\n", e.what()); g_bIsRecording = false; } } } // 👉 存入队列用于录制 if (g_bIsRecording) { std::lock_guard<std::mutex> lock(g_queueMutex); while (g_frameQueue.size() >= MAX_QUEUE_SIZE) { g_frameQueue.pop(); } ImageFrame frame; frame.data = std::move(imgData); frame.info = *pInfo; frame.info.nWidth = outWidth; // 更新为旋转后尺寸 frame.info.nHeight = outHeight; frame.timestamp = std::chrono::steady_clock::now(); g_frameQueue.push(std::move(frame)); g_cvQueue.notify_one(); } MV_CC_FreeImageBuffer(handle, &stImageInfo); } else { printf("⚠️ Timeout getting frame from camera.\n"); } } { std::lock_guard<std::mutex> lock(g_queueMutex); g_bIsRecording = false; } g_cvQueue.notify_all(); cv::destroyAllWindows(); return 0; } // ======================================== // 主函数 // ======================================== int main() { int nRet = MV_OK; void* handle = nullptr; HANDLE hThread = nullptr; do { nRet = MV_CC_Initialize(); if (nRet != MV_OK) { printf("❌ Initialize SDK failed! [0x%x]\n", nRet); break; } MV_CC_DEVICE_INFO_LIST stDevList = { 0 }; nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDevList); if (nRet != MV_OK || stDevList.nDeviceNum == 0) { printf("❌ No devices found! [0x%x]\n", nRet); break; } printf("✅ Found %d device(s):\n", stDevList.nDeviceNum); for (unsigned int i = 0; i < stDevList.nDeviceNum; ++i) { printf(" [%d]: ", i); PrintDeviceInfo(stDevList.pDeviceInfo[i]); } unsigned int index = 0; printf("👉 Input camera index (0-%d): ", stDevList.nDeviceNum - 1); scanf_s("%d", &index); if (index >= stDevList.nDeviceNum) { printf("❌ Invalid index!\n"); break; } nRet = MV_CC_CreateHandle(&handle, stDevList.pDeviceInfo[index]); if (nRet != MV_OK) { printf("❌ CreateHandle failed! [0x%x]\n", nRet); break; } nRet = MV_CC_OpenDevice(handle); if (nRet != MV_OK) { printf("❌ OpenDevice failed! [0x%x]\n", nRet); break; } if (stDevList.pDeviceInfo[index]->nTLayerType == MV_GIGE_DEVICE) { int packetSize = MV_CC_GetOptimalPacketSize(handle); if (packetSize > 0) { MV_CC_SetIntValueEx(handle, "GevSCPSPacketSize", packetSize); } } MV_CC_SetEnumValue(handle, "TriggerMode", 0); // 关闭触发,连续采集 unsigned int chosenFormat = 0; if (SetPreferredPixelFormat(handle, chosenFormat) != MV_OK) break; g_pixelFormat = chosenFormat; nRet = MV_CC_StartGrabbing(handle); if (nRet != MV_OK) { printf("❌ StartGrabbing failed! [0x%x]\n", nRet); break; } unsigned int threadId = 0; hThread = (HANDLE)_beginthreadex(nullptr, 0, WorkThread, handle, 0, &threadId); if (!hThread) { printf("❌ Failed to create work thread!\n"); break; } printf("\n📌 Instructions:\n"); printf(" ➡ Press 'R' to record %.1f seconds video\n", RECORD_DURATION_SECONDS); printf(" ➡ Live preview will NOT freeze!\n"); printf(" ➡ Output FPS matches actual camera FPS\n"); printf(" ➡ File name includes milliseconds to avoid overwrite\n"); printf(" ➡ Press 'Q' or ESC to exit\n\n"); while (!g_bExit) { Sleep(100); } } while (false); g_bExit = true; if (hThread) { WaitForSingleObject(hThread, 5000); CloseHandle(hThread); } if (handle) { MV_CC_StopGrabbing(handle); MV_CC_CloseDevice(handle); MV_CC_DestroyHandle(handle); } MV_CC_Finalize(); printf("👋 Program exited. Press any key to close.\n"); _getch(); return 0; } 旋转是在哪一步进行,需要增加按钮使能控制是否旋转,并代码补全注释
最新发布
11-22
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值