使用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:
若需编码为WMA,需额外配置编码参数并确保编码器可用,优先先匹配输入数据格式验证声音正常。hr = pAudioType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); - 时间戳计算精度错误:原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编码,并设置编码比特率:
此配置会让SinkWriter自动将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); - 检查视频压缩配置:若视频未使用WMV压缩格式(如
MFVideoFormat_WMV3),也会导致体积过大,需同步确认视频媒体类型设置。
其他关键注意事项
- 严格检查HRESULT返回值:即使无报错,调试时需确保每一步操作的返回值均为
S_OK,排查隐性错误。 - 写入完成后调用Finalize:所有数据写入完毕后,调用
pSinkWriter->Finalize()确保数据完全写入文件:hr = pSinkWriter->Finalize();
内容的提问来源于stack exchange,提问作者Jack Waterhouse
相关产品推荐
相关产品推荐

