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

推荐订阅源

美团技术团队
D
DataBreaches.Net
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
Docker
N
Netflix TechBlog - Medium
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
Check Point Blog
腾讯CDC
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
IT之家
IT之家
月光博客
月光博客
U
Unit 42
K
Kaspersky official blog
T
Threatpost
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
GbyAI
GbyAI
P
Proofpoint News Feed
Last Week in AI
Last Week in AI
云风的 BLOG
云风的 BLOG
酷 壳 – CoolShell
酷 壳 – CoolShell
I
InfoQ
Engineering at Meta
Engineering at Meta
Recorded Future
Recorded Future
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Security @ Cisco Blogs
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
Security Archives - TechRepublic
Security Archives - TechRepublic
Webroot Blog
Webroot Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
S
Schneier on Security
S
Secure Thoughts
The Register - Security
The Register - Security
B
Blog RSS Feed
The Last Watchdog
The Last Watchdog
P
Palo Alto Networks Blog
爱范儿
爱范儿
B
Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
N
News and Events Feed by Topic
阮一峰的网络日志
阮一峰的网络日志
L
LINUX DO - 热门话题
C
Cisco Blogs
Spread Privacy
Spread Privacy
F
Full Disclosure
博客园 - 聂微东
T
The Blog of Author Tim Ferriss

博客园 - 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进行拷贝即可实现深拷贝数据