【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图。该样式的加载进度条可以用在页面跳转或数据处理等情况下的加载动画,比起普通的横条进度条来说,样式效果更生动美观。 实现思路: 这…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
