ffmpeg视频编码原理和实战-(5)对编码过程进行封装并解决丢帧问题
头文件:
xencode.h
#pragma once
#include <mutex>
#include<vector>
struct AVCodecContext;
struct AVPacket;
struct AVFrame;
class XEncode
{
public:///// 创建编码上下文/// @para codec_id 编码器ID号,对应ffmpeg/// @return 编码上下文 ,失败返回nullptrstatic AVCodecContext* Create(int codec_id);///// 设置对象的编码器上下文 上下文传递到对象中,资源由XEncode维护/// 加锁 线程安全/// @para c 编码器上下文 如果c_不为nullptr,则先清理资源void set_c(AVCodecContext* c);//// 设置编码参数,线程安全bool SetOpt(const char* key, const char* val);bool SetOpt(const char* key, int val);///// 打开编码器 线程安全bool Open();///// 编码数据 线程安全 每次新创建AVPacket/// @para frame 空间由用户维护/// @return 失败范围nullptr 返回的AVPacket用户需要通过av_packet_free 清理AVPacket* Encode(const AVFrame* frame);/////根据AVCodecContext 创建一个AVFrame,需要调用者释放av_frame_freeAVFrame* CreateFrame();//返回所有编码缓存中的AVPacket,解决丢帧问题std::vector<AVPacket*> End();private:AVCodecContext* c_ = nullptr; //编码器上下文std::mutex mux_; //编码器上下文锁
};
源文件:
xencode.cpp
#include "xencode.h"
#include <iostream>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}
//预处理指令导入库
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")static void PrintErr(int err)
{char buf[1024] = { 0 };av_strerror(err, buf, sizeof(buf) - 1);cerr << buf << endl;
}//
/// 创建编码上下文
/// @para codec_id 编码器ID号,对应ffmpeg
/// @return 编码上下文 ,失败返回nullptr
AVCodecContext* XEncode::Create(int codec_id)
{//1 找到编码器auto codec = avcodec_find_encoder((AVCodecID)codec_id);if (!codec){cerr << "avcodec_find_encoder failed!" << codec_id << endl;return nullptr;}//创建上下文auto c = avcodec_alloc_context3(codec);if (!c){cerr << "avcodec_alloc_context3 failed!" << codec_id << endl;return nullptr;}//设置参数默认值c->time_base = { 1,25 };c->pix_fmt = AV_PIX_FMT_YUV420P;c->thread_count = 16;return c;
}//
/// 设置对象的编码器上下文 上下文传递到对象中,资源由XEncode维护
/// 加锁 线程安全
/// @para c 编码器上下文 如果c_不为nullptr,则先清理资源
void XEncode::set_c(AVCodecContext* c)
{unique_lock<mutex>lock(mux_);if (c_){avcodec_free_context(&c_);}this->c_ = c;
}bool XEncode::SetOpt(const char* key, const char* val)
{unique_lock<mutex>lock(mux_);if (!c_)return false;auto re = av_opt_set(c_->priv_data, key, val, 0);if (re != 0){cerr << "av_opt_set failed!";PrintErr(re);}return true;
}bool XEncode::SetOpt(const char* key, int val)
{unique_lock<mutex>lock(mux_);if (!c_)return false;auto re = av_opt_set_int(c_->priv_data, key, val, 0);if (re != 0){cerr << "av_opt_set failed!";PrintErr(re);}return true;
}//
/// 打开编码器 线程安全
bool XEncode::Open()
{unique_lock<mutex>lock(mux_);if (!c_)return false;auto re = avcodec_open2(c_, NULL, NULL);if (re != 0){PrintErr(re);return false;}return true;
}
//
/// 编码数据 线程安全 每次新创建AVPacket
/// @para frame 空间由用户维护
/// @return 失败范围nullptr 返回的AVPacket用户需要通过av_packet_free 清理
AVPacket* XEncode::Encode(const AVFrame* frame)
{if (!frame)return nullptr;unique_lock<mutex>lock(mux_);if (!c_)return nullptr;//发送到编码线程auto re = avcodec_send_frame(c_, frame);if (re != 0)return nullptr;auto pkt = av_packet_alloc();//接收编码线程数据re = avcodec_receive_packet(c_, pkt);if (re == 0){return pkt;}av_packet_free(&pkt);if (re == AVERROR(EAGAIN) || re == AVERROR_EOF){return nullptr;}if (re < 0){PrintErr(re);}return nullptr;
}//返回所有编码缓存中的AVPacket,解决丢帧问题
std::vector<AVPacket*> XEncode::End()
{std::vector<AVPacket*> res;//类似一个数组,存的是AVPacket 对象指针unique_lock<mutex>lock(mux_);if (!c_)return res;auto re = avcodec_send_frame(c_, NULL);//发送NULL到编码器中,获取编码器中的缓存区if (re != 0)return res;while (re >= 0){auto pkt = av_packet_alloc();re = avcodec_receive_packet(c_,pkt);//avpacket从编码器c_中读取编码数据。if (re != 0){av_packet_free(&pkt);break;}res.push_back(pkt);}return res;}///
//根据AVCodecContext 创建一个AVFrame,需要调用者释放av_frame_free
AVFrame* XEncode::CreateFrame()
{unique_lock<mutex>lock(mux_);if (!c_)return nullptr;auto frame = av_frame_alloc();frame->width = c_->width;frame->height = c_->height;frame->format = c_->pix_fmt;auto re = av_frame_get_buffer(frame, 0);if (re != 0){av_frame_free(&frame);PrintErr(re);return nullptr;}return frame;
}
main.cpp
#include <iostream>
#include <fstream>
#include "xencode.h"
using namespace std;
extern "C" { //指定函数是c语言函数,函数名不包含重载标注
//引用ffmpeg头文件
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}int main(int argc, char* argv[])
{string filename = "400_300_25_preset";AVCodecID codec_id = AV_CODEC_ID_H264;if (argc > 1){string codec = argv[1];if (codec == "h265" || codec == "hevc"){codec_id = AV_CODEC_ID_HEVC;}}if (codec_id == AV_CODEC_ID_H264){filename += ".h264";}else if (codec_id == AV_CODEC_ID_HEVC){filename += ".h265";}//输出文件ofstream ofs;ofs.open(filename, ios::binary);XEncode en;auto c = en.Create(codec_id);c->width = 400;c->height = 300;en.set_c(c);en.SetOpt("crf", 18); //恒定速率因子(CRF)en.Open();auto frame = en.CreateFrame();int count = 0;//写入文件的帧数,看是否有500,SPS PPS IDR放在一帧中for (int i = 0; i < 500; i++){//生成AVFrame 数据 每帧数据不同//Yfor (int y = 0; y < c->height; y++){for (int x = 0; x < c->width; x++){frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;}}//UVfor (int y = 0; y < c->height / 2; y++){for (int x = 0; x < c->width / 2; x++){frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;}}frame->pts = i;//显示的时间auto pkt = en.Encode(frame);if (pkt){count++;ofs.write((char*)pkt->data, pkt->size);av_packet_free(&pkt);}}auto missing_pkt = en.End();for (auto pkt : missing_pkt){count++;ofs.write((char*)pkt->data, pkt->size);av_packet_free(&pkt);}ofs.close();en.set_c(nullptr);cout << "encode" << count << endl;getchar();return 0;
}
讨论:
1.上一篇文章提到的丢帧问题得到了解决,原因是编码器缓存不够
2.封装使得代码更加简洁
相关文章:
ffmpeg视频编码原理和实战-(5)对编码过程进行封装并解决丢帧问题
头文件: xencode.h #pragma once #include <mutex> #include<vector> struct AVCodecContext; struct AVPacket; struct AVFrame; class XEncode { public:///// 创建编码上下文/// para codec_id 编码器ID号,对应ffmpeg/// return 编码上…...
halo进阶-主题插件使用
开始捣鼓捣鼓halo,换换主题,加个页面 可参考:Halo 文档 安装/更新主题 主题如同壁纸,萝卜青菜各有所爱,大家按需更换即可; Halo好在一键更换主题,炒鸡方便。 安装/更新插件 此插件还扩展了插件…...
资深开发推荐的IDEA 插件
开发如虎添翼 工欲善其事,必先利其器。想要提升编程开发效率,必须选择一款顺手的开发工具,插件不在多,而在精,作为从业10年的程序员,我目前用到这十几个插件,在平时开发,代码review…...
数学题目系列(一)|丑数|各位和|埃氏筛|欧拉筛
一.丑数 链接:丑数 分析: 丑数只有2,3,5这三个质因数,num 2a 3b 5c也就是一个丑数是由若干个2,3,5组成,那么丑数除以这若干个数字最后一定变为1 代码 class Solution {publi…...
k8s学习--Secret详细解释与应用
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Secret什么是Secret?Secret四种类型及其特点Secret应用案例(1)将明文密码进行base64编码(2)编写创建secret的YAML文…...
功能问题:如何防止接口重复请求?
大家好,我是大澈! 本文约 1400 字,整篇阅读约需 3 分钟。 防止接口重复请求在软件开发中非常重要,重复请求必然会导致服务器资源的浪费。 因为每次请求都需要服务器进行处理,如果请求是重复的,那么服务…...
系统架构设计师【第5章】: 软件工程基础知识 (核心总结)
文章目录 5.1 软件工程5.1.1 软件工程定义5.1.2 软件过程模型5.1.3 敏捷模型5.1.4 统一过程模型(RUP)5.1.5 软件能力成熟度模型 5.2 需求工程5.2.1 需求获取5.2.2 需求变更5.2.3 需求追踪 5.3 系统分析与设计5.3.1 结构化方法5.3.2 面向对象…...
嵌入式Linux系统编程 — 2.2 标准I/O库:检查或复位状态
目录 1 检查或复位状态简介 2 feof()函数 2.1 feof()函数简介 2.2 示例程序 3 ferror()函数 4 clearerr()函数 4.1 clearerr()函数简介 4.2 示例程序 1 检查或复位状态简介 调用 fread() 函数读取数据时,如果返回值小于参数 nmemb 所指定的值,这…...
pESC-HIS是什么,怎么看?-实验操作系列-2
01 典型的pESC-HIS质粒遗传图谱 02 介绍 质粒类型:酿酒酵母蛋白表达载体 表达水平:高拷贝 诱导方法:半乳糖 启动子:GAL1和GAL10 克隆方法:多克隆位点,限制性内切酶 载体大小:6706bp 5 测…...
树形表/树形数据接口的开发
数据表格式 需要返回的json格式 点击查看json数据 [{"childrenTreeNodes" : [{"childrenTreeNodes" : null,"id" : "1-1-1","isLeaf" : null,"isShow" : null,"label" : "HTML/CSS","na…...
二叉树的镜像--c++【做题记录】
【问题描述】 给定扩展二叉树的前序序列,构建二叉树。 求这课二叉树的镜像,并输出其前序遍历序列。 【输入形式】 输入扩展二叉树的前序序列。 【输出形式】 输出镜像二叉树的前序遍历序列。 【样例输入】 ab##cd##e## 【样例输出】 镜像后二叉树的前序遍…...
redis安裝启动
1、下载redis解压 https://github.com/tporadowski/redis/releases 2、打开cmd,切换到解压的文件夹 3、redis-server.exe redis.windows.conf 启动redis redis可通过命令行输入config set requirepass password和直接修改redis.config文件中修改 requirepass 来设…...
为什么Java中的main方法必须是public static void的?
当我们创建main方法时,首先都是public、都是static,返回值都是void,方法名都是main,入参都是一个字符串数组。 在以上的方法声明中,唯一可以改变的部分就是方法的参数名,我们可以吧args改成任意我们想要使…...
shell的编程方式
文章目录 变量俩种方式第一种方式第二种方式 取消变量数组创建数组获取数组元素的方式 read输出的方式限制输入的方式 流程控制方式for循环输出的方式第一种方式第二种方式while循环输出的方式select选择输出的方式 判断方式判断的四种方式第一种方式第二种方式第三种方式 算术…...
前端面试项目细节重难点(已工作|做分享)想(八)
面试官:请你讲讲你在该项目中遇到的印象深刻的问题是什么? 答:我的回答:该项目的实现过程中我确实遇到了问题:【我会给大家整理回答思路和角度,那那么遇到这样的问题也可借鉴这种思路进行阐述】 第一层面…...
Loguru,一个 Python 日志神器
大家好!我是爱摸鱼的小鸿,关注我,收看每期的编程干货。 一个简单的库,也许能够开启我们的智慧之门, 一个普通的方法,也许能在危急时刻挽救我们于水深火热, 一个新颖的思维方式,也许能激发我们无尽的创造力, 一个独特的技巧,也许能成为我们的隐形盾牌…… 神奇的 Pyth…...
C++ 反转单词
在C中,反转一个字符串中的单词(单词之间通过空格分隔,但单词内部保持原有顺序)可以通过以下步骤实现: 找到字符串中的所有单词,这可以通过查找空格来实现。将单词存储在一个容器中(例如 std::v…...
Apache Doris 基础 -- 数据表设计(表索引)
1、索引概述 索引用于帮助快速过滤或搜索数据。目前,Doris支持两种类型的索引:内置智能索引和用户创建的二级索引。 内置智能索引 排序键和前缀索引:Apache Doris基于排序键以有序的方式存储数据。它为每1024行数据创建一个前缀索引。索引中的键是当前1024行组的…...
资源描述框架的用途及实际应用解析
什么是RDF? RDF代表 资源描述框架 RDF是用于描述网络资源的框架 RDF旨在被计算机阅读和理解 RDF并非设计用于供人阅读 RDF以 XML 编写 示例 描述购物商品的属性,如价格和可用性描述网络活动的时间表描述网页的信息(内容,作者&a…...
工业级物联网边缘网关解决方案-天拓四方
随着工业4.0时代的到来,越来越多的企业开始寻求智能化升级,以提高生产效率、降低运营成本并增强市场竞争力。然而,在实际的转型升级过程中,许多企业面临着数据孤岛、设备兼容性差、网络安全风险高等问题,这些问题严重制…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
