ffmpeg视频解码
一、视频解码流程
使用ffmpeg解码视频帧主要可分为两大步骤:初始化解码器和解码视频帧,以下代码以mjpeg为例
1. 初始化解码器
初始化解码器主要有以下步骤:
(1)查找解码器
// 查找MJPEG解码器pCodec = avcodec_find_decoder_by_name(videoCodecName);if (pCodec == nullptr) {release();return false;}// 分配解码器上下文pCodecCtx = avcodec_alloc_context3(pCodec);if (!pCodecCtx) {release();return false;}
(2)设置解码器参数
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;pCodecCtx->width = mVideoSrcWidth; // 视频宽度pCodecCtx->height = mVideoSrcHeight; // 视频高度pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ422P; // 或者其他适合的格式pCodecCtx->time_base = { 1, mVideoSrcFps }; // 帧率pCodecCtx->thread_count = 2;
(3)打开解码器
// 打开解码器if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {release();return false;}
2. 解码视频帧数据
解码视频帧数据主要有以下步骤:
(1)将编码数据送往解码器
AVPacket* packet = av_packet_alloc();if (!packet) {release();return false;}packet->data = data; // 待解码数据地址packet->size = size; // 待解码数据大小// 发送数据到解码器int ret = avcodec_send_packet(pCodecCtx, packet);if (ret < 0) {release();return false;}
(2)接收解码数据
ret = avcodec_receive_frame(pCodecCtx, pFrame);
二、使用ffmpeg实现对内存中的视频帧数据解码
以下代码中InitDecoder为初始化解码器接口,DecodeVideoFrame为解码视频帧接口
需注意:
(1)解码的色彩空间pCodecCtx->pix_fmt不可随意指定,调用avcodec_send_packet后可能会变化,这与视频帧的编码方式有关
(2)每次接收完解码数据要调用av_frame_unref进行释放,否则会有内存泄漏问题
extern "C" { // ffmpeg为使用C语言库,因此要声明为C语言的方式链接
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/error.h>
}char videoCodecName[] = "mjpeg";
FILE* out_file = nullptr;AVFormatContext* pFormatCtx = nullptr;AVCodecContext* pCodecCtx = nullptr;
const AVCodec* pCodec = nullptr;
AVFrame* pFrame = nullptr;
AVFrame* pFrameYUYV = nullptr;
int yuyv_size = 0;
uint8_t* buffer = nullptr;
SwsContext* sws_ctx = nullptr;int mVideoSrcWidth = 1920;
int mVideoSrcHeight = 1080;
int mVideoSrcFps = 30;bool InitDecoder() {fopen_s(&out_file, "test_yuv.yuv", "wb");// 查找MJPEG解码器pCodec = avcodec_find_decoder_by_name(videoCodecName);if (pCodec == nullptr) {release();return false;}// 分配解码器上下文pCodecCtx = avcodec_alloc_context3(pCodec);if (!pCodecCtx) {release();return false;}pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;pCodecCtx->width = mVideoSrcWidth; // 视频宽度pCodecCtx->height = mVideoSrcHeight; // 视频高度pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ422P; // 或者其他适合的格式pCodecCtx->time_base = { 1, mVideoSrcFps }; // 帧率pCodecCtx->thread_count = 2;// 打开解码器if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {release();return false;}// 分配帧pFrame = av_frame_alloc();pFrameYUYV = av_frame_alloc();if (!pFrame || !pFrameYUYV) {release();return false;}// 分配YUYV帧的缓冲区yuyv_size = av_image_get_buffer_size(AV_PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height, 1);buffer = (uint8_t*)av_malloc(yuyv_size * sizeof(uint8_t));if (!buffer) {release();return false;}av_image_fill_arrays(pFrameYUYV->data, pFrameYUYV->linesize, buffer, AV_PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height, 1);// 创建图像转换上下文sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUYV422,SWS_BILINEAR, nullptr, nullptr, nullptr);if (!sws_ctx) {release();return false;}
}bool DecodeVideoFrame(uint8_t* data, int size)
{AVPacket* packet = av_packet_alloc();if (!packet) {release();return false;}packet->data = data;packet->size = size;// 发送数据到解码器int ret = avcodec_send_packet(pCodecCtx, packet);if (ret < 0) {release();return false;}// 循环接收解码后的帧while (ret >= 0) {ret = avcodec_receive_frame(pCodecCtx, pFrame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;}else if (ret < 0) {release();return false;}/******** 将解码后数据进行处理 ********/// 转换为YUYV格式if (sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUYV->data, pFrameYUYV->linesize) < 0) {return false;}fwrite(pFrameYUYV->data[0], 1, yuyv_size, out_file);/******** 将解码后数据进行处理 ********/av_frame_unref(pFrame); // 将每次接收的解码帧释放掉,否则会内存泄露}av_packet_unref(packet);av_packet_free(&packet);return true;
}void release()
{if (buffer) {av_free(buffer);buffer = nullptr;}if (pCodecCtx) {avcodec_free_context(&pCodecCtx);pCodecCtx = nullptr;}if (pFormatCtx) {avformat_close_input(&pFormatCtx);pFormatCtx = nullptr;}if (pFrame) {av_frame_free(&pFrame);pFrame = nullptr;}if (pFrameYUYV) {av_frame_free(&pFrameYUYV);pFrameYUYV = nullptr;}if (out_file) {fclose(out_file);}}相关文章:
ffmpeg视频解码
一、视频解码流程 使用ffmpeg解码视频帧主要可分为两大步骤:初始化解码器和解码视频帧,以下代码以mjpeg为例 1. 初始化解码器 初始化解码器主要有以下步骤: (1)查找解码器 // 查找MJPEG解码器pCodec avcodec_fin…...
前端入门一之CSS知识详解
前言 CSS是前端三件套之一,在MarkDown中也完美兼容这些语法;这篇文章是本人大一学习前端的笔记;欢迎点赞 收藏 关注,本人将会持续更新。 文章目录 Emmet语法:CSS基本语法:css语法结构只有3种:…...
【JS学习】10. web API-BOM
文章目录 Web APIs - 第5天笔记js组成window对象定时器-延迟函数location对象navigator对象histroy对象本地存储(今日重点)localStorage(重点)sessionStorage(了解)localStorage 存储复杂数据类型 综合案例…...
C#实现递归获取所有父级的列表
条件: 父级的id是子级的父id形成递归条件 实现功能: 获取自己到最顶级父级的列表(假如最顶级父级的父ID0) 代码: 解释:CF_CODE是自己的ID,CF_PARENT_ID是父id /// <summary>/// 递归获…...
【深度学习】梯度累加和直接用大的batchsize有什么区别
梯度累加与使用较大的batchsize有类似的效果,但是也有区别 1.内存和计算资源要求 梯度累加: 通过在多个小的mini-batch上分别计算梯度并累积,梯度累积不需要一次加载所有数据,因此显著减少了内存需求。这对于显存有限的设别尤为重…...
【Linux】网络相关的命令
目录 ① ip addr show ② ip route show ③ iptables -nvL ④ ping -I enx00e04c6666c0 192.168.1.100 ⑤ ip route get 192.168.1.100 ⑥ sudo ip addr add dev enx00e04c6666c0 192.168.1.101/24 ⑦ ifconfig ⑧ netstat ⑨ traceroute ⑩ nslookup ① ip addr sho…...
leetcode哈希表(五)-四数相加II
题目 454.四数相加II 给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] nums4[l] 0 示例 1: 输入&…...
Java学习路线:Maven(一)认识Maven
目录 认识Maven 新建Maven文件 导入依赖 认识Maven Maven是一个Java的项目管理工具,通过Maven,我们可以实现: 项目自动构建,包括代码的编译、测试、打包、安装等依赖管理,快速完成依赖的导入 在学习Maven之前&…...
【深度学习】— 多输入多输出通道、多通道输入的卷积、多输出通道、1×1 卷积层、汇聚层、多通道汇聚层
【深度学习】— 多输入多输出通道、多通道输入的卷积、多输出通道、11 卷积层、汇聚层、多通道汇聚层 多输入多输出通道多通道输入的卷积示例:多通道的二维互相关运算 多输出通道实现多通道输出的互相关运算 11 卷积层11 卷积的作用 使用全连接层实现 11 卷积小结 …...
java mapper 的 xml讲解
<?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace"com.bnc.s12.mapper.GoodaCateDT…...
全面解析:区块链技术及其应用
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 全面解析:区块链技术及其应用 文章目录 全面解析:区块链技术及其应用什么是区块链区块链的工作原理1. 分…...
python基础学习笔记
本文类比c语言讲解python 一.变量和类型 前缀小知识: 注意:1.python写每一行代码时,结尾不需要 ; 这点是和c语言有很大区别的 2.代码的缩进(就是每行代码前面的空格)是非常重要的后文会提到 1.定义变量 注意: 和C/C …...
【dvwa靶场:XSS系列】XSS (DOM) 低-中-高级别,通关啦
一、低级low 拼接的url样式: http://127.0.0.1/dvwa/vulnerabilities/xss_d/?default 拼接的新内容 <script>alert("假客套")</script> 二、中级middle 拼接的url样式: http://127.0.0.1/dvwa/vuln…...
ONLYOFFICE 8.2深度体验:高效协作与卓越性能的完美融合
📝个人主页🌹:Eternity._ 🌹🌹期待您的关注 🌹🌹 ❀ONLYOFFICE 8.2 🔍引言📒1. ONLYOFFICE 产品简介📚2. 功能与特点🍁协作编辑 PDF🍂…...
Mac如何将多个pdf文件归并到一个
电脑:MacBook Pro M1 操作方式: very easy 选中想要归并的所有pdf文件,然后 右键 -> quick actions -> Create PDF 然后就可以看到将所选pdf文件归并为一个pdf的文件了...
LINUX下的Mysql:Mysql基础
目录 1.为什要有数据库 2.什么是数据库 3.LINUX下创建数据库的操作 4.LINUX创建表的操作 5.SQL语句的分类 6.Mysql的架构 1.为什要有数据库 直接用文件直接存储数据难道不行吗?非得搞个数据库呢? 首先用文件存储数据是没错,但是文件不方…...
自然语言处理方向学习建议
自然语言处理方向学习建议 自然语言处理(NLP)作为人工智能的一个重要分支,近年来在学术界和工业界都取得了显著的发展。作为即将或正在攻读博士学位的你,投身于NLP领域无疑是一个充满挑战与机遇的选择。以下是一些针对NLP方向学习…...
介绍一下如何生成随机数(c基础)
适合对象 c语言初学者 总结语言用色,个人强调用红色,注意为易错点,若有问题请告诉我谢谢。(建议通过目录观看)。一定要自己动手打代码。 rand函数 是生成随机数的函数,但实则是伪随机数。(即是同一个值) 格式 #include<st…...
24-11-1-读书笔记(三十一)-《契诃夫文集》(五)下([俄] 契诃夫 [译] 汝龙)生活乏味但不乏魅力。
文章目录 《契诃夫文集》(五)下([俄] 契诃夫 [译] 汝龙)生活乏味但不乏魅力。目录阅读笔记总结 《契诃夫文集》(五)下([俄] 契诃夫 [译] 汝龙)生活乏味但不乏魅力。 休息,…...
从“点”到“面”,热成像防爆手机如何为安全织就“透视网”?
市场上测温产品让人眼花缭乱,通过调研分析,小编发现测温枪占很高比重。但是,测温枪局限于显示单一数值信息,无法直观地展示物体的整体温度分布情况,而且几乎没有功能拓展能力。以AORO A23为代表的热成像防爆手机改变了…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...
