最简单的基于 FFmpeg 的编码器 - 纯净版(不包含 libavformat)

最简单的基于 FFmpeg 的编码器 - 纯净版(不包含 libavformat)

  • 最简单的基于 FFmpeg 的视频编码器(YUV 编码为 HEVC(H.265))
    • 正文
    • 结果
    • 工程文件下载

最简单的基于 FFmpeg 的视频编码器(YUV 编码为 HEVC(H.265))

参考雷霄骅博士的文章,链接:最简单的基于FFmpeg的编码器-纯净版(不包含libavformat)

正文

本文记录一个更加“纯净”的基于 FFmpeg 的视频编码器。此前记录过一个基于 FFmpeg 的视频编码器:最简单的基于 FFmpeg 的视频编码器(YUV 编码为 HEVC(H.265))。

这个视频编码器调用了 FFmpeg 中的 libavformat 和 libavcodec 两个库完成了视频编码工作。但是这不是一个“纯净”的编码器。上述两个库中 libavformat 完成封装格式处理,而 libavcodec 完成编码工作。一个“纯净”的编码器,理论上说只需要使用 libavcodec 就足够了,并不需要使用 libavformat。

本文记录的编码器就是这样的一个“纯净”的编码器,它仅仅通过调用 libavcodec 将 YUV 数据编码为 H.264/HEVC 等格式的压缩视频码流。

仅使用 libavcodec(不使用 libavformat)编码视频的流程如下图所示:

在这里插入图片描述

流程图中关键函数的作用如下所列:

  1. avcodec_register_all():注册所有的编解码器。
  2. avcodec_find_encoder():查找编码器。
  3. avcodec_alloc_context3():为 AVCodecContext 分配内存。
  4. avcodec_open2():打开编码器。
  5. avcodec_encode_video2():编码一帧数据。

两个存储数据的结构体如下所列:

  1. AVFrame:存储一帧未编码的像素数据。
  2. AVPacket:存储一帧压缩编码数据。

简单记录一下这个只使用 libavcodec 的“纯净版”视频编码器和使用 libavcodec+libavformat 的视频编码器的不同。

  1. 下列与libavformat相关的函数在“纯净版”视频编码器中都不存在:
    • av_register_all():注册所有的编解码器,复用/解复用器等等组件。其中调用了 * avcodec_register_all()注册所有编解码器相关的组件。
    • avformat_alloc_context():创建 AVFormatContext 结构体。
    • avformat_alloc_output_context2():初始化一个输出流。
    • avio_open():打开输出文件。
    • avformat_new_stream():创建 AVStream 结构体。avformat_new_stream() 中会调用 avcodec_alloc_context3() 创建 AVCodecContext 结构体。
    • avformat_write_header():写文件头。
    • av_write_frame():写编码后的文件帧。
    • av_write_trailer():写文件尾。
  2. 新增了如下几个函数:
    • avcodec_register_all():只注册编解码器有关的组件。
    • avcodec_alloc_context3():创建 AVCodecContext 结构体。

可以看出,相比于“完整”的编码器,这个纯净的编码器函数调用更加简单,功能相对少一些,相对来说更加的“轻量”。

源代码:

