ffmpeg 使用 gdb 调试相关技巧

本文说明了,在ffmpeg二次开发或调用库的过程,如何借助于ffmpeg源码进行调试。

:ffmpeg版本是4.0。

1. 编写代码

编写将pcm数据转换为mp2的代码

pcm_to_mp2.c

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
#include <stdio.h>
#include <stdbool.h>

SwrContext *g_swr_ctx = NULL;

int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
{
    int ret = 0;
    int got_frame = 0;
    AVPacket enc_pkt;
    if(!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
                CODEC_CAP_DELAY))
    {
        return 0;
    }

    while(1)
    {
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
        ret = avcodec_encode_audio2(fmt_ctx->streams[stream_index]->codec, &enc_pkt, NULL, &got_frame);
        if(ret < 0)
        {
            break;
        }
        if(!got_frame)
        {
            ret = 0;
            break;
        }

        ret = av_write_frame(fmt_ctx, &enc_pkt);
        if(ret < 0)
        {
            break;
        }
    }

    return ret;
}

void usage(void)
{
    printf("./aac input_file output_file.mp2
");
}

int main(int argc, char *argv[])
{

    if(argc != 3)
    {
        usage();
        return -1;
    }

    char *input_file = argv[1];
    char *output_file = argv[2];

    AVFormatContext *pFormatCtx = NULL;
    AVOutputFormat *ofmt = NULL;
    AVStream *audio_stream = NULL;
    AVCodecContext *pCodecCtx = NULL;
    AVCodec *pCodec = NULL;

    uint8_t *frame_buf = NULL;
    AVFrame *pFrame = NULL;
    AVPacket pkt;

    int got_frame = 0;
    int ret = 0;
    int size = 0;

    int i = 0;

    FILE *fp = fopen(input_file, "rb");

    av_register_all();
    avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, output_file);
    ofmt = pFormatCtx->oformat;

    if(avio_open(&pFormatCtx->pb, output_file, AVIO_FLAG_READ_WRITE) < 0)
    {
        printf("Error: call avio_open failed!
");
        return -1;
    }

    audio_stream = avformat_new_stream(pFormatCtx, 0);
    if(!audio_stream)
    {
        return -1;
    }


    //初始化编码器
    pCodecCtx = audio_stream->codec;
    pCodecCtx->codec_id = ofmt->audio_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
    pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
    pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
    pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
    pCodecCtx->sample_rate = 44100;
    pCodecCtx->bit_rate = 192000;
    pCodecCtx->frame_size = 1152;

    AVFrame *src_frame = NULL;
    AVFrame *dst_frame = NULL;

    src_frame = av_frame_alloc();
    src_frame->nb_samples = 1152;
    src_frame->sample_rate = 44100;
    src_frame->format= AV_SAMPLE_FMT_S16;
    src_frame->channel_layout = AV_CH_LAYOUT_STEREO;
    src_frame->channels = av_get_channel_layout_nb_channels(src_frame->channel_layout);

    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if(!pCodec)
    {
        printf("Error: call avcodec_find_encoder failed!
");
        return -1;
    }

    if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Error: call avcodec_open2 failed!
");
        return -1;
    }

    int sr_size = 0;
    sr_size = av_samples_get_buffer_size(NULL, src_frame->channels, src_frame->nb_samples, src_frame->format, 1);
    frame_buf = (uint8_t *)av_malloc(sr_size);
    if(!frame_buf)
    {
        printf("Error: call av_malloc failed, sr_size = %d
", sr_size);
        return -1;
    }

    if(av_sample_fmt_is_planar(src_frame->format))
    {
        avcodec_fill_audio_frame(src_frame, src_frame->channels, src_frame->format, (const uint8_t *)frame_buf, sr_size * src_frame->channels, 1 );
    }
    else
    {
        avcodec_fill_audio_frame(src_frame, src_frame->channels, src_frame->format, (const uint8_t *)frame_buf, sr_size, 0 );
    }

    //Write Header
    if(avformat_write_header(pFormatCtx,NULL) < 0)
    {
        printf("Error: call avformat_write_header..
");
        return -1;
    }

    AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
    av_init_packet(packet);
    
    av_dump_format(pFormatCtx, 0, output_file, 1);

    src_frame->pts = 0;
    while(fread(frame_buf, 1, sr_size, fp) > 0)
    {
        got_frame = 0;

        ret = avcodec_encode_audio2(pCodecCtx, packet, src_frame, &got_frame);
        if(ret < 0)
        {
            printf("Error: call avcodec_encode_audio2
");
            return -1;
        }
        i++;

        src_frame->pts = i * 100;

        if(1 == got_frame)
        {
            packet->stream_index = audio_stream->index;
            ret = av_write_frame(pFormatCtx, packet);
            if(ret < 0)
            {
                printf("Error: call av_write_frame..
");
                return -1;
            }
            av_free_packet(packet);
        }

    }

    //flush encoder
    ret = flush_encoder(pFormatCtx, 0);
    if(ret < 0)
    {
        printf("Error: call flush_encoder failed!
");
        return -1;
    }

    if(av_write_trailer(pFormatCtx) < 0)
    {
        printf("Error: call av_write_trailer..
");
        return -1;
    }

    if(audio_stream)
    {
        avcodec_close(audio_stream->codec);
        av_free(src_frame);
        av_free(frame_buf);
    }

    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);
    av_free(packet);

    fclose(fp);

    printf("Encode Audio End...
");

    return 0;
}

2. 编译ffmpeg源码

$ ./configure --enable-static --disable-shared --enable-debug --disable-optimizations --disable-asm --disable-stripping --enable-doc

$ make -j 4

3. 将代码放置在examples下

pcm_to_mp2.c拷贝到 doc/examples目录下,该目录下都是ffmpeg的例子程序。

4. 添加编译配置

修改doc/Makefile文件添加pcm_to_mp2的编译项目。

ffmpeg 使用 gdb 调试相关技巧

修改ffbuild/config.mak文件,
添加CONFIG_PCM_TO_MP2_EXAMPLE=yes宏。
ffmpeg 使用 gdb 调试相关技巧

5. 编译

ffmpeg源码根目录下,执行make examples
这样就在doc/examples目录下生成了pcm_to_mp2的可执行程序。

ffmpeg 使用 gdb 调试相关技巧

6. gdb调试
ffmpeg 使用 gdb 调试相关技巧