Я использую FFmpeg и пытаюсь кодировать и декодировать необработанный звук PCM в Opus, используя встроенный код «opus» FFmpeg c. Мои входные образцы - это необработанный PCM 8000 Гц 16-битный моно в формате AV_SAMPLE_FMT_S16. Поскольку для Opus требуется формат сэмплов AV_SAMPLE_FMT_FLTP и частота дискретизации только 48000 Гц, поэтому я перед кодированием делаю повторную выборку сэмплов. SwrContext
, я использую первый экземпляр ResamplerAudio
для передискретизации необработанного входного аудиосигнала PCM перед кодированием, а второй - для ресэмплинга декодированного аудио, чтобы получить его формат и частоту дискретизации, такие же, как исходные значения входного необработанного аудио.
Класс ResamplerAudio имеет функцию, которая инициализирует его член SwrContext следующим образом:
void ResamplerAudio::init(AVCodecContext *codecContext, int inSampleRate, int outSampleRate, AVSampleFormat inSampleFmt, AVSampleFormat outSampleFmt)
{
swrContext = swr_alloc();
if (!swrContext)
{
LOGE(TAG, "[init] Couldn't allocate swr context");
return;
}
av_opt_set_int(swrContext, "in_channel_layout", (int64_t) codecContext->channel_layout, 0);
av_opt_set_int(swrContext, "out_channel_layout", (int64_t) codecContext->channel_layout, 0);
av_opt_set_int(swrContext, "in_channel_count", codecContext->channels, 0);
av_opt_set_int(swrContext, "out_channel_count", codecContext->channels, 0);
av_opt_set_int(swrContext, "in_sample_rate", inSampleRate, 0);
av_opt_set_int(swrContext, "out_sample_rate", outSampleRate, 0);
av_opt_set_sample_fmt(swrContext, "in_sample_fmt", inSampleFmt, 0);
av_opt_set_sample_fmt(swrContext, "out_sample_fmt", outSampleFmt, 0);
int ret = swr_init(swrContext);
if (ret < 0)
{
LOGE(TAG, "[init] swr_init error: %s", av_err2str(ret));
return;
}
LOGD(TAG, "[init] success codecContext->channel_layout: %d; inSampleRate: %d; outSampleRate: %d; inSampleFmt: %d; outSampleFmt: %d", (int) codecContext->channel_layout, inSampleRate, outSampleRate, inSampleFmt, outSampleFmt);
}
И я вызываю функцию ResamplerAudio::init
для первого экземпляра ResamplerAudio
(этот экземпляр выполняет ресампинг исходного входного аудио PCM перед кодирование, и я назвал его resamplerEncoder
) со следующими аргументами:
resamplerEncoder->init(contextEncoder, 8000, 48000, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP);
Второй экземпляр ResamplerAudio
(этот экземпляр выполняет ресампинг после декодирования звука из Opus, и я назвал его resamplerDecoder
). со следующими аргументами:
resamplerDecoder->init(contextDecoder, 48000, 8000, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16);
Функция ResamplerAudio
, которая выполняет повторную выборку, выглядит следующим образом:
std::vector<uint8_t> ResamplerAudio::convert(uint8_t **inData, int inSamplesCount, int outChannels, int outFormat)
{
std::vector<uint8_t> result;
uint8_t *dstData = NULL;
const int dstNbSamples = swr_get_out_samples(swrContext, inSamplesCount);
av_samples_alloc(&dstData, NULL, outChannels, dstNbSamples, AVSampleFormat(outFormat), 1);
int resampledSize = swr_convert(swrContext, &dstData, dstNbSamples, (const uint8_t **)inData, inSamplesCount);
int dstBufSize = av_samples_get_buffer_size(NULL, outChannels, resampledSize, AVSampleFormat(outFormat), 1);
if (dstBufSize <= 0) return result;
std::copy(&dstData[0], &dstData[dstBufSize], std::back_inserter(result));
return result;
}
И я вызываю функцию ResamplerAudio::convert
перед кодированием со следующими аргументами:
// data - an array of raw pcm audio
// dataLength - the length of data array
// getSamplesCount() - function that calculates samples count
// frameEncode - AVFrame that using for encode audio
std::vector<uint8_t> resampledData = resamplerEncoder->convert(&data, getSamplesCount(dataLength, frameEncode->channels, AV_SAMPLE_FMT_S16), frameEncode->channels, frameEncode->format);
getSamplesCount()
функция выглядит так:
getSamplesCount(int bytesCount, int channels, AVSampleFormat format)
{
return bytesCount / av_get_bytes_per_sample(format) / channels;
}
После этого я заполните мой frameEncode
сэмплами с передискретизацией:
memcpy(&frame->data[0][0], &resampledData[0], sizeof(uint8_t) * resampledDataLength);
И передайте frameEncode
в кодировку, подобную этой encodeFrame(resampledDataLength)
:
void encodeFrame(int dataLength)
{
/* send the frame for encoding */
int ret = avcodec_send_frame(contextEncoder, frameEncode);
if (ret < 0)
{
LOGE(TAG, "[encodeFrame] avcodec_send_frame error: %s", av_err2str(ret));
return;
}
/* read all the available output packets (in general there may be any number of them */
while (ret >= 0)
{
ret = avcodec_receive_packet(contextEncoder, packetEncode);
if (ret < 0 && ret != AVERROR(EAGAIN)) LOGE(TAG, "[encodeFrame] error in avcodec_receive_packet: %s", av_err2str(ret));
if (ret < 0) break;
// encodedData - std::vector<uint8_t> that stores encoded data
std::copy(&packetEncode->data[0], &packetEncode->data[dataLength], std::back_inserter(encodedData));
av_packet_unref(packetEncode);
}
}
Затем я декодирую свои закодированные сэмплы и делаю ресэмплинг до вернуть их в формате исходного образца и с частотой дискретизации, поэтому я вызываю функцию ResamplerAudio::convert
для resamplerDecoder
со следующими аргументами:
// frameDecode - AVFrame that holds decoded audio
std::vector<uint8_t> resampledData = resamplerDecoder->convert(frameDecode->data, frameDecode->nb_samples, frameDecode->channels, AV_SAMPLE_FMT_S16);
И звук результата прерывистый, и я также заметил, что размер декодированного массива равен больше, чем размер исходного массива с необработанным звуком pcm.
Пожалуйста, какие-нибудь идеи, что я делаю не так? *, Я сделал пересэмплинг сырого звука PCM без каких-либо процедур кодирования и декодирования. Сначала я попытался преобразовать частоту дискретизации входного звука с 8000 Гц на 48000 Гц, затем я взял повторно дискретизированные образцы из шага выше и преобразовал его частоту дискретизации с 48000 Гц на 8000 Гц, и в результате звук был идеальным и чистым, также я сделал то же самое. шаги, но я преобразовал не частоту дискретизации, а формат выборки из AV_SAMPLE_FMT_S16 в AV_SAMPLE_FMT_FLTP и наоборот, и снова звук результата идеальный и чистый, также я получил тот же результат, когда покрыл как частоту дискретизации, так и формат выборки. Итак, я предполагаю, что проблема искаженного и прерывистого звука заключается в моей процедуре кодирования или декодирования, я думаю, наиболее вероятно, в процедуре декодирования, потому что после декодирования я ВСЕГДА получаю AVFrame с 960 nb_samples, несмотря на размер входного звука .
Моя процедура декодирования выглядит так:
std::vector<uint8_t> decode(uint8_t *data, unsigned int dataLength)
{
decodedData.clear();
int dataSize = dataLength;
while (dataSize > 0)
{
if (!frameDecode)
{
frameDecode = av_frame_alloc();
if (!frameDecode)
{
LOGE(TAG, "[decode] Couldn't allocate the frame");
return EMPTY_DATA;
}
}
ret = av_parser_parse2(parser, contextDecoder, &packetDecode->data, &packetDecode->size, &data[0], dataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
LOGE(TAG, "[decode] av_parser_parse2 error: %s", av_err2str(ret));
return EMPTY_DATA;
}
data += ret;
dataSize -= ret;
doDecode();
}
return decodedData;
}
void doDecode()
{
if (packetDecode->size) {
/* send the packet with the compressed data to the decoder */
int ret = avcodec_send_packet(contextDecoder, packetDecode);
if (ret < 0) LOGE(TAG, "[decode] avcodec_send_packet error: %s", av_err2str(ret));
/* read all the output frames (in general there may be any number of them */
while (ret >= 0)
{
ret = avcodec_receive_frame(contextDecoder, frameDecode);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) LOGE(TAG, "[decode] avcodec_receive_frame error: %s", av_err2str(ret));
if (ret < 0) break;
std::vector<uint8_t> resampledData = resamplerDecoder->convert(frameDecode->data, frameDecode->nb_samples, frameDecode->channels, AV_SAMPLE_FMT_S16);
if (!resampledData.size()) continue;
std::copy(&resampledData.data()[0], &resampledData.data()[resampledData.size()], std::back_inserter(decodedData));
}
}
}
UPD 30.05.2020
Я решил отказаться от использования FFmpeg в моем проекте и использовать libopus 1.3 .1 , поэтому я сделал вокруг него обертку , и он отлично работает.