You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用IMFSinkWriter生成WMV无音频且文件过大问题求助

问题描述

使用IMFSinkWriter创建包含音视频的WMV文件时,视频功能完全正常,音频写入过程无报错,但播放生成的WMV文件时没有声音。此外,用FFmpeg存储相同音视频内容生成的WMV文件约2MB,而代码生成的文件约77MB。音频存储格式为PCMWAVEFORMAT后跟随16位短整型采样数据,怀疑遗漏了IMFSinkWriter及其组件所需的参数,寻求解决建议。

相关代码片段

HRESULT CreateAudioSinkWriter(IMFSinkWriter *pSinkWriter,DWORD *pStreamIndexAudio,HANDLE hAudio)
{
        PCMWAVEFORMAT *pwave = NULL;
        IMFMediaType *pAudioType = NULL;
        DWORD streamIndexAudio = 0;
        HRESULT hr;
        long nChannels = 2;
        long nSamplesPerSec = 44100;
        long wBitsPerSample = 16;
        long nBlockAlign = 4;
        long nAvgBytesPerSec = 176400;

        if(hAudio != NULL) {
                if((pwave = (PCMWAVEFORMAT *)GlobalLock(hAudio)) != NULL) {
                        nChannels = pwave->wf.nChannels;
                        nSamplesPerSec = pwave->wf.nSamplesPerSec;
                        wBitsPerSample = pwave->wBitsPerSample;

                        nBlockAlign = nChannels * (wBitsPerSample / 8);
                        nAvgBytesPerSec = nBlockAlign * nSamplesPerSec;

                        GlobalUnlock(hAudio);
                }
        }

        hr = MFCreateMediaType(&pAudioType);
        hr = pAudioType->SetGUID(MF_MT_MAJOR_TYPE,MFMediaType_Audio);
        hr = pAudioType->SetGUID(MF_MT_SUBTYPE,MFAudioFormat_WMAudioV8);
        hr = pAudioType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE,wBitsPerSample);
        hr = pAudioType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND,nSamplesPerSec);
        hr = pAudioType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS,nChannels);
        hr = pAudioType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT,nBlockAlign);
        hr = pAudioType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND,nAvgBytesPerSec);
        hr = pAudioType->SetUINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX,TRUE);
        hr = pSinkWriter->AddStream(pAudioType,&streamIndexAudio);

        *pStreamIndexAudio = streamIndexAudio;

        SafeRelease(pAudioType);

        return(hr);
}

HRESULT CreateSinkWriter(const WCHAR *outputFile,IMFSinkWriter **ppWriter,DWORD *pStreamIndex,DWORD *pStreamIndexAudio,long width,long height,long rate,HANDLE hAudio) 
{
        *ppWriter = NULL;
        *pStreamIndex = NULL;
        *pStreamIndexAudio = NULL;

        IMFSinkWriter *pSinkWriter=NULL;
        IMFAttributes *pAttributes=NULL;
        HRESULT hr;
 
        hr = MFCreateAttributes(&pAttributes,2);
    hr = pAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS,TRUE);
    hr = pAttributes->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING,TRUE);
        hr = MFCreateSinkWriterFromURL(outputFile,NULL,pAttributes,&pSinkWriter);
        hr = CreateVideoSinkWriter(pSinkWriter,pStreamIndex,width,height,rate);
        hr = CreateAudioSinkWriter(pSinkWriter,pStreamIndexAudio,hAudio);
        hr = pSinkWriter->BeginWriting();       

        *ppWriter = pSinkWriter;
        (*ppWriter)->AddRef();

        SafeRelease(pAttributes);
        SafeRelease(pSinkWriter);
      
        return(hr);
}


