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. java反射原理及Class应用

    反射:框架设计灵魂 框架:半成品软件,可以在框架基础上进行软件开发,简化编码 反射:将类的各个组成部分封装我其他对象,这就是反射机制 好处:  1.可以在程序运行过程中,操作这些对象  2.可以解耦, ...

  2. Vue 报错 listen EADDRINUSE :::8080

    今天在重启vue项目的时候,发现报了错, listen EADDRINUSE :::8080错误提示 原因:因为另一个项目占用了8080端口,我直接在命令行npm run dev第二个项目,就给出了这 ...

  3. C#实现Hash应用全解

    1.引言 HASH是根据文件内容的数据通过逻辑运算得到的数值, 不同的文件(即使是相同的文件名)得到的HASH值是不同的. 通过一定的哈希算法(典型的有MD5,SHA-1等),将一段较长的数据映射为较 ...

  4. Web前端开发——Ionic 3.0【爱创课堂专业前端培训】

    前端开发——Ionic 3.0 一.Ionic 移动端有三种开发方向 源生APP开发, 移动端web开发 混合开发(介于以上两者之间的) 类微信小程序 reactNative,用react语法,开发a ...

  5. 创建String对象过程的内存分配

      转载自  https://blog.csdn.net/xiabing082/article/details/49759071       常量池(Constant Pool):指的是在编译期被确定 ...

  6. jQuery插件之路(一)——试着给jQuery的一个Carousel插件添加新的功能

    前几日在网上看到了一个关于Carousel插件的教学视频,于是也顺便跟着学习着做了一下.但是在做完之后发现,在别的网站上面看到类似的效果要比现在做的这个要多一个功能,也就是在底下会有一些按钮,当鼠标放 ...

  7. 9、数组中删除元素(test6.java)

    前文讲到,通过函数,进行数组元素的添加,这里同样通过这个函数,进行数组的删除. 举个例子,代码如下: //导入输入所需要的包 import java.util.Scanner; public clas ...

  8. [转载]使用Java操作Mongodb

    HelloWorld程序 学习任何程序的第一步,都是编写HelloWorld程序,我们也不例外,看下如何通过Java编写一个HelloWorld的程序. 首先,要通过Java操作Mongodb,必须先 ...

  9. mybatis批量更新策略

    我们知道循环中操作db会导致连接数满,严重影响数据库性能.所以在对db进行DQL与DML时,根据业务逻辑尽量批量操作,这里我们介绍下使用mybatis批量更新mysql的两种方式. 方式一: < ...

  10. Oralce PL/SQL 调用C

    1.要把C写成扩展的形式 ex.c文件 int __declspec(dllexport) sum(int a,int b) { return a+b; } 2.把C代码编译成动态库(*.dll 或 ...