// Simplest FFmpeg Video Encoder - Pure.cpp : 定义控制台应用程序的入口点。
///**
* 最简单的基于 FFmpeg 的视频编码器(纯净版)
* Simplest FFmpeg Video Encoder Pure
*
* 源程序:
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 修改:
* 刘文晨 Liu Wenchen
* 812288728@qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.csdn.net/ProgramNovice
*
* 本程序实现了 YUV 像素数据编码为视频码流(H264,MPEG2,VP8 等等)。
* 它仅仅使用了 libavcodec(而没有使用 libavformat)。
* 是最简单的 FFmpeg 视频编码方面的教程。
* 通过学习本例子可以了解 FFmpeg 的编码流程。
*
* This software encode YUV420P data to video bitstream
* (Such as H.264, H.265, VP8, MPEG2 etc).
* It only uses libavcodec to encode video (without libavformat)
* It's the simplest video encoding software based on FFmpeg.
* Suitable for beginner of FFmpeg
*/#include "stdafx.h"#include <stdio.h>
#include <stdlib.h>// 解决报错:fopen() 函数不安全
#pragma warning(disable:4996)// 解决报错:无法解析的外部符号 __imp__fprintf,该符号在函数 _ShowError 中被引用
#pragma comment(lib, "legacy_stdio_definitions.lib")
extern "C"
{// 解决报错:无法解析的外部符号 __imp____iob_func,该符号在函数 _ShowError 中被引用FILE __iob_func[3] = { *stdin, *stdout, *stderr };
}#define __STDC_CONSTANT_MACROS#ifdef _WIN32
// Windows
extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif
#endif// test different codec
#define TEST_H264  0
#define TEST_HEVC  1int main(int argc, char* argv[])
{AVCodec *pCodec;AVCodecContext *pCodecCtx = NULL;AVFrame *pFrame;AVPacket pkt;FILE *fp_in;FILE *fp_out;int ret;int got_output = 0;int y_size;int i = 0;int framecnt = 0;char filename_in[] = "ds_480x272.yuv";#if TEST_HEVCAVCodecID codec_id = AV_CODEC_ID_HEVC;char filename_out[] = "ds.hevc";
#elseAVCodecID codec_id = AV_CODEC_ID_H264;char filename_out[] = "ds.h264";
#endifconst int in_width = 480, in_height = 272; // Input data's width and heightint framenum = 100; // Frames to encode avcodec_register_all();pCodec = avcodec_find_encoder(codec_id);if (!pCodec){// 没有找到合适的编码器printf("Can't find encoder.\n");return -1;}pCodecCtx = avcodec_alloc_context3(pCodec);if (!pCodecCtx){printf("Can't allocate video codec context.\n");return -1;}pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;pCodecCtx->width = in_width;pCodecCtx->height = in_height;pCodecCtx->bit_rate = 400000;pCodecCtx->gop_size = 10;pCodecCtx->time_base.num = 1;pCodecCtx->time_base.den = 25;// H.264// pCodecCtx->me_range = 16;// pCodecCtx->max_qdiff = 4;// pCodecCtx->qcompress = 0.6;// pCodecCtx->qmin = 10;// pCodecCtx->qmax = 51;// Optional ParampCodecCtx->max_b_frames = 1;if (codec_id == AV_CODEC_ID_H264){av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);}ret = avcodec_open2(pCodecCtx, pCodec, NULL);if (ret < 0){// 编码器打开失败printf("Failed to open encoder.\n");return -1;}pFrame = av_frame_alloc();if (!pFrame){printf("Can't allocate video frame.\n");return -1;}pFrame->format = pCodecCtx->pix_fmt;pFrame->width = pCodecCtx->width;pFrame->height = pCodecCtx->height;ret = av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, 16);if (ret < 0){printf("Can't allocate raw picture buffer.\n");return -1;}// Input raw datafp_in = fopen(filename_in, "rb");if (!fp_in){printf("Can't open %s.\n", filename_in);return -1;}// Output bitstreamfp_out = fopen(filename_out, "wb");if (!fp_out){printf("Can't open %s.\n", filename_out);return -1;}y_size = pCodecCtx->width * pCodecCtx->height;// Encodefor (i = 0; i < framenum; i++){av_init_packet(&pkt);pkt.data = NULL; // packet data will be allocated by the encoderpkt.size = 0;// Read raw YUV dataif (fread(pFrame->data[0], 1, y_size, fp_in) <= 0 ||fread(pFrame->data[1], 1, y_size / 4, fp_in) <= 0 ||fread(pFrame->data[2], 1, y_size / 4, fp_in) <= 0){return -1;}else if (feof(fp_in)){break;}// PTSpFrame->pts = i;// Encode the frameret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output);if (ret < 0){printf("Error encoding frame.\n");return -1;}if (got_output){printf("Succeed to encode frame: %5d\tsize:%5d.\n", framecnt, pkt.size);framecnt++;fwrite(pkt.data, 1, pkt.size, fp_out);av_free_packet(&pkt);}}// Flush Encoderfor (got_output = 1; got_output; i++){ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);if (ret < 0){printf("Error encoding frame.\n");return -1;}if (got_output){printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d.\n", pkt.size);fwrite(pkt.data, 1, pkt.size, fp_out);av_free_packet(&pkt);}}fclose(fp_out);avcodec_close(pCodecCtx);av_free(pCodecCtx);av_freep(&pFrame->data[0]);av_frame_free(&pFrame);system("pause");return 0;
}

