簡體   English   中英

播放 PCM 解碼的音頻時沒有聲音

[英]No Sound When Playing Back PCM-Decoded Audio

我正在讀取 AAC 音頻幀,然后使用 Media Foundation 將其解碼為 PCM,並嘗試通過 WASAPI 播放。 特別是 48000khz 2 通道,16 位。 我能夠解碼幀,將它們寫入文件full.pcm ,然后在 Audacity 中成功打開並播放該 PCM 文件。 但是,我通過設備揚聲器播放的代碼什么也沒給我。 我嘗試播放的源是默認源,即我的 DAC。 我沒有從任何與 WASAPI 相關的代碼中得到任何不好的 HRESULTS,所以我很困惑。 WASAPI 對我來說是新的,所以也許我缺少一些明顯的東西。

#include "AudioDecoder.h"
#include <vector>
#include <__msvc_chrono.hpp>
#include <string>
#include <fstream>
#include <cassert>
#include <filesystem>

#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys.h> 
#include <audioclient.h>

int fps_counter = 0;
int frame_index = 0;

IAudioClient* audio_client;
IAudioRenderClient* render_client = nullptr;

int setup_audio_playback()
{
    HRESULT hr = S_OK;

    IMMDeviceEnumerator* pEnumerator = nullptr;
    IMMDevice* pDevice = nullptr;

    ATLENSURE_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator));

    ATLENSURE_SUCCEEDED(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice));

    IPropertyStore* ips;
    ATLENSURE_SUCCEEDED(pDevice->OpenPropertyStore(STGM_READ, &ips));

    PROPVARIANT varName;
    // Initialize container for property value.
    PropVariantInit(&varName);
    ATLENSURE_SUCCEEDED(ips->GetValue(PKEY_Device_FriendlyName, &varName));

    std::wcout << L"Device name: " << varName.pwszVal << std::endl;

    ATLENSURE_SUCCEEDED(pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&audio_client));

    WAVEFORMATEX* format;
    ATLENSURE_SUCCEEDED(audio_client->GetMixFormat(&format));

    ATLENSURE_SUCCEEDED(audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, format, NULL));

    uint32_t bufferFrameCount;
    ATLENSURE_SUCCEEDED(audio_client->GetBufferSize(&bufferFrameCount));

    ATLENSURE_SUCCEEDED(audio_client->GetService(__uuidof(IAudioRenderClient), (void**)&render_client));

    ATLENSURE_SUCCEEDED(audio_client->Start());

    return hr;
}

int main()
{
    HRESULT hr = S_OK;

    std::ofstream fout_all_frames_pcm;

    std::filesystem::remove(std::filesystem::current_path() / "full.pcm");

    fout_all_frames_pcm.open("full.pcm", std::ios::binary | std::ios::out);

    if (FAILED(hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)))
        return hr;
    if (FAILED(hr = MFStartup(MF_VERSION)))
        return hr;

    setup_audio_playback();

    AudioDecoder* ad = new AudioDecoder();

    std::vector<uint8_t> data;

    while (true)
    {
        std::chrono::time_point<std::chrono::steady_clock> iteration_time = std::chrono::high_resolution_clock::now();

        // Read frame data
        std::ifstream fin("Encoded Audio Frames\\frame" + std::to_string(frame_index) + ".aac", std::ios::binary | std::ios::in);

        if (fin.fail())
        {
            //throw std::runtime_error("Invalid file path specified");
            break;
        }

        // Get file length
        fin.seekg(0, std::ios::end);
        size_t const length = fin.tellg();
        fin.seekg(0, std::ios::beg);

        if (length > data.size())
        {
            static size_t constexpr const granularity = 64 << 10;
            data.resize((length + (granularity - 1)) & ~(granularity - 1));
            assert(length <= data.size());
        }

        // Copy frame data from file to array;
        fin.read(reinterpret_cast<char*>(data.data()), length);
        fin.close();

        CComPtr<IMFSample> pcm_sample;
        while (!ad->decode_sync(data.data(), length, &pcm_sample))
        {
            if (pcm_sample == nullptr) // This will happen if the color converter isn't able to produce output, so we will continue in that case
                continue;

            CComPtr<IMFMediaBuffer> buffer;
            if (FAILED(hr = pcm_sample->ConvertToContiguousBuffer(&buffer)))
                return hr;

            unsigned char* datas;
            DWORD length;
            if (FAILED(hr = buffer->GetCurrentLength(&length)))
                return hr;

            if (FAILED(hr = buffer->Lock(&datas, nullptr, &length)))
                return hr;

            fout_all_frames_pcm.write((char*)datas, length);

            // Does nothing
            //Sleep(120);

            // Grab all the available space in the shared buffer.
            uint8_t* pData;
            ATLENSURE_SUCCEEDED(render_client->GetBuffer(1, &pData));

            memcpy(pData, datas, length);

            DWORD flags = 0;
            ATLENSURE_SUCCEEDED(render_client->ReleaseBuffer(1, flags));

            pcm_sample.Release();
        }

        frame_index++;
    }

    audio_client->Stop();

    return 0;
}

正在做

render_client->GetBuffer(1, ...

不會給您任何穩定的行為,因為您正在嘗試逐個樣本提交數據。 從字面上看,每秒 48000 個樣本中的一個 PCM 樣本。 當然,代碼可能會被破壞更多,因為您似乎只是丟失了大部分數據,從解碼器獲得了更多數據,並且只向設備提供了一個樣本。

您可能希望在代碼確定GetBuffer將攜帶多少樣本的部分中查看這篇文章,然后循環准確地填充這些緩沖區,直到您使用您的IMFsample數據。

這些緩沖區有多大,您使用GetBuffer獲得的緩沖區有多大? 對於非常典型的 10 ms 緩沖區和 48 kHz 采樣率,每個緩沖區將有 480 個樣本。 使用立體聲和 16 位 PCM,每個樣本有 4 個字節,因此每次GetBuffer / ReleaseBuffer迭代將提供大約 2K 字節。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM