【FFmpeg】调用ffmpeg进行H264软解
调用FFmpeg库实现264软件解码
- 1. FFmpeg的编译
- 2. 调用FFmpeg实现H264软解
- 2.1 基本框架
- 2.2 代码实现
- 2.3 测试结果
- 3. 分析工具
- 3.1 码流分析
- 3.2 YUV分析
====== 示例工程 ======
【FFmpeg】调用FFmpeg库实现264软编
1. FFmpeg的编译
FFmpeg在Windows下的编译参考:http://t.csdnimg.cn/BtAW5
本文使用的FFmpeg版本为7.0
2. 调用FFmpeg实现H264软解
参考FFmpeg当中的/doc/examples当中的video_decode.c文件,进行调用过程的理解和实现。
2.1 基本框架
根据/doc/examples当中的实现进行修改,包括:
1.增加pragma去除fopen的warning
2.增加extern调用ffmpeg库文件
3.增加error code的打印,可以查阅其对应的错误信息
4.修改一些变量的定义
5.文件存储的方式直接修改为yuv,不使用pgm
在解码过程当中,使用了如下的函数
| 函数名 | 作用 |
|---|---|
| avcodec_find_decoder | 根据ID查找编码器,输入为AVCodecID,返回为AVCodec,记录了解码器信息 |
| avcodec_alloc_context3 | 创建codec的上下文信息,输入为AVCodec,返回为AVCodecContext,其记录了编码过程上下文的流信息 |
| av_packet_alloc | 创建数据包packet,并且初始化为默认值,返回为AVPacket;该结构存储压缩之后的数据。通常由解码器导出,然后作为输入传递给解码器,或者作为编码器的输出接收,然后传递给解码器 |
| avcodec_open2 | 打开编码器,输入为AVCodec,返回为ret。该函数初始化了codec线程和配置 |
| av_parser_init | 创建解析器的上下文,用于解析码流,输入为AVCodecID,返回为AVCodecParserContext |
| av_frame_alloc | 创建frame,返回为AVFrame。这个结构描述待编码的原始的音频或视频数据 |
| av_parser_parse2 | 解析码流文件,从输入的一串码流当中解析出一帧数据,存储到AVPacket当中,返回值ret即为一帧数据结束的索引号 |
| avcodec_send_packet | 将packet送入到解码器当中进行解码,输入为AVCodecContext和AVPacket,输出为ret |
| avcodec_receive_frame | 接收解码器已解码信息,输入为AVCodecContext和AVFrame,输出为ret |
| av_parser_close | 释放解析器 |
| avcodec_free_context | 释放解码器的上下文信息 |
| av_frame_free | 释放frame的缓冲区以及结构体本身 |
| av_packet_free | av_packet_free以及结构体本身 |
从上面这一系列的函数调用来看,大致操作流程和数据走向大约是
1.解码器的创建和初始化(avcodec_find_decoder)
2.解码器上下文的创建和初始化(avcodec_alloc_context3)
3.创建解码器输入信息,使用AVPacket进行存储(av_packet_alloc)
4.创建解析器,用于解析解码器输入信息(av_parser_init)
5.创建解码器输出信息,使用AVFrame进行存储(av_frame_alloc)
6.打开解码器(avcodec_open2)
7.进入do循环,使用fread读取数据,存储变量名为inbuf
8.对输入的数据进行解析,因为解码器是一帧一帧解码的,所以需要将数据存储到AVPacket当中。同时必须知道码流中每一帧结束的索引,用以确定下一帧的起始位置(av_parser_parse2)
9.将当前帧信息送入到解码器当中去解码,输入载体是AVPacket(avcodec_send_packet)
10.将已经解码的数据取出,输出载体是AVFrame(avcodec_receive_frame)
11.将已解码的数据存储为yuv格式【可选操作】
12.解析下一帧数据
13.当所有帧解码完毕之后,释放解析器、上下文信息、AVFrame以及AVPacket等结构体
这里使用了avcodec_send_packet和avcodec_receive_frame两个函数,这里的send和receive可以假想为使用线上网络传输软件进行数据流的传输,send将码流文件送出,receive将已经解码的yuv文件接收。
2.2 代码实现
#pragma warning(disable : 4996)#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "video_decode.h"#ifdef _WIN32
//Windows
extern "C" // 在C++文件中调用C文件需要使用,ffmpeg是使用C实现的
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#ifdef __cplusplus
};
#endif
#endif#define IMG_WIDTH 1920
#define IMG_HEIGHT 1200
#define INBUF_SIZE IMG_WIDTH * IMG_HEIGHTstatic void pgm_save(unsigned char* buf, int wrap, int xsize, int ysize, const char* filename)
{FILE* f;f = fopen(filename, "wb");fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);for (int i = 0; i < ysize; i++) {fwrite(buf + i * wrap, 1, xsize, f);}fclose(f);
}void decode_internal(AVCodecContext* av_codec_ctx, AVFrame* av_frm, AVPacket* av_pkt, FILE* fp_out)
{static char buf[1024];int ret;// 将当前帧送入到解码器当中去解码ret = avcodec_send_packet(av_codec_ctx, av_pkt);if (ret < 0) {fprintf(stderr, "Error sending a packet for decoding, error code:%d\n", ret);exit(1);}while (ret >= 0) {// 获取已经解码的数据ret = avcodec_receive_frame(av_codec_ctx, av_frm);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return;}else if (ret < 0) {fprintf(stderr, "Error during decoding, error code:%d\n", ret);exit(1);}fprintf(stderr, "saving frame %3" PRId64"\n", av_codec_ctx->frame_num);fflush(stdout);//snprintf(buf, sizeof(buf), "%s-%s" PRId64, out_filename, av_codec_ctx->frame_num);//pgm_save(av_frm->data[0], av_frm->linesize[0], av_frm->width, av_frm->height, out_filename);// 将已经解码的数据存储到文件中int size = av_frm->width * av_frm->height;fwrite(av_frm->data[0], 1, size, fp_out);//Yfwrite(av_frm->data[1], 1, size / 4, fp_out);//Ufwrite(av_frm->data[2], 1, size / 4, fp_out);//V}
}int decode(const char* in_file, const char* out_file)
{const AVCodec* av_codec;AVCodecParserContext* av_parser;AVCodecContext* av_codec_ctx = NULL;AVFrame* av_frame;uint8_t* data;size_t data_size;int ret;int eof;AVPacket* av_pkt;// malloc input bufferuint8_t* inbuf = (uint8_t*)malloc((INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) * sizeof(uint8_t));if (!inbuf) {fprintf(stderr, "Error! alloc inbuf failed");exit(1);}memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);// create h264 decoderav_codec = avcodec_find_decoder(AV_CODEC_ID_H264);if (!av_codec) {fprintf(stderr, "Codec not found\n");exit(1);}// create ctxav_codec_ctx = avcodec_alloc_context3(av_codec);if (!av_codec_ctx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}// creat pktav_pkt = av_packet_alloc();if (!av_pkt) {fprintf(stderr, "Error! alloc pkt failed");exit(1);}// parse codec infoav_parser = av_parser_init(av_codec->id);if (!av_parser) {fprintf(stderr, "parser not found\n");exit(1);}// create frameav_frame = av_frame_alloc();if (!av_frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}// open fileFILE* fp_in = fopen(in_file, "rb");if (!fp_in) {fprintf(stderr, "Could not open %s\n", in_file);exit(1);}FILE* fp_out = fopen(out_file, "wb");if (!fp_out) {fprintf(stderr, "Could not open %s\n", out_file);exit(1);}// open dec codecif (avcodec_open2(av_codec_ctx, av_codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}do {/* read raw data from the input file */data_size = fread(inbuf, 1, INBUF_SIZE, fp_in);if (ferror(fp_in)) {break;}eof = !data_size;/* use the parser to split the data into frames */data = inbuf;while (data_size > 0 || eof) {// 从输入码流当中解析出一帧数据,送入到解码器当中解码// 如果是第1帧(IDR)的话,ret表示的索引还包括头信息(SPS+PPS+SEI)ret = av_parser_parse2(av_parser, av_codec_ctx, &av_pkt->data, &av_pkt->size,data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0) {fprintf(stderr, "Error! can not parse iput data");exit(1);}// 更新后面帧的起始地址data += ret;data_size -= ret;if (av_pkt->size) {// decodedecode_internal(av_codec_ctx, av_frame, av_pkt, fp_out);}else if (eof) {break;}}} while (!eof);/* flush the decoder */decode_internal(av_codec_ctx, av_frame, NULL, fp_out);fclose(fp_in);free(inbuf);av_parser_close(av_parser);avcodec_free_context(&av_codec_ctx);av_frame_free(&av_frame);av_packet_free(&av_pkt);return 0;
}
2.3 测试结果
saving frame 1
saving frame 2
saving frame 3
saving frame 4
saving frame 5
saving frame 6
saving frame 7
saving frame 8
saving frame 9
saving frame 10
3. 分析工具
3.1 码流分析
264/265码流分析工具(有release文件):https://gitcode.com/latelee/H264BSAnalyzer
3.2 YUV分析
YUV分析工具:https://gitcode.com/IENT/YUView/overview
Github: https://github.com/DoFulangChen/video_implementation.git
相关文章:
【FFmpeg】调用ffmpeg进行H264软解
调用FFmpeg库实现264软件解码 1. FFmpeg的编译2. 调用FFmpeg实现H264软解2.1 基本框架2.2 代码实现2.3 测试结果 3. 分析工具3.1 码流分析3.2 YUV分析 示例工程 【FFmpeg】调用FFmpeg库实现264软编 1. FFmpeg的编译 FFmpeg在Windows下的编译参考:http://t.csdni…...
网络安全防护:抵御DDoS和CC攻击
在当今数字化时代,网络安全已成为任何组织或个人不可忽视的重要议题。DDoS(分布式拒绝服务)攻击和CC(命令与控制)攻击作为两种最为常见的网络攻击方式,给网络运营者和用户带来了巨大的威胁和影响。本文将介…...
初次查询大数据信用报告,需要注意哪些问题?
随着大数据的普及,基于大数据技术的大数据信用也变得越来越重要,比如在申贷之前,不少地方都会查询申贷人的大数据信用,作为风险控制的必要手段,那对于初次查询大数据信用报告的人来说,需要注意哪些问题呢?…...
最短路径[floyd算法]-----视频讲解+代码实现
求最短路径,一般有三种方法: 单源最短路径--Dijkstra算法 此算法只能求不带负权值的有向无环图 单源最短路径--Bellman-Ford算法(少考) 此算法优点在于:可以求带权值的右向无环图 但只是缺点明显,时间复杂度…...
图像/视频恢复和增强CodeFormer
github:https://github.com/sczhou/CodeFormer 尝试增强旧照片/修复人工智能艺术 面部修复 面部色彩增强和恢复 脸部修复...
WPF中ObservableCollection
在WPF(Windows Presentation Foundation)中,ObservableCollection<T> 是一个非常重要的类,它用于实现动态数据绑定功能。这个类位于 System.Collections.ObjectModel 命名空间中,是 ICollection<T>, IList…...
如何用鼠标点击在picturebox的图像上做标记
鼠标点击图像,在点击处画一个圆。 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.T…...
k8s介绍
一、前言 Kubernetes(通常简称为 K8s)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序,它提供了丰富的功能使得用户能够轻松地管理大规模的容器集群,包括自动化部署和扩展、服务发现和负载均衡、存…...
K-means聚类模型:深入解析与应用指南
K-means聚类是一种广泛使用的无监督学习算法,它通过迭代过程将数据集划分为K个聚类。以下是一篇关于K-means聚类模型的技术文章,将从不同的角度进行详尽的描述。 1. 引言 K-means聚类算法是一种简单且高效的聚类方法,广泛应用于数据挖掘、市…...
CTF-密码学基础
概述 密码学(Cryptolopy):是研究信息系统安全保密的科学 密码学研究的两个方向: 密码编码学(Cryptography):主要研究对信息进行编码,实现对信息的隐蔽密码分析学(Cryptanalytics):主要研究加密信息的破译或消息的伪造…...
代码随想录算法训练营day22 | 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树
654.最大二叉树 和构造二叉树差不多,本题使用索引的方式 class Solution:def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:return self.traversal(nums, 0, len(nums)-1)def traversal(self, nums, left, right):if left > r…...
企业信息防泄漏软件分析:盘点常用企业信息防泄漏软件
在当今数字化时代,企业信息防泄漏软件已成为保障企业数据安全不可或缺的一环。市面上众多的防泄漏软件各具特色,如何从中挑选出最适合自己企业的产品,成为了一个值得深入探讨的话题。 一、企业信息防泄漏软件分析 首先,我们需要…...
Rancher-Kubewarden-保姆级教学-含Demo测试
一、什么是Kubewarden? What is Kubewarden? | Kubewarden 1、就是容器集群的准入策略引擎。 1、使用的策略其实就是k8s原生的security context. 2、使用WebAssembly来编写策略。 1、WebAssembly,可以使用擅长的开发语言来编写策略。(下面的…...
Lumerical Script ------ array 数组类型 和 matrix 矩阵类型
Lumerical Script ------ array 数组类型 和 matrix 矩阵类型 引言正文array 数组类型matrix 矩阵类型引言 这篇仅仅用作个人笔记,因为作者本人比较擅长 Python,每次写 Lumerical Script 总是会写错代码。 正文 array 数组类型 Lumerical Script 脚本有些像 Matlab 脚本,…...
Springboot自动装配源码分析
版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version><relativePath/> <!-- lookup parent from repository --> </par…...
Visual Transformer (ViT)模型详解 动图讲解
1 Vit简介 1.1 Vit的由来 ViT是2020年Google团队提出的将Transformer应用在图像分类的模型,虽然不是第一篇将transformer应用在视觉任务的论文,但是因为其模型“简单”且效果好,可扩展性强(scalable,模型越大效果越好),成为了transformer在CV领域应用的里程碑著作,也…...
C++:完美转发(一)(std::forward)
一、理解引用折叠 (一)引用折叠 1. 在C中,“引用的引用”是非法的。像 auto& &rx x;(注意两个&之间有空格)这种直接定义引用的引用是不合法的,但是编译器在通过类型别名或模板参数推导等语境…...
西部首个全域直播基地,打造西部直播基地领军形象
天府锋巢直播产业基地作为西部直播产业的领军者,以其前瞻性的战略布局和卓越的服务体系,正加速推动全域直播的快速发展,助力直播产业实现新升级。该基地作为成都规模最大的直播基地,以加快全域直播为核心目标,通过促进…...
钟表——蓝桥杯十三届2022国赛大学B组真题
问题分析 这个问题的关键有两点:1.怎么计算时针,分针,秒针之间的夹角,2.时针,分针,秒针都是匀速运动的,并非跳跃性的。问题1很好解决看下面的代码就能明白,我们先考虑问题2…...
CSS 之 圆形波浪进度条效果
一、简介 本篇博客讲述了如何实现一个圆形波浪进度条的样式效果,具体效果参考下方GIF图。该样式的加载进度条可以用在页面跳转或数据处理等情况下的加载动画,比起普通的横条进度条来说,样式效果更生动美观。 实现思路: 这…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
全面解析各类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…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
