惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
H
Help Net Security
F
Fortinet All Blogs
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 司徒正美
量子位
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recorded Future
Recorded Future
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
AI
AI
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
U
Unit 42
V2EX - 技术
V2EX - 技术
MongoDB | Blog
MongoDB | Blog
Schneier on Security
Schneier on Security
博客园 - Franky
H
Heimdal Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
W
WeLiveSecurity
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
B
Blog RSS Feed
N
News | PayPal Newsroom
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
Hacker News - Newest:
Hacker News - Newest: "LLM"
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
雷峰网
雷峰网

博客园 - yeren2046

RedCoins,一个免费的类似bluecoins的个人财务管理软件 记录一个栈溢出导致的崩溃问题 一个免费的图片数据标注工具 常用zip命令 git版本导致的"Permission denied (publickey). fatal: Could not read from remote repository." 只显示全部特定进程名的top信息的shell脚本 linux 上用 core 文件定位线上问题 ffmpeg 时基转换 昇腾卡通道号范围 英伟达硬解码错误汇总 DVPP问题汇总 TensorRT生成INT8校准文件 结构体指定初始化 ffmpeg命令行基于英伟达显卡编解码的转码 C++11 获取当前时间戳 基于CUDA查询显卡型号和显存大小 nvjpeg 简单使用 AV_PIX_FMT_CUDA 数据转 RGB C++ do{ } while(0)
ffmpeg视频截取
yeren2046 · 2024-09-29 · via 博客园 - yeren2046
#include <string>
#include <stdio.h>

extern "C"
{
    #include "libavformat/avformat.h"
    #include "libavcodec/bsf.h"
};

//释放所有上下文信息
void release_context(AVFormatContext **in, AVFormatContext *out, AVIOContext *ctx, AVBSFContext **bsf_ctx_)
{
    //关闭输入
    avformat_close_input(in);
    if (!((out->oformat->flags) & AVFMT_NOFILE))
    {
        //关闭输出
        if (avio_close(ctx) < 0)
        {
            printf("failed to close output file\n");
            return;
        }
    }
    //释放输出AVFormatContext
    avformat_free_context(out);
    //释放AVBSFContext
    av_bsf_free(bsf_ctx_);
}

