Screen Record H.264

目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式。

在上一篇文章中,通过调用FFmpeg API完成了Mac平台下的录屏功能。在本篇中,对上次的录屏进行优化,将采集到的视频流编码为H.264格式,同时设定FPS和分辨率。

因为是对上次录屏功能的优化,因此处理思路仍然分为三部分:

  1. 打开输入设备(默认的屏幕设备)
  2. 初始化输出设备(mp4文件)
  3. 内容转码

和上次使用的API对比,本次主要增加了涉及到H.264参数设定和H.264 pts/dts 设定的API:

  1. avcodec_parameters_from_context
  2. av_rescale_q

初始化输入设备

仍然采用上篇中打开设备的方法:

  1. 通过av_find_input_format("avfoundation")获取AVInputFormat。
  2. 通过avformat_open_input 打开指定的屏幕设备。

然后FFmpeg会返回此设备中的数据流,而FFmpeg处理数据流一般都遵循:确定codec(编码 or 解码)->初始化codec上下文参数->打开codec,这三步。 针对输入设备也就是下面的顺序:

avcodec_find_decoder -> avcodec_alloc_context3 -> avcodec_open2

AVInputFormat会有多个数据流(视频流/音频流),所以首先找到需要处理的流:

codecpar->codec_type == AVMEDIA_TYPE_VIDEO

然后依次调用avcodec_find_decoder,avcodec_alloc_context3avcodec_open2来初始化codec。

初始化输出设备

最后是将视频数据编码为H.264,并封装到MP4容器中。所以文件名仍设定为out.mp4

打开输出设备的方法和打开输入设备方法类似:

avcodec_find_encoder -> avcodec_alloc_context3 -> avcodec_open2 -> avformat_write_header

最后的avformat_write_header不是必须的,只有当容器格式要求写Header时才会调用。与上篇中不同的时,明确指定输出CodecContext的编码器类型:

    outCodecContext->codec_id = AV_CODEC_ID_H264;
outCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
outCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
outCodecContext->bit_rate = 400000; // 2500000
outCodecContext->width = 1920;
outCodecContext->height = 1080;

同时H.264对pts和dts有要求,因此需要设定timebase:

    outCodecContext->time_base = (AVRational) {1, 25};

转码

有了上次的经验之后,处理转码就会稍显简单。 处理流程大致为:

    while av_read_frame
|
+---> avcodec_send_packet
|
+----> while avcodec_receive_frame
| 对每一数据帧进行解码
| 通过`sws_scale`进行源帧和目标帧的数据转换
|
+----> avcodec_send_frame
|
+---> while avcodec_receive_packet
|
|
+--->av_interleaved_write_frame (写入到输出设备)

转码最重要的环节就是在avcodec_receive_frame之后的逻辑。 上面说过H.264对pts有要求,因此这里需要对每一帧添加pts值。

    int64_t now = av_gettime();
const AVRational codecTimebase = outStream->time_base;
oframe->pts = av_rescale_q(now, (AVRational) {1, 1000000}, codecTimebase);

在最后写入到输出设备之前,仍需要修改pts值:

    if (opacket->pts != AV_NOPTS_VALUE)
opacket->pts = av_rescale_q(opacket->pts, outCodecContext->time_base, outStream->time_base);

至此就完成了对视频进行H.264编码的过程。可以看到和上篇处理过程大致相同,唯一不同的地方就是针对H.264编码格式进行了一些特殊处理,除此之外大致流程完全一致。

源码请点击 https://andy-zhangtao.github.io/ffmpeg-examples/