结果

通过设定定义在程序开始的宏,确定需要使用的编码器。

// test different codec
#define TEST_H264  0
#define TEST_HEVC  1

输入文件是“ds_480x272.yuv”,如下图所示:

在这里插入图片描述

当 TEST_H264 设置为 1 的时候,编码 H.264 文件“ds.h264”。

程序运行的截图如下所示:

在这里插入图片描述

在这里插入图片描述

输出的 H.264 文件如下图所示:

在这里插入图片描述

当 TEST_HEVC 设置为 1 的时候,解码 HEVC 文件“ds.hevc”。

程序运行的截图如下所示:

在这里插入图片描述

在这里插入图片描述

输出的 HEVC 文件如下图所示:

在这里插入图片描述

工程文件下载

GitHub:UestcXiye / Simplest-FFmpeg-Video-Encoder-Pure

CSDN:Simplest FFmpeg Video Encoder - Pure.zip

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2807196.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

ES坑-创建索引使用_下划线-黑马旅游搜不到

学ES的时候&#xff0c;星级过滤无效 找不到数据。 需要 但是我们在创建的时候使用的是keyword 通过研究发现&#xff0c;我们导入数据的时候应该默认的为starName 我get库时候发现有2个字段 所以通过star_name搜索因为都是空数据搜不到&#xff0c;而starName类型为text所以…

UE蓝图 函数调用(CallFunction)节点和源码

系列文章目录 UE蓝图 Get节点和源码 UE蓝图 Set节点和源码 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 UE蓝图 入口(FunctionEntry)节点和源码 UE蓝图 返回结果(FunctionResult)节点和源码 UE蓝图 函数调用(CallFunction)节点和源码 文章目录 系列文章目录一、Call…

使用PM2实现高效的应用监控与管理

微信搜索“好朋友乐平”关注公众号。 1. pm2 PM2 是一个流行的进程管理器&#xff0c;用于 Node.js 应用程序。它支持应用程序的负载均衡、自动重启、日志管理、监控以及多环境管理等功能。PM2让开发者能够以守护进程的方式运行和管理 Node.js 应用&#xff0c;即使在应用崩溃…

什么是负载均衡集群?

目录 1、集群是什么&#xff1f; 2、负载均衡集群技术 3、负载均衡集群技术的实现 4、实现效果如图 5、负载均衡分类 6、四层负载均衡&#xff08;基于IP端口的负载均衡&#xff09; 7、七层的负载均衡&#xff08;基于虚拟的URL或主机IP的负载均衡) 8、四层负载与七层…

消息中间件篇之RabbitMQ-消息重复消费

一、导致重复消费的情况 1. 网络抖动。 2. 消费者挂了。 消费者消费消息后&#xff0c;当确认消息还没有发送到MQ时&#xff0c;就发生网络抖动或者消费者宕机。那当消费者恢复后&#xff0c;由于MQ没有收到消息&#xff0c;而且消费者有重试机制&#xff0c;消费者就会再一次消…

【Java程序设计】【C00282】基于Springboot的校园台球厅人员与设备管理系统(有论文)

基于Springboot的校园台球厅人员与设备管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的校园台球厅人员与设备管理系统 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xf…

政安晨:【机器学习基础】(一)—— 泛化:机器学习的目标

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 政安晨的机器学习笔记 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 简述 泛化是机器学习中的基本概念之一。它指的是通过学习…

代码随想录刷题第41天

首先是01背包的基础理论&#xff0c;背包问题&#xff0c;即如何在有限数量的货物中选取使具有一定容量的背包中所装货物价值最大。使用动规五步曲进行分析&#xff0c;使用二维数组do[i][j]表示下标从0到i货物装在容量为j背包中的最大价值&#xff0c;dp[i][j]可由不放物品i&a…

Linux---进程间通信(下)

1、System V 共享内存 原理如下图 系统调用接口介绍 int shmget(key_t key, size_t size, int shmflg) 功能&#xff1a;用来创建共享内存 参数 key&#xff1a;这个共享内存段名字&#xff0c;内核用key来标识共享内存size&#xff1a;共享内存大小shmflg&#xff1a;由九个权…

Vue局部注册组件实现组件化登录注册

