Конвертация сэмплов полученных с помощью 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 шт):
Аудиоданные в 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?