• ffmpeg sdk 视频合成


    还是给自己 学习啊 大部分是 命令 或是android的~~  这边得用原生的 我也是刚学 到底什么区别也不太知道~~ 还请大神指点一二

    • 需求场景1(视频中提取照片):
      各大网站在线播放视频时,鼠标滑到某一时刻能够提前显示那一时刻的画面。短的视频编辑APP中,为了更好的对视频进行编辑,会提取出视频各个时刻的画面进行预览,那么这些是如何实现的呢?本文将给出基于ffmpeg的实现代码以及实现思路。
    • 需求场景2(照片合成视频):
      摄影师经常不间断的拍摄一组连续的画面用于合成延时视频,剪印APP中也有时光相册这样通过照片生成视频的功能(不过剪印APP照片合成的视频采用了插值算法生成了额外的过度动画照片以及特效,功能更加复杂,但是不管怎样,最终还是会由照片合成视频)。本文基于ffmpeg实现简单的照片合成视频思路以及详细代码

    实现思路 

    • 视频中提取照片:

    1、fmpeg对将像素数据写入到JPG图片中也封装到了avformat_xxx系列接口中,它的使用流程和封装视频数据到mp4文件一模一样,只不过一个JPG文件中只包含了一帧视频数据而已;
    2、ffmpeg对JPG文件的封装支持模式匹配,即如果想要将多张图片写入到多张jpg中只需要文件名包含百分号即可,例如 name%3d.jpg,那么在每一次调用av_write_frame()函数写入视频数据到jpg图片时都会生成一张jpg图片。这样做的好处是不需要每一张要写入的jpg文件都创建一个AVFormatContext与之对应。其它流程和写入一张jpg一样。

    流程为:
    1、先从MP4中提取指定时刻AVPacket解码成AVFrame
    2、然后将步骤1得到的AVFrame进行从素格式YUV420P到JPG需要的YUVJ420P像素格式的转换
    3、再重新编码,然后再封装到jpg中

    • 照片合成视频:
      因为JPG的编码方式为AV_CODEC_ID_MJPEG,MP4如果采用h264编码,那么两者的编码方式是不一致的,所以就需要先解码再编码,具体流程为:
      1、先将JPG解码成AVFrame
      2、将JPG解码后的源像素格式YUVJ420P转换成x264编码需要的YUV420P像素格式
      3、再重新编码,然后再封装到mp4中

    流程图

    #视频中提取照片流程图

    #照片合成视频: 

    实现

    tips:要对jpg进行封装和解封装,编译ffmpeg时要添加如下封装器和解封装
    封装器(及对应的编码器)
    --enable-muxer=image2
    --enable-encoder=mjpeg
    接封装器(及对应的解码器)
    --enable-demuxer=image2
    --enable-decoder=mjpeg

     音视频精准截取

    还要IOS的框架,不过本人用不到就不贴了...

    有时会碰到这样的需求场景,对一个视频中的某一段感兴趣,想要精确的截取这一段视频以及对应的音频。例如,有一个25fps的MP4的文件,时长20秒,我想要截取从5秒开始到15秒结束的视频以及对应的音频,这里有两点需要说明:
    1、对于视频:开始时间5秒,结束时间15秒。只能做到尽量接近,因为源文件25fps,即每一帧的显示间隔为0.04秒,可能5秒附近的视频帧刚好在5.012秒,最大误差一帧时间差就是1/25==0.04秒,所以4.96-5.04秒范围内都可以。
    2、对于音频:比如采样率44100,每一个AVPacket包含1024个采样,那么每一帧的显示时间间隔为1/43≈0.025秒,音频再5秒处的最大误差为0.025秒,所以4.975-5.025秒范围内都可以。

    这里以MP4文件为例,假设视频的编码方式为H264,音频为aac,其它格式类似。

    实现思路

    1、在ffmpeg解封装之后得到的AVPacket代表着压缩的音/视频数据,该结构体内有一个字段pts,表示了该音/视频的时间,即前面说的5秒就是要参考这个时间。
    2、对于音频来说,只需要依次取出5秒(或者最接近5秒)到15秒处的AVPacket,然后调用ffmpeg封装接口依次写入新的MP4文件即可
    3、对于视频来说,写入MP4的第一个AVPacket一定要是I帧(对于H264等有IPB帧改变的编码方式来说,其它编码方式则跟音频一样处理),所以视频的处理分两种情况:

    • 当5秒(或者最接近5秒)处的AVPacket刚好为I帧,那么只需依次取出直到15秒处的AVpacket,然后依次写入MP4文件即可;
    • 当5秒处的AVPacket非I帧,那么就需要往前找出最近一个I帧的AVPacket,然后用这个AVPacket往后依次解码,直到解码出5秒处的AVPacket,然后将解码得到AVFrame重新进行编码为AVPacket(那么5秒处的AVPacket就一定是I帧了)再写入MP4文件,5秒后的视频也按照先解码再编码为AVPacket再写入MP4文件的思路进行

    流程图 

    关键函数

    • int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
      int flags);

    调用av_read_frame()函数时,其内部会有一个文件指针(默认情况下指向文件中的首个AVPacket),所以默认时读取的第一个AVPacket就是文件中的首个AVPacket,调用结束后指针再指向下一个AVPacket。此函数的作用就相当于将指针指向指定的key_frame的AVPacket,那么首次调用av_read_frame()函数时将获取的是满足要求的AVPacket,而非文件的第一个AVPacket。

    1、stream_index:代表音视频流的索引,如果为-1,那么将对文件的默认流索引进行操作
    2、timestamp:移动到指定的时间戳(单位为AVStream.time_base )。如果stream_index为-1,单位为(AV_TIME_BASE)
    3、flag:移动参考的方式,取值如下:
    #define AVSEEK_FLAG_BACKWARD 1 ///< seek backward
    #define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes
    #define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes
    #define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number

    AVSEEK_FLAG_BACKWARD
    基于前面指定的时间戳参数查找,如果timestamp处的AVPacket的key_frame非1,那么就往前找,直到找到最近一个key_frame为1的AVPacket

    AVSEEK_FLAG_BYTE
    基于位置进行查找

    AVSEEK_FLAG_ANY
    基于前面指定的时间戳参数进行查找,不管AVPacket是否key_frame为1都返回

    AVSEEK_FLAG_FRAME
    基于帧编号进行查找

    遇到问题 

    1、音频正常播放,视频只是播放了很短的几帧画面
    分析原因:由于pkt的时间戳没有减去起始时间导致了音视频的时间戳错乱
    解决方案:音频时间戳减去起始时间即可

    最后还有一些ffmpeg sdk推流的代码 有需要交流的欢迎加我讨论哦~~~

    whaosoft aiot http://143ai.com  

  • 相关阅读:
    基于STM32设计的车库监控报警系统
    PyQt5 设置窗口背景
    2022牛客多校训练第二场 J题 Link with Arithmetic Progression
    道可云元宇宙每日资讯|2023焦作市文旅元宇宙产业发展座谈会举行
    使用Bind提供的域名解析服务
    c++基础第三章:数值类型
    Mysql导入导出大型数据库方法
    Spark SQL操作数据源
    图扑 Web SCADA 零代码组态水泥生产工艺流程 HMI
    Seata-TCC模式
  • 原文地址:https://blog.csdn.net/qq_29788741/article/details/127656338