Vue局部注册组件实现组件化登录注册 一、效果二、代码1、index.js2、App.vue3、首页4、登录&#xff08;注册同理&#xff09; 一、效果 注意我这里使用了element组件 二、代码 1、index.js import Vue from vue import VueRouter from vue-router import Login from ../vie…

独立版表情包小程序完整版源码前后端源码,附带系统搭建教程

搭建要求&#xff1a; 1.系统要求Nginx 1.18.0PHP-7.2mysql5.6&#xff0c;开启 ssl&#xff0c;php需要安装 sg11 扩展 2.设置伪静态 location / { index index.php index.html index.htm; if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?s$1; } } location /a…

运维的利器–监控–zabbix–第二步:建设–部署zabbix agent--windows server系统

文章目录 在windows server 2016安装zabbix agent第一步&#xff1a;下载windows安装agent软件第二步&#xff1a;解压到指定目录第三步&#xff1a;配置zabbix-agent.win.conf第四步&#xff1a;zabbix-agent安装第五步&#xff1a;启动zabbix-agent客户端第六步&#xff1a;确…

冯诺依曼体系结构 计算机组成的金字塔

01 冯诺依曼体系结构&#xff1a;计算机组成的金字塔 学习计算机组成原理&#xff0c;到底是在学些什么呢&#xff1f;这个事儿&#xff0c;一两句话还真说不清楚。不过没关系&#xff0c;我们先从“装电脑”这个看起来没有什么技术含量的事情说起&#xff0c;来弄清楚计算机到…

旋转齿轮加载

效果演示 实现了一个旋转齿轮的动画效果。具体来说&#xff0c;页面背景为深灰色&#xff0c;中间有一个齿轮装置&#xff0c;包括四个齿轮。每个齿轮都有内部的齿轮条&#xff0c;整体呈现出旋转的效果。其中&#xff0c;齿轮2是顺时针旋转的&#xff0c;齿轮1、3、4是逆时针旋…

freemarker模板引擎结合node puppeteer库实现html生成图片

效果图&#xff1a; 先看效果图&#xff0c;以下是基于freemarker模板渲染数据&#xff0c;puppeteer加载html中的js及最后图片生成&#xff1a; 背景&#xff1a; 目前为止&#xff0c;后台java根据html模板或者一个网页路径生成图片&#xff0c;都不支持flex布局及最新的c…

《The Art of InnoDB》第一部分|第2章:基础原理-整体架构

第2章&#xff1a;整体架构 目录 第2章&#xff1a;整体架构 2.1 单机架构 2.1.1 Mysql架构分层 2.1.2 InnoDB架构分层 2.1.3 小结 2.2 集群架构 2.2.1 主从模式 2.2.2 Cluster模式 2.2.3 主从模式和Cluste的区别 2.2.4 小结 2.3 总结 2.1 单机架构 2.1.1 Mysql架…

目标跟踪之KCF详解

High-Speed Tracking with Kernelized Correlation Filters 使用内核化相关滤波器进行高速跟踪 大多数现代跟踪器的核心组件是判别分类器&#xff0c;其任务是区分目标和周围环境。为了应对自然图像变化&#xff0c;此分类器通常使用平移和缩放的样本补丁进行训练。此类样本集…

Android 如何添加自定义字体

Android 如何添加自定义字体 比如我要添加 jetbrains 相关字体 在 res 文件夹中添加 font 文件夹。里面放入你的字体文件 .ttf .otf&#xff0c;字体文件名需要是小写&#xff0c;只能是字母和下划线。 在 xml 布局文件中直接通过 android:fontFamily"font/jetbrainsmo…

【JVM】StringTable 字符串常量池

参考&#xff1a;javaGuide 字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串&#xff08;String 类&#xff09;专门开辟的一块区域&#xff0c;主要目的是为了避免字符串的重复创建 String的不可变性 1.通过字面量的方式&#xff08;区别于new&#xff09;给一个…

【回顾】蚂蚁链自研TEE技术全项通过国家金融科技认证中心认证

2022年3月&#xff0c;蚂蚁集团自研TEE技术&#xff08;HyperEnclave&#xff09;通过了北京国家金融科技认证中心认证&#xff0c;TEE功能&#xff08;CA与TA交互、数据存储、加密解密算法等&#xff09;、TEE安全&#xff08;硬件安全、系统软件层安全等&#xff09;47个项目…