LibAV将流音频文件转换为PCM

时间:2018-10-31 02:43:56

标签: libavcodec libav libavformat

我正在尝试使用LibAV库将流音频文件(通过AVIOContext)转换为签名的16位PCM。但是,我很难阅读自动生成的文档和示例以执行我要尝试执行的操作。

以下是我尝试对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)。当我在不确定的部分中发表评论时,我能得到的最多的是第一个提供完整的null帧的帧,然后在随后的每个帧中完全废弃数据。

所以,我认为鉴于结果,我将编解码器初始化错误。一切都在混乱的注释行中签出。采样率,格式和通道均从参数中检出。运行av_dump_format(ctx, 0, "stream", 0)甚至可以提供正确的轨道调试信息,包括轨道本身的长度。

1 个答案:

答案 0 :(得分:0)

When calling swr_alloc_set_opts, the input options and output options were reversed via arguments -- which does not make sense. Swapping these fixed the issue. Output now provides PCM.