LibAV конвертирует потоковый аудиофайл в PCM - PullRequest
0 голосов
/ 31 октября 2018

Я пытаюсь преобразовать потоковый аудиофайл (через AVIOContext) в подписанный 16-битный PCM, используя библиотеку LibAV. Однако мне трудно читать автоматически сгенерированную документацию и примеры, чтобы выполнить то, что я пытаюсь сделать.

Ниже представлена ​​моя попытка декодировать любой потоковый аудиофайл, предоставляемый функцией extern read_packet, и передавать полученные данные PCM в функцию extern process_data.

#define BUFFER_SIZE 4096

extern int read_packet(void *, uint8_t *, int);

extern void process_data(uint8_t *, int);

int perform_decoding() {
    AVFormatContext *ctx = avformat_alloc_context();
    uint8_t *buffer = av_malloc(BUFFER_SIZE);
    AVIOContext *aioctx = avio_alloc_context(
            buffer,       // Buffer
            BUFFER_SIZE,  // Buffer size
            0,            // write_flag
            NULL,         // opaque
            read_packet,  // read_packet
            NULL,         // write_packet
            NULL          // seek
    );
    ctx->pb = aioctx;
    avformat_open_input(&ctx, "stream", NULL, NULL);
    if (avformat_find_stream_info(ctx, NULL) < 0) { return -1; }

    int audio_stream_index = -1;
    AVStream *stream = NULL;
    for (int i = 0; i < ctx->nb_streams; i++) {
        stream = ctx->streams[i];
        if (stream->codec.codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_stream_index = i;
            break;
        }
    }
    if (audio_stream_index == -1) { return -2; }

    AVCodecContext *cctx = stream->codec;
    AVCodecParameters *params = stream->codecpar;

    /// At this point we have
    ///     (1) The codec context
    ///     (2) The codec parameters for the incoming song (which can change per incoming file stream)
    /// At this point I do not have the AVCodec* which I need to decode frames
    /// And `cctx->codec` == NULL at this point (this would have been my AVCodec*)


    /// ---------- Confusion starts below this line ----------


    /// To the best of my knowledge, this is the best way to get one
    /// I am not sure if one already exists that I can use.
    AVCodec *codec = avcodec_find_decoder(cctx->codec_id);
    if (codec == NULL) { return -3; } // Codec not found

    /// These three commented out lines are lines I have always found when `avcodec_find_decoder` is performed
    // cctx = avcodec_alloc_context3(codec);           // Do I need?
    // avcodec_alloc_context3(cctx, codec, NULL);      // Do I need?
    // avcodec_open2(cctx, codec, NULL) { return -3; } // Do I need?


    /// ---------- Decoder section ----------


    SwrContext *swr = swr_alloc_set_opts(
            NULL,                   // Allocating a new context
            params->channel_layout, // Input channel layout
            params->format,         // Input format
            params->sample_rate,    // Input sample rate
            STEREO,                 // Output channel layout
            AV_SAMPLE_FMT_S16,      // Output format
            48000,                  // Output sample rate
            0,                      // Log offset
            NULL                    // Log ctx
    );
    if (swr == NULL) { return -4; }

    AVPacket *packet = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();

    while (av_read_frame(ctx, packet) >= 0) { // Fetch a packet
        avcodec_send_packet(cctx, packet); // Toss the packet into the codec context
        avcodec_receive_frame(cctx, frame); // Pull out a frame

        /// Send the data to an external function that exects the decoded track in signed 16bit PCM format (bytes)
        /// where the first argument is the pointer to the buffer and the second argument is the size
        process_data(frame->data[0], frame->linesize[0]);

        /// Do I need to reallocate these bellow? Or can I just reuse the packet & frame objects?
        // packet = av_packet_alloc();
        // frame = av_frame_alloc();
    }

    return 0;
}

Как указано, приложение ниже предоставит кадры без данных (размер 0). Когда я пытаюсь комментировать некоторые из разделов, в которых я не уверен, самое большее, что я могу получить, - это первый кадр, содержащий полный кадр нулей, за которым следуют полностью мусорные данные в каждом последующем кадре.

Итак, я считаю, что я неправильно инициализировал кодек, учитывая результаты. Все до комментируемой линии путаницы проверяется. Частота дискретизации, формат и каналы все оформить из параметров. Запуск av_dump_format(ctx, 0, "stream", 0) даже обеспечивает правильную информацию об отладке трека, включая длину самого трека.

1 Ответ

0 голосов
/ 15 января 2019

При вызове swr_alloc_set_opts параметры ввода и параметры вывода менялись местами через аргументы - что не имеет смысла. Обмен этим исправил проблему. Вывод теперь обеспечивает PCM.

...