从flv文件中提取h264码流(使用av_bsf_send_packet和av_bsf_receive_packet)

本文介绍了如何使用最新版本的FFmpeg API替换旧版,从FLV文件中提取H264视频流,重点讲解了`avcodec_parameters_from_context`在新API中的作用,并提供了一个修改后的代码示例,适合音视频开发初学者参考。
摘要由CSDN通过智能技术生成

最近在学习音视频开发,需要开发一个从flv文件中提取h264码流的demo。

具体的原理,在雷神的文章中写的很清楚了:
https://blog.csdn.net/leixiaohua1020/article/details/39767055
https://blog.csdn.net/leixiaohua1020/article/details/39802819

在这里致敬一下雷神,给我们这些小白入门音视频铺平了道路!

但是雷神在Demo中使用的API是旧版本的API,比如bitstream_filter相关的:
av_bitstream_filter_init()
av_bitstream_filter_filter()
等,这些API即将被弃用,我们需要使用新版本的API

但是网上并没有找到相关的实现,根据上面链接的方法也并不能正确解析出h264码流。

在分析bitstream_filter.c源码后我们发现,在调用av_bsf_init()前需要调用avcodec_parameters_from_context(),增加解码必要的一些参数。

修改后的demo如下,大家可以用来参考。因为我也是刚入门,所以欢迎大家批评指正~:

#include <stdio.h>
#include <libavformat/avformat.h>
#define FFMPEG_NEW

int main() {
    AVFormatContext *ifmt_ctx = NULL;
    AVPacket pkt;
    int ret, i;
    int videoindex = -1, audioindex = -1;
    const char *in_filename = "demo.flv";
    const char *out_filename_v = "ffmpeg_demo.h264";
    const char *out_filename_a = "ffmpeg_demo.mp3";

    //av_register_all(); 已废弃

    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf("Could not open input file.");
        goto ERROR;
    }

    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        printf("Failed to retrieve input stream information");
        goto ERROR;
    }

    videoindex = -1;
    for (i=0; i<ifmt_ctx->nb_streams; i++) { //nb_streams:视音频流的个数
        if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            videoindex = i;
        else if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            audioindex = i;
    }

    printf("\nInput Video===========================\n");
	av_dump_format(ifmt_ctx, 0, in_filename, 0);  // 打印信息
	printf("\n======================================\n");

    FILE *fp_audio=fopen(out_filename_a,"wb+");  
	FILE *fp_video=fopen(out_filename_v,"wb+");

#ifdef FFMPEG_NEW
    AVBSFContext *bsf_ctx = NULL; 
    const AVBitStreamFilter *pfilter = av_bsf_get_by_name("h264_mp4toannexb");
    if (pfilter == NULL) {
        printf("Get bsf failed!\n");
        goto ERROR;
    }
#else 
    AVBitStreamFilterContext* h264bsfc =  av_bitstream_filter_init("h264_mp4toannexb");
#endif

    while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == videoindex) {
#ifdef FFMPEG_NEW
            if ((ret = av_bsf_alloc(pfilter, &bsf_ctx)) != 0) {
                printf("Alloc bsf failed!\n");
                goto ERROR;
            }
            ret = avcodec_parameters_from_context(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codec);
            if (ret < 0) {
                printf("Set Codec failed!\n");
                goto ERROR;
            }
            ret = av_bsf_init(bsf_ctx);
            if (ret < 0) {
                printf("Init bsf failed!\n");
                goto ERROR;
            }
            av_bsf_send_packet(bsf_ctx, &pkt);
            ret = av_bsf_receive_packet(bsf_ctx, &pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                break;
            else if (ret < 0) {
                printf("Receive Pkt failed!\n");
                goto ERROR;
            }
#else
            av_bitstream_filter_filter(h264bsfc, ifmt_ctx->streams[videoindex]->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif
            printf("Write Video Packet. size:%d\tpts:%lld\n",pkt.size,pkt.pts);
            fwrite(pkt.data, 1, pkt.size, fp_video);
        }
        else if (pkt.stream_index == audioindex) {
            printf("Write Audio Packet. size:%d\tpts:%lld\n",pkt.size,pkt.pts);
            fwrite(pkt.data, 1, pkt.size, fp_audio);
        }
        av_packet_unref(&pkt);
    }
#ifdef FFMPEG_NEW
    av_bsf_free(&bsf_ctx);
#else
    av_bitstream_filter_close(h264bsfc); 
#endif

    fclose(fp_video);
	fclose(fp_audio);
 
	avformat_close_input(&ifmt_ctx);
    return 0;

ERROR:
    if (ifmt_ctx)
        avformat_close_input(&ifmt_ctx);
    if (fp_audio)
        fclose(fp_audio);
    if (fp_video)
        fclose(fp_video);
    if (bsf_ctx)
        av_bsf_free(&bsf_ctx);
    return -1;
}
  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
av_bsf_receive_packet 函数是 FFmpeg 的一个函数,它属于 Bitstream Filter(比特流过滤器)模块。该函数的作用是从输入 AVBSFContext(比特流过滤器上下文)读取一个压缩数据包(AVPacket),然后经过过滤器处理后将输出的压缩数据包写入到输出 AVBSFContext 。 以下是 av_bsf_receive_packet 函数的源码实现: ```c int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt) { int ret; AVPacket *out_pkt = NULL; if (!pkt) return AVERROR(EINVAL); if (!(ctx->filter->input_stream_ids[0] == pkt->stream_index || ctx->filter->input_stream_ids[0] == AVSTREAM_ALL_STREAMS)) { av_log(ctx, AV_LOG_ERROR, "Packet with wrong stream index: %d (not in %d-%d)\n", pkt->stream_index, ctx->filter->input_stream_ids[0], ctx->filter->input_stream_ids[ctx->filter->nb_inputs - 1]); return AVERROR(EINVAL); } if (ctx->finished) return AVERROR_EOF; ret = av_packet_ref(pkt); if (ret < 0) return ret; ret = av_packet_make_refcounted(pkt); if (ret < 0) goto fail; ret = ff_bsf_get_packet_ref(ctx, pkt, &out_pkt); if (ret < 0) goto fail; if (out_pkt) { av_packet_move_ref(pkt, out_pkt); return 0; } return AVERROR(EAGAIN); fail: av_packet_unref(pkt); return ret; } ``` 在这段代码,我们可以看到以下几个关键步骤: 1. 检查输入的 AVPacket 是否为空,如果为空则返回错误。 2. 检查输入的 AVPacket 的流索引是否与设定的输入流索引匹配。 3. 检查过滤器是否已经处理完毕,如果已经处理完毕则返回 EOF 错误。 4. 通过 av_packet_ref 函数将输入的 AVPacket 引用计数加 1。 5. 通过 av_packet_make_refcounted 函数将输入的 AVPacket 转换为带有引用计数的 AVPacket。 6. 调用 ff_bsf_get_packet_ref 函数进行过滤器处理,得到输出的 AVPacket。 7. 如果输出的 AVPacket 存在,则通过 av_packet_move_ref 函数将其移动到输入的 AVPacket 。 8. 返回 0 表示处理成功,否则返回对应的错误码。 总体来说,av_bsf_receive_packet 函数的作用相当于是 FFmpeg 比特流过滤器的核心函数,它通过调用过滤器处理函数来实现对压缩数据包的过滤。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值