swr_convert float planar to S16 - PullRequest
       14

swr_convert float planar to S16

1 голос
/ 06 января 2020

Как конвертировать с помощью libav API AV_SAMPLE_FMT_FLTP в AV_SAMPLE_FMT_S16

Я пытаюсь выяснить, как повторно сэмплировать и кодировать PCM (захваченный с микрофона) с 44,1 кГц до AA C 48,0 кГц

Это мой инициализатор ресамплера:

void initialize_resampler(SwrContext*& resamplerCtx, AVCodecContext* encoder, AVFrame*& rawResampledAudioFrame, AVStream* audioFormatStream)
{
    int nb_samples = (encoder->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) ? encoder->sample_rate : encoder->frame_size;

    int encoderFrameSize = encoder->channels * av_get_bytes_per_sample(encoder->sample_fmt) * encoder->frame_size;
    rawResampledAudioFrame = allocate_audioframe(encoder->sample_fmt, encoder->channel_layout, encoder->sample_rate, nb_samples);

    // Copy the stream parameters to the muxer
    check(avcodec_parameters_from_context(audioFormatStream->codecpar, encoder));

    // Create resampler context
    resamplerCtx = swr_alloc();
    if (resamplerCtx == nullptr)
        throw std::runtime_error("Could not allocate resampler context");

    // Set options
    check(av_opt_set_int(resamplerCtx, "in_channel_count", 2, 0));
    check(av_opt_set_int(resamplerCtx, "in_sample_rate", 44100, 0));
    check(av_opt_set_sample_fmt(resamplerCtx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0));
    check(av_opt_set_int(resamplerCtx, "out_channel_count", encoder->channels, 0));
    check(av_opt_set_int(resamplerCtx, "out_sample_rate", encoder->sample_rate, 0));
    check(av_opt_set_sample_fmt(resamplerCtx, "out_sample_fmt", encoder->sample_fmt, 0));

    // initialize the resampling context
    check(swr_init(resamplerCtx));
}

И для повторной выборки у меня есть этот код:

    AVPacket pkt{};
    while (true)
    {
        AVPacket input_packet;
        av_init_packet(&input_packet);

        check(av_read_frame(inputContext, &input_packet));

        check(avcodec_send_packet(decoderContext, &input_packet));
        check(avcodec_receive_frame(decoderContext, decodedFrame));


        // WHAT DO HERE swr_convert(resamplerContext, )
        av_packet_unref(&input_packet);

        av_init_packet(&pkt);
        auto in_stream = inputContext->streams[pkt.stream_index];
        auto out_stream = outputContext->streams[pkt.stream_index];

        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;

        check(avcodec_send_frame(encoderContext, decodedFrame));
        check(avcodec_receive_packet(encoderContext, &pkt));

        check(av_interleaved_write_frame(outputContext, &pkt));
        av_packet_unref(&pkt);
    }

Читая документы, я не могу понять, что именно мне нужно перейти к функции. У меня есть этот код для кодирования PCM в MP2 (вывод AV_SAMPLE_FMT_S16)

const uint8_t* inPtr[] { const_cast<const uint8_t*>(&pcmData[0]), nullptr, nullptr,nullptr,nullptr,nullptr,nullptr,nullptr };
uint8_t* outPtr[] { &resampledAudioData[0], nullptr, nullptr,nullptr,nullptr,nullptr,nullptr,nullptr };

int resampledSamplesCount{ swr_convert(
    resamplerCtx,
    outPtr,
    maxResampledSamplesCount,
    inPtr,
    inputSampleCount) };

// Negativo indica erro.
check(resampledSamplesCount);

pcmData является необработанные данные из ввода AVPacket (PCM)

Что я получаю здесь: MP2 не плоский, поэтому он использует тот же outPtr [0], отличный от plannar, которому нужны два действительных указателя на одни и те же записываемые данные. Но что мне нужно перейти к inPtr, например?

Когда я пытаюсь использовать тот же код, ffmpeg пытается записать в outPtr [1] значение nullptr.

1 Ответ

1 голос
/ 08 января 2020

ЧТО ДЕЛАТЬ ЗДЕСЬ часть должна быть примерно такой:

int out_samples = swr_convert(swr,
                             &audio_buf,                   /* out */
                         (int)out_count,                   /* out */
             (const uint8_t**)decodedFrame->extended_data, /* in  */
                              decodedFrame->nb_samples);   /* in  */

Что касается out_count, то можно использовать что-то подобное (вы можете улучшить это):

double frame_nb = 1.0 * encoder->sample_rate / audio_st->codec->sample_rate * decodedFrame->nb_samples;
out_count = floor(frame_nb);

audio_buf - это заранее выделенный буфер вывода (48000 * 4 - хороший размер). Напоследок теперь вопрос в том, сколько данных записано в буфер. Это формула:

int data_size = out_samples * av_get_bytes_per_sample(encoder->sample_fmt) * decodedFrame->channels;

Надеюсь, это поможет.

...