int main1(int argc, char *argv[])
{
    const char *in_filename = "/data/cmhu/test_data/20041609451310232511_20211128090026~1.mp4";
    AVFormatContext *in_fmt_ctx = NULL;
    //打开输入流,创建并初始化输入AVFormatContext
    if (avformat_open_input(&in_fmt_ctx, in_filename, NULL, NULL) < 0)
    {
        printf("failed to open input file\n");
        release_context(&in_fmt_ctx, NULL, NULL, NULL);
        return -1;
    }
    //格式化输出输入流信息
    av_dump_format(in_fmt_ctx, 0, in_filename, 0);
    //寻找流的信息以及编解码信息
    if (avformat_find_stream_info(in_fmt_ctx, NULL) < 0)
    {
        printf("failed to find media stream info\n");
        release_context(&in_fmt_ctx, NULL, NULL, NULL);
        return -1;
    }
    AVFormatContext *out_fmt_ctx = NULL;
    const char *out_filename = "/data/cmhu/jks_aiplatform_projectx/bin/res/test_trans.mp4";
    //创建并初始化输出AVFormatContext
    //不指定AVOutputFormat,ffmpeg会根据格式名或者文件名猜出上下文信息并初始化输出AVFormatContext
    if (avformat_alloc_output_context2(&out_fmt_ctx, NULL, NULL, out_filename) < 0)
    {
        printf("failed to create output format context\n");
        release_context(&in_fmt_ctx, out_fmt_ctx, NULL, NULL);
        return -1;
    }

    int video_index = -1;
    int audio_index = -1;
    AVStream *in_video_stream = nullptr;
    AVStream *in_audio_stream = nullptr;
    //遍历所有输入流
    for (int i = 0; i < in_fmt_ctx->nb_streams; i++)
    {
        AVStream *in_stream = in_fmt_ctx->streams[i];
         enum AVMediaType avMediaType = in_stream->codecpar->codec_type;
        if (avMediaType == AVMEDIA_TYPE_AUDIO)  {
            audio_index = i;
            in_audio_stream = in_stream;
        } else if (avMediaType == AVMEDIA_TYPE_VIDEO) {
            video_index = i;
            in_video_stream = in_stream;
        }
        //创建输出流
        AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
        if (!out_stream)
        {
            printf("failed to create new stream\n");
            release_context(&in_fmt_ctx, out_fmt_ctx, NULL, NULL);
            return -1;
        }
        //拷贝编解码器的参数设置
        if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0)
        {
            printf("failed to copy codec parameters from src to dst\n");
            release_context(&in_fmt_ctx, out_fmt_ctx, NULL, NULL);
            return -1;
        }
        //不同封装格式码流格式不同,所以要将codec_tag设为0
        //这样ffmpeg会自动选择和封装格式匹配的码流格式
        out_stream->codecpar->codec_tag = 0;
    }
    //格式化输出输出流信息
    av_dump_format(out_fmt_ctx, 0, out_filename, 1);
    //判断该上下文是否依赖于输入输出,1:不依赖,0:依赖
    if (!((out_fmt_ctx->oformat->flags) & AVFMT_NOFILE))
    {
        //打开输出文件
        if (avio_open2(&out_fmt_ctx->pb, out_filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
        {
            printf("failed to open output file\n");
            release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, NULL);
            return -1;
        }
    }
    //写入文件头
    if (avformat_write_header(out_fmt_ctx, NULL) < 0)
    {
        printf("failed to write header\n");
        release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, NULL);
        return -1;
    }

    AVStream *out_video_stream = out_fmt_ctx->streams[video_index];


    AVBSFContext *bsf_ctx = NULL;
    //获取比特流过滤器

    AVCodecParameters *codecpar = in_video_stream->codecpar;
    const AVBitStreamFilter * filter = nullptr;
    if(codecpar->codec_id == AV_CODEC_ID_H264){
        filter = av_bsf_get_by_name("h264_mp4toannexb");
    }else if(codecpar->codec_id == AV_CODEC_ID_HEVC){
        filter = av_bsf_get_by_name("hevc_mp4toannexb");
    }else {
        printf("codec_id is not supported!");
        release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, NULL);
        return -1;
    }

    //创建AVBSFContext
    if (av_bsf_alloc(filter, &bsf_ctx) < 0)
    {
        printf("failed to alloc AVBSFContext\n");
        release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
        return -1;
    }
    //拷贝编解码器参数
    if (avcodec_parameters_copy(bsf_ctx->par_in, in_video_stream->codecpar) < 0)
    {
        printf("failed to copy codec parameters from src to dst\n");
        release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
        return -1;
    }
    //初始化AVBSFContext
    if (av_bsf_init(bsf_ctx) < 0)
    {
        printf("failed to init AVBSFContext\n");
        release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
        return -1;
    }

    AVRational ctx_time_base = AVRational{ 1, 25 };
    
    //写入流信息
    int counting = 0;
    int video_frame_index = 0;
    int audio_frame_index = 0;
    AVPacket* pkt ;
    pkt = av_packet_alloc();
    av_init_packet( pkt );
    while (true)
    {
        //读取一帧视频或者几帧音频
        int ret = av_read_frame(in_fmt_ctx, pkt);
        if (ret < 0)
        {
            if (ret == AVERROR_EOF)
            {
                printf("end of file\n");
                //取消对缓缓区的引用,将packet其他字段置为默认值
                av_packet_unref(pkt);
                break;
            }
            printf("failed to read frame\n");
            av_packet_unref(pkt);
            release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, NULL);
            return -1;
        }


       //限定为输出格式为avi且是视频流
       if (video_index == pkt->stream_index){

            if ((pkt->flags & AV_PKT_FLAG_KEY) != 0)
            {
                counting++;
            }

            if (counting < 1)
            {
                continue;
            }

            if (counting > 2)
            {
                break;
            }

            video_frame_index++;

            //将输入时间基表示的时间戳转换为输出时间基表示
            pkt->pts = av_rescale_q_rnd(video_frame_index, ctx_time_base, out_video_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            pkt->dts = av_rescale_q_rnd(video_frame_index, ctx_time_base, out_video_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            pkt->duration = av_rescale_q(1, ctx_time_base, out_video_stream->time_base);
            pkt->stream_index = out_video_stream->index;

            //将数据送入过滤器
            ret = av_bsf_send_packet(bsf_ctx, pkt);
            if (ret < 0)
            {
                //单个packet不足以完成过滤,需要继续送入数据
                if (ret == AVERROR(EAGAIN))
                {
                    av_packet_unref(pkt);
                    continue;
                }
                printf("failed to send pakcet to bit stream filter\n");
                av_packet_unref(pkt);
                release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
                return -1;
            }
            //接收过滤后的数据
            //由于单个输入packet可能产生多个输出packet,因此需要使用循环
            int ans = 0;
            do
            {
                //获取过滤后的数据
                ans = av_bsf_receive_packet(bsf_ctx, pkt);
                if (ans < 0)
                {
                    if (ans == AVERROR_EOF)
                    {
                        av_packet_unref(pkt);
                        break;
                    }
                    if (ans != AVERROR(EAGAIN))
                    {
                        printf("failed to reveive filtered packet\n");
                        av_packet_unref(pkt);
                        release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
                        return -1;
                    }
                }
                //交错写入
                if (av_interleaved_write_frame(out_fmt_ctx, pkt) < 0)
                {
                    printf("failed to write frame\n");
                    av_packet_unref(pkt);
                    release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
                    return -1;
                }
            } while (ans == AVERROR(EAGAIN));
            //**********************过滤结束****************************
       } else if (audio_index == pkt->stream_index){
            //交错写入
            if (av_interleaved_write_frame(out_fmt_ctx, pkt) < 0)
            {
                printf("failed to write frame\n");
                av_packet_unref(pkt);
                release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
                return -1;
            }
       }

    //    av_packet_unref(pkt);
    }
    //写入文件尾
    if (av_write_trailer(out_fmt_ctx) != 0)
    {
        printf("failed to write tail\n");
        release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);
        return -1;
    }
    release_context(&in_fmt_ctx, out_fmt_ctx, out_fmt_ctx->pb, &bsf_ctx);

    return 0;
}

(2)一个与本代码关联不大,但是比较通用但是容易出错的点:在拷贝AVPacket时,如果想要做的事深拷贝,不要用ffmpeg自带的一些拷贝操作(除非你彻底的了解你用的接口做的是深拷贝),本人在此处被坑过很久,ffmpeg自带的一些复制、拷贝、克隆都是浅拷贝,在一处释放后会把其他处的也给释放,从而导致程序出现严重问题。我的做法是直接用malloc和memcpy根据pkt->data、pkt->size进行拷贝即可实现深拷贝数据