我正在编写一个代码,它将合并多个音频(具有不同格式)并创建一个音频。当我将编码器sample_rate和sample_fmt设置为与输入视频相同时,合并音频没有问题。但是很明显,所有输入音频格式都与输出格式不同,所以我必须进行格式转换。我尝试为此目的使用“avresample”,但是当输入和输出的sample_rate和sample_fmt不同时,无法对输出帧进行编码。
它可以手动手动完成(通过样本丢弃、插值等),但由于 libav 提供了一个转换 api,我认为这可以(并且可能应该为了整洁)自动完成。
这是我设置编码器和重新采样上下文参数的方式:
AVCodecContext* avAudioEncoder = outputAudioStream->codec;
AVCodec * audioEncoder = avcodec_find_encoder(AV_CODEC_ID_MP3);
avcodec_get_context_defaults3(avAudioEncoder, audioEncoder);
avAudioEncoder->sample_fmt = AV_SAMPLE_FMT_S16P;
avAudioEncoder->sample_rate = 48000;
avAudioEncoder->channels = 2;
avAudioEncoder->time_base.num = 1;
avAudioEncoder->time_base.den = 48000;
avAudioEncoder->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
if (outputAVFormat->oformat->flags & AVFMT_GLOBALHEADER)
{
avAudioEncoder->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
avcodec_open2(avAudioEncoder, audioEncoder, nullptr);
std::shared_ptr<AVAudioResampleContext> avAudioResampleContext(avresample_alloc_context(), [](AVAudioResampleContext * avARC){avresample_close(avARC), avresample_free(&avARC); });
av_opt_set_int(avAudioResampleContext.get(), "in_channel_layout", 2, 0);
av_opt_set_int(avAudioResampleContext.get(), "in_sample_rate", 44100, 0);
av_opt_set_int(avAudioResampleContext.get(), "in_sample_fmt", AV_SAMPLE_FMT_S16P, 0);
av_opt_set_int(avAudioResampleContext.get(), "out_channel_layout", avAudioEncoder->channels, 0);
av_opt_set_int(avAudioResampleContext.get(), "out_sample_rate", avAudioEncoder->sample_rate, 0);
av_opt_set_int(avAudioResampleContext.get(), "out_sample_fmt", avAudioEncoder->sample_fmt, 0);
这是我读取和编码帧的方式
...
int result = avcodec_decode_audio4(avAudioDecoder.get(), audioFrame.get(), &isFrameAvailable, &decodingPacket);
...
if (isFrameAvailable)
{
decodingPacket.size -= result;
decodingPacket.data += result;
encodeAudioFrame->format = outputAudioStream->codec->sample_fmt;
encodeAudioFrame->channel_layout = outputAudioStream->codec->channel_layout;
auto available = avresample_available(avAudioResampleContext.get());
auto delay = avresample_get_delay(avAudioResampleContext.get());
encodeAudioFrame->nb_samples = available + av_rescale_rnd( delay + audioFrame->nb_samples, avAudioEncoder->sample_rate, audioStream->codec->sample_rate, AV_ROUND_ZERO);
int linesize;
av_samples_alloc(encodeAudioFrame->data, &linesize, avAudioEncoder->channels, encodeAudioFrame->nb_samples, avAudioEncoder->sample_fmt, 1);
encodeAudioFrame->linesize[0] = linesize;
avresample_convert(avAudioResampleContext.get(), nullptr, encodeAudioFrame->linesize[0], encodeAudioFrame->nb_samples, &audioFrame->data[0], audioFrame->linesize[0], audioFrame->nb_samples*outputAudioStream->codec->channels);
std::shared_ptr<AVPacket> outPacket(new AVPacket, [](AVPacket* p){ av_free_packet(p); delete p; });
av_init_packet(outPacket.get());
outPacket->data = nullptr;
outPacket->size = 0;
while (avresample_available(avAudioResampleContext.get()) >= encodeAudioFrame->nb_samples)
{
avresample_read(avAudioResampleContext.get(), &encodeAudioFrame->data[0], encodeAudioFrame->nb_samples*outputAudioStream->codec->channels);
encodeAudioFrame->pts = av_rescale_q(++encodedAudioPts, outputAudioStream->codec->time_base, outputAudioStream->time_base);
encodeAudioFrame->pts *= avAudioEncoder->frame_size;
...
auto ret = avcodec_encode_audio2(avAudioEncoder, outPacketPtr, encodeAudioFramePtr, &got_output);
...
}
似乎我无法正确使用 avresample,但我不知道如何解决这个问题。任何帮助将不胜感激。