新手学习FFmpeg - 调用API完成录屏并进行H.264编码的更多相关文章

  1. 新手学习FFmpeg - 调用API完成录屏

    调用FFMPEG Device API完成Mac录屏功能. 调用FFMPEG提供的API来完成录屏功能,大致的思路是: 打开输入设备. 打开输出设备. 从输入设备读取视频流,然后经过解码->编码 ...

  2. 新手学习FFmpeg - 调用API编写实现多次淡入淡出效果的滤镜

    前面几篇文章聊了聊FFmpeg的基础知识,我也是接触FFmpeg不久,除了时间处理之外,很多高深(滤镜)操作都没接触到.在学习时间处理的时候,都是通过在ffmpeg目前提供的avfilter基础上面修 ...

  3. 新手学习FFmpeg - 调用API完成视频的读取和输出

    在写了几个avfilter之后,原本以为对ffmpeg应该算是入门了. 结果今天想对一个视频文件进行转码操作,才发现基本的视频读取,输出都搞不定. 痛定思痛,仔细研究了一下ffmpeg提供的examp ...

  4. 新手学习FFmpeg - 调用API完成两个视频的任意合并

    本次尝试在视频A中的任意位置插入视频B. 在上一篇中,我们通过调整PTS可以实现视频的加减速.这只是对同一个视频的调转,本次我们尝试对多个视频进行合并处理. Concat如何运行 ffmpeg提供了一 ...

  5. 新手学习FFmpeg - 调用API计算关键帧渲染时间点

    通过简单的计算来,线上I帧在视频中出现的时间点. 完整代码请参考 https://andy-zhangtao.github.io/ffmpeg-examples/ 名词解释 首先需要明确以下名词概念: ...

  6. 新手学习FFmpeg - 调用API调整视频局部速率

    通过修改setpts代码实现调整视频部分的播放速率. 完整代码可参考: https://andy-zhangtao.github.io/ffmpeg-examples/ 在前面提到了PTS/DTS/T ...

  7. 新手学习FFmpeg - 通过API实现可控的Filter调用链

    虽然通过声明[x][y]avfilter=a=x:b=y;avfilter=xxx的方式可以创建一个可用的Filter调用链,并且在绝大多数场合下这种方式都是靠谱和实用的. 但如果想精细化的管理AVF ...

  8. 新手学习FFmpeg - 通过API完成filter-complex功能

    本篇尝试通过API实现Filter Graph功能. 源码请参看 https://andy-zhangtao.github.io/ffmpeg-examples/ FFmpeg提供了很多实用且强大的滤 ...

  9. android 调用 screenrecord 实现录屏

    首先要说明的是并未实现,本文讲一下自己的思路. adb 使用shell 命令 screenrecord 可录屏. 自己写了个app,通过Process p = Runtime.getRuntime() ...

随机推荐

  1. ssm框架下的文件上传和文件下载

    最近在做一个ssm的项目,遇到了添加附件和下载的功能,在网上查了很多资料,发现很多都不好用,经过摸索,发现了一套简便的方法,和大家分享一下. 1.在自己已经构建好的maven  web项目中 pom. ...

  2. UPC Contest RankList – 2019年第二阶段我要变强个人训练赛第十六场

    E: 飞碟解除器 •题目描述 wjyyy在玩跑跑卡丁车的时候,获得了一个飞碟解除器,这样他就可以免受飞碟的减速干扰了.飞碟解除器每秒末都会攻击一次飞碟,但每次只有p/q的概率成功攻击飞碟.当飞碟被成功 ...

  3. php之布尔类型判断

    字符串只要不为空且不为0都为true 执行结果为 执行结果为false 因为===不仅比较值,还比较类型,所以输出为false.如果使用===号比较,最好先将变量强转为bool类型,不然可能得不到想要 ...

  4. Redis 学习笔记(篇七):Redis 持久化

    因为 Redis 是内存数据库,它将自己的数据储存在内存里面,所以如果不想办法将储存在内存中的数据库状态保存到磁盘里面,那么一旦服务器进程退出,服务器中的数据也将会丢失,为了解决这个问题,Redis ...

  5. Java经典编程题

    [程序1]   题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?   //这是一个菲波拉契数列问题p ...

  6. JS 自执行函数

    由于自己js基础知识薄弱,很多js的知识还没有掌握,所以接下来会经常写一些关于js基础知识的博客,也算给自己提个醒吧. js自执行函数,听到这个名字,首先会联想到函数.接下来,我来定义一个函数: fu ...

  7. jq css3实现跑马灯+大转盘

    前端效果, <!DOCTYPE HTML><html><head> <meta http-equiv="Content-Type" con ...

  8. 如何用Hexo+Github创建自己的技术博客

    注册一个github GitHub官网.按照一般的网站注册登录执行就好了,不详细说. 安装git 安装很简单,一直下一步 git安装教程 很多教程里都说要配置环境变量,我本人安装过5次左右的git,一 ...

  9. java的八种数据类型

    1)四种整数类型(byte.short.int.long):    byte:8 位,用于表示最小数据单位,如文件中数据,-128~127    short:16 位,很少用,-32768 ~ 327 ...

  10. CodeForces 526D Om Nom and Necklace

    洛谷题目页面传送门 & CodeForces题目页面传送门 给定字符串\(a\),求它的每一个前缀,是否能被表示成\(m+1\)个字符串\(A\)和\(m\)个字符串\(B\)交错相连的形式, ...