3

我正在编写一个代码,它将合并多个音频(具有不同格式)并创建一个音频。当我将编码器sample_ratesample_fmt设置为与输入视频相同时,合并音频没有问题。但是很明显,所有输入音频格式都与输出格式不同,所以我必须进行格式转换。我尝试为此目的使用“avresample”,但是当输入和输出的sample_ratesample_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,但我不知道如何解决这个问题。任何帮助将不胜感激。

4

0 回答 0