Конвертация сэмплов полученных с помощью wasapi в AVFrame FFmpeg & C++

Я получаю сэмплы с помощью wasapi в виде BYTE. И хочу записать их в файл с помощью FFmpeg. Сами сэмплы я получаю так:

DWORD AudioCapture::recordAudio()
{
    HRESULT hr;
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);

    hr = pCaptureClient->GetNextPacketSize(&packetLength);
    if (FAILED(hr))
    {
      printf("Faild to get next data packet size\n");
      return hr;
    }
    while (packetLength != 0)
    {
      hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
      if (FAILED(hr))
      {
        printf("Faild to get buffer\n");
        return hr;
      }
      if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
      {
        pData = NULL;
        BYTE * buffer = new BYTE[(numFramesAvailable * pwfx->nBlockAlign)];
        memcpy(buffer, 0, pwfx->nBlockAlign * numFramesAvailable);
        m_curAudioData.push_back({buffer, numFramesAvailable});
      }
      else
      {
        BYTE * buffer = new BYTE[(numFramesAvailable * pwfx->nBlockAlign)];
        memcpy(buffer, pData, numFramesAvailable * pwfx->nBlockAlign);
        m_curAudioData.push_back({buffer, numFramesAvailable});
      }
      hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
      if (FAILED(hr))
      {
        printf("Faild to release buffer\n");
        return hr;
      }
      hr = pCaptureClient->GetNextPacketSize(&packetLength);
      if (FAILED(hr))
      {
        printf("Faild to get next data packet size\n");
        return hr;
      }
    }
    return 0;
}

Каким образом я могу конвертировать эти байты в AVFrame?


Ответы (1 шт):

Автор решения: Roman R.

Аудиоданные в Windows API повсюду в виде сплошного массива, т.е. interleaved в терминах libav. В то же время в самом libav многие кодеки сделаны так, что им нужны planar данные, то есть разобранные по каналам. Сами "значения" отдельных семплов при этом - те же самые. То есть в конце концов, разница лишь в том, выделены ли отдельно каналы (AV_SAMPLE_FMT_FLTP) или нет (AV_SAMPLE_FMT_FLT).

По большому счету нет нужды перераспределять байты вызовом внешнего API, т.к. всё, что нужно сделать, по сути укладывается в такой цикл:

UINT ChannelCount = ...; // каналов аудио
FLOAT const* Input = reinterpret_cast<FLOAT const*>(...); // из WASAPI
AVFrame* Frame = ...; // подготовка фрейма для кодека
// ниже предполагается что nb_samples уже указывает количество сэмплов в буфере, а data указывает на массивы, 
// для кадого канала которые можно инициализировать отдельно или взять один общий блок памяти и в нем выделить
// отдельные куски для каждого канала
for(INT S = 0; S < Frame->nb_samples; S++)
    for(UINT C = 0; C < ChannelCount; C++)
    {
        FLOAT const& I = Input[S * ChannelCount + C];
        FLOAT& O = reinterpret_cast<FLOAT*>(Frame->data[C])[S];
        O = I;
    }

То же самое, конечно, можно сделать и средствами libav.

См. также What is the difference between AV_SAMPLE_FMT_S16P and AV_SAMPLE_FMT_S16?

→ Ссылка