Как преобразовать частоту дискретизации из AV SAMPLE FMT FLTP в AV SAMPLE FMT S16?
Я декодирую aac в pcm с ffmpeg с помощью avcodec_decode_audio3. Однако он декодируется в формат образца AV_SAMPLE_FMT_FLTP (PCM 32bit Float Planar), и мне нужен AV_SAMPLE_FMT_S16 (PCM 16 bit signed - S16LE).
Я знаю, что ffmpeg может сделать это легко с -sample_fmt. Я хочу сделать то же самое с кодом, но я до сих пор не мог понять.
audio_resample не работает: выдает сообщение об ошибке .... преобразование не удалось.
3 ответов
редактировать 9 апреля 2013: разработал, как использовать libswresample для этого... гораздо быстрее!
в какой-то момент за последние 2-3 года выходной формат декодера AAC FFmpeg изменился с AV_SAMPLE_FMT_S16 на AV_SAMPLE_FMT_FLTP. Это означает, что каждый аудиоканал имеет собственный буфер, а каждое значение выборки представляет собой 32-разрядное значение с плавающей запятой, масштабируемое от -1.0 до +1.0.
тогда как с AV_SAMPLE_FMT_S16 данные находятся в одном буфере, с образцами перемежается, и каждый образец представляет собой целое число со знаком от -32767 до +32767.
и если вам действительно нужен ваш аудио как AV_SAMPLE_FMT_S16, то вы должны сделать преобразование самостоятельно. Я придумал два способа сделать это:--6-->
1. Используйте libswresample (рекомендуется)
#include "libswresample/swresample.h"
...
SwrContext *swr;
...
// Set up SWR context once you've got codec information
swr = swr_alloc();
av_opt_set_int(swr, "in_channel_layout", audioCodec->channel_layout, 0);
av_opt_set_int(swr, "out_channel_layout", audioCodec->channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", audioCodec->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", audioCodec->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
swr_init(swr);
...
// In your decoder loop, after decoding an audio frame:
AVFrame *audioFrame = ...;
int16_t* outputBuffer = ...;
swr_convert(&outputBuffer, audioFrame->nb_samples, audioFrame->extended_data, audioFrame->nb_samples);
и это все, что вам нужно сделать!
2. Сделайте это вручную в C (оригинальный ответ, не рекомендуется)
Итак, в вашем цикле декодирования, когда у вас есть аудио пакет вы декодируете его следующим образом:
AVCodecContext *audioCodec; // init'd elsewhere
AVFrame *audioFrame; // init'd elsewhere
AVPacket packet; // init'd elsewhere
int16_t* outputBuffer; // init'd elsewhere
int out_size = 0;
...
int len = avcodec_decode_audio4(audioCodec, audioFrame, &out_size, &packet);
и затем, если у вас есть полный кадр аудио, вы можете конвертировать его довольно легко:
// Convert from AV_SAMPLE_FMT_FLTP to AV_SAMPLE_FMT_S16
int in_samples = audioFrame->nb_samples;
int in_linesize = audioFrame->linesize[0];
int i=0;
float* inputChannel0 = (float*)audioFrame->extended_data[0];
// Mono
if (audioFrame->channels==1) {
for (i=0 ; i<in_samples ; i++) {
float sample = *inputChannel0++;
if (sample<-1.0f) sample=-1.0f; else if (sample>1.0f) sample=1.0f;
outputBuffer[i] = (int16_t) (sample * 32767.0f);
}
}
// Stereo
else {
float* inputChannel1 = (float*)audioFrame->extended_data[1];
for (i=0 ; i<in_samples ; i++) {
outputBuffer[i*2] = (int16_t) ((*inputChannel0++) * 32767.0f);
outputBuffer[i*2+1] = (int16_t) ((*inputChannel1++) * 32767.0f);
}
}
// outputBuffer now contains 16-bit PCM!
Я оставил пару вещей для ясности... зажимать в mono пути должен идеально быть дублирован в стерео пути. И код можно легко оптимизировать.
Я нашел 2 функции повторной выборки из FFMPEG. Производительность может быть лучше.
- avresample_convert() http://libav.org/doxygen/master/group__lavr.html
- swr_convert ()http://spirton.com/svn/MPlayer-SB/ffmpeg/libswresample/swresample_test.c
спасибо Рубену за решение этой проблемы. Я нашел, что некоторые из значений выборки были немного по сравнению с прямым через FFmpeg -I файл.в формате WAV. Кажется, что в преобразовании они используют round() для значения.
чтобы сделать преобразование, я сделал то, что вы сделали с предложением модификации для работы на любом количестве каналов:
if (audioCodecContext->sample_fmt == AV_SAMPLE_FMT_FLTP)
{
int nb_samples = decoded_frame->nb_samples;
int channels = decoded_frame->channels;
int outputBufferLen = nb_samples & channels * 2;
short* outputBuffer = new short[outputBufferLen/2];
for (int i = 0; i < nb_samples; i++)
{
for (int c = 0; c < channels; c++)
{
float* extended_data = (float*)decoded_frame->extended_data[c];
float sample = extended_data[i];
if (sample < -1.0f) sample = -1.0f;
else if (sample > 1.0f) sample = 1.0f;
outputBuffer[i * channels + c] = (short)round(sample * 32767.0f);
}
}
// Do what you want with the data etc.
}
Я пошел от ffmpeg 0.11.1 - > 1.1.3 и нашел изменение формата образца раздражающим. Я посмотрел на установку request_sample_fmt в AV_SAMPLE_FMT_S16, но кажется, что ААС декодер не поддерживает ничего кроме AV_SAMPLE_FMT_FLTP.