HRESULT WriteAudioData(IMFSinkWriter *pWriter,DWORD streamIndex,HANDLE hAudio)
{
        HRESULT hr = S_OK;
        IMFSample *pSample=NULL;
        IMFMediaBuffer *pBuffer=NULL;
        DWORD sampleSize = GlobalSize(hAudio) - sizeof(PCMWAVEFORMAT);
        PCMWAVEFORMAT *pwave;
        LONGLONG duration;
        BYTE *pData;
        char *ps;
        DWORD chunkSize = 8192;
        LONGLONG sampleTime = 0;

        if((ps = (char *)GlobalLock(hAudio)) == NULL) return(E_OUTOFMEMORY);

        pwave = (PCMWAVEFORMAT *)ps;
        ps += sizeof(PCMWAVEFORMAT);

        while(sampleSize > 0) {
                DWORD currentChunkSize = min(chunkSize,sampleSize);

                duration = (LONGLONG)((double)currentChunkSize / (pwave->wf.nSamplesPerSec * pwave->wf.nBlockAlign) * 10000000);
                
                hr = MFCreateMemoryBuffer(currentChunkSize,&pBuffer);
                hr = pBuffer->Lock(&pData,NULL,NULL);
                
                memcpy(pData,ps,currentChunkSize);

                hr = pBuffer->Unlock();
                hr = pBuffer->SetCurrentLength(currentChunkSize);
                hr = MFCreateSample(&pSample);
                hr = pSample->AddBuffer(pBuffer);
        hr = pSample->SetSampleTime(sampleTime);
        hr = pSample->SetSampleDuration(duration);
        hr = pWriter->WriteSample(streamIndex,pSample);
                
                ps += currentChunkSize;
                sampleSize -= currentChunkSize;
                sampleTime += duration;

                SafeRelease(pBuffer);
                SafeRelease(pSample);
        }

        SafeRelease(pBuffer);
        SafeRelease(pSample);
        GlobalUnlock(hAudio);
        return(hr);
}

解决建议

无声音问题修复

  • 音频媒体类型不匹配:当前代码设置音频子类型为MFAudioFormat_WMAudioV8(WMA编码格式),但写入的是原始PCM数据,格式不兼容导致播放器无法解析。应将子类型改为PCM对应的MFAudioFormat_PCM:
    hr = pAudioType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
    
    若需编码为WMA,需额外配置编码参数并确保编码器可用,优先先匹配输入数据格式验证声音正常。
  • 时间戳计算精度错误:原duration计算使用浮点运算易丢失精度,导致播放器无法正确同步音频。修正为整数运算:
    DWORD sampleCount = currentChunkSize / pwave->wf.nBlockAlign;
    duration = (LONGLONG)sampleCount * 10000000 / pwave->wf.nSamplesPerSec;
    
  • 补充PCM格式关键参数:添加MF_MT_ALL_SAMPLES_INDEPENDENT参数明确为独立采样:
    hr = pAudioType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
    

文件体积过大问题修复

  • 启用音频压缩编码:当前写入未压缩的原始PCM数据是体积过大的核心原因。需配置输入类型为PCM、输出类型为WMA编码,并设置编码比特率:
    // 创建输入媒体类型(匹配原始PCM数据)
    IMFMediaType *pInputType = NULL;
    hr = MFCreateMediaType(&pInputType);
    hr = pInputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
    hr = pInputType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
    hr = pInputType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, wBitsPerSample);
    hr = pInputType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, nSamplesPerSec);
    hr = pInputType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, nChannels);
    hr = pInputType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, nBlockAlign);
    hr = pInputType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, nAvgBytesPerSec);
    hr = pInputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
    
    // 创建输出媒体类型(WMA编码)
    IMFMediaType *pOutputType = NULL;
    hr = MFCreateMediaType(&pOutputType);
    hr = pOutputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
    hr = pOutputType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV8);
    hr = pOutputType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, nSamplesPerSec);
    hr = pOutputType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, nChannels);
    // 设置128kbps比特率,转换为字节每秒:128000 / 8 = 16000
    hr = pOutputType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 16000);
    
    // 添加流并设置输入输出类型
    hr = pSinkWriter->AddStream(pInputType, &streamIndexAudio);
    hr = pSinkWriter->SetInputMediaType(streamIndexAudio, pInputType, NULL);
    hr = pSinkWriter->SetOutputMediaType(streamIndexAudio, pOutputType, NULL);
    
    此配置会让SinkWriter自动将PCM编码为WMA,大幅降低文件体积。
  • 检查视频压缩配置:若视频未使用WMV压缩格式(如MFVideoFormat_WMV3),也会导致体积过大,需同步确认视频媒体类型设置。

其他关键注意事项

  • 严格检查HRESULT返回值:即使无报错,调试时需确保每一步操作的返回值均为S_OK,排查隐性错误。
  • 写入完成后调用Finalize:所有数据写入完毕后,调用pSinkWriter->Finalize()确保数据完全写入文件:
    hr = pSinkWriter->Finalize();
    

内容的提问来源于stack exchange,提问作者Jack Waterhouse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 19:33:09