FFmpeg源码中,计算CRC校验的实现
一、CRC简介
CRC(Cyclic Redundancy Check),即循环冗余校验,是一种根据网络数据包或电脑文件等数据产生简短固定位数校核码的快速算法,主要用来检测或校核数据传输或者保存后可能出现的错误。CRC利用除法及余数的原理,实现错误侦测的功能,具有原理清晰、实现简单等优点。注意,CRC校验只能对数据进行检错,而不能对其纠错。因此,在CAN总线上的每个节点一旦发现数据有错,CAN控制器会根据总线仲裁原则和受损报文优先发送原则对已损坏的报文进行自动重传。具体可以参考:《百度百科——CRC》。
二、CRC校验原理
可以参考:《CRC校验原理及实现》、《什么是CRC循环冗余校验,是如何对数据进行计算的?》、《CRC码计算及校验原理的最通俗诠释》等文章。
三、CRC8/CRC16/CRC32
常见的CRC校验类型有CRC8/CRC16/CRC32。其中:
CRC8:一种较短的CRC校验类型,其生成的校验码由8比特组成,即一个字节。这意味着它能提供的唯一校验结果数量为2^8=256种。
CRC16:相较于CRC8,CRC16提供了更强的错误检测能力,其生成的校验码由16比特构成,对应于2^16种不同的校验值。这种扩展提高了算法对单比特错误以及一定长度突发错误序列的检测概率。
CRC32:位于CRC系列顶端的是CRC32,它产生的校验码有32比特,可形成2^32种不同组合。这种级别的CRC校验尤其适用于大数据传输、高可靠性要求的数据完整性保证场合,如ISO 9660文件系统、ZIP压缩文件格式,以及TCP/IP协议栈中的IP头校验等。
具体可以参考:《CRC8/CRC16/CRC32全面对比详解》。
四、FFmpeg源码中,计算CRC校验的实现
FFmpeg源码中通过av_crc函数计算CRC校验,该函数定义在libavutil/crc.c中:
/*** Calculate the CRC of a block.* @param ctx initialized AVCRC array (see av_crc_init())* @param crc CRC of previous blocks if any or initial value for CRC* @param buffer buffer whose CRC to calculate* @param length length of the buffer* @return CRC updated with the data from the given block** @see av_crc_init() "le" parameter*/
uint32_t av_crc(const AVCRC *ctx, uint32_t crc,const uint8_t *buffer, size_t length)
{const uint8_t *end = buffer + length;#if !CONFIG_SMALLif (!ctx[256]) {while (((intptr_t) buffer & 3) && buffer < end)crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);while (buffer < end - 3) {crc ^= av_le2ne32(*(const uint32_t *) buffer); buffer += 4;crc = ctx[3 * 256 + ( crc & 0xFF)] ^ctx[2 * 256 + ((crc >> 8 ) & 0xFF)] ^ctx[1 * 256 + ((crc >> 16) & 0xFF)] ^ctx[0 * 256 + ((crc >> 24) )];}}
#endifwhile (buffer < end)crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);return crc;
}
形参ctx:输入型参数,指向通过av_crc_get_table函数获取到的CRC table,即初始化的AVCRC数组。
形参crc:输入型参数,先前块的CRC(如果有的话)或CRC的初始值。
形参buffer:输入型参数,指向缓冲区的指针,该缓冲区存放要计算其CRC校验的数据。
形参length:输入型参数,形参buffer指向的缓冲区的长度,单位为字节。
返回值:计算出来的CRC校验。
五、av_crc函数的使用例子
(一)通过av_crc函数计算CRC校验
需要校验的数据如下(以十六进制表示),总共28个字节数据:
02 B0 1D 00 01 C1 00 00 E1 00 F0 00 1B E1 00 F0 00 0F E1 01 F0 06 0A 04 75 6E 64 00
以参数模型为CRC-32/MPEG-2为例,通过 CRC(循环冗余校验)在线计算 得出上面数据的CRC校验为0x08,0x7D,0xE8,0x77:

把FFmpeg中CRC相关的函数移植出来,编写测试例子main.cpp:
#include <stdio.h>
#include <stdint.h>
//#include <stdint-uintn.h>#ifdef __GNUC__
# define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
# define AV_GCC_VERSION_AT_MOST(x,y) (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y))
#else
# define AV_GCC_VERSION_AT_LEAST(x,y) 0
# define AV_GCC_VERSION_AT_MOST(x,y) 0
#endif#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__)
# define av_const __attribute__((const))
#else
# define av_const
#endif#ifndef av_always_inline
#if AV_GCC_VERSION_AT_LEAST(3,1)
# define av_always_inline __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
# define av_always_inline __forceinline
#else
# define av_always_inline inline
#endif
#endif#define AV_BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff))
#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16))
#define AV_BSWAP64C(x) (AV_BSWAP32C(x) << 32 | AV_BSWAP32C((x) >> 32))#define AVOnce char
#define AV_ONCE_INIT 0static inline int ff_thread_once(char *control, void (*routine)(void))
{if (!*control) {routine();*control = 1;}return 0;
}#define EINVAL 22 /* Invalid argument */
#define AVERROR(e) (-(e)) ///< Returns a negative error code from a POSIX error code, to return from library functions.#define CRC_TABLE_SIZE 1024#define av_le2ne32(x) (x)typedef uint32_t AVCRC;typedef enum {AV_CRC_8_ATM,AV_CRC_16_ANSI,AV_CRC_16_CCITT,AV_CRC_32_IEEE,AV_CRC_32_IEEE_LE, /*< reversed bitorder version of AV_CRC_32_IEEE */AV_CRC_16_ANSI_LE, /*< reversed bitorder version of AV_CRC_16_ANSI */AV_CRC_24_IEEE,AV_CRC_8_EBU,AV_CRC_MAX, /*< Not part of public API! Do not use outside libavutil. */
}AVCRCId;static AVCRC av_crc_table[AV_CRC_MAX][CRC_TABLE_SIZE];#define DECLARE_CRC_INIT_TABLE_ONCE(id, le, bits, poly) \
static AVOnce id ## _once_control = AV_ONCE_INIT; \
static void id ## _init_table_once(void) \
{ \av_crc_init(av_crc_table[id], le, bits, poly, sizeof(av_crc_table[id])); \
}#ifndef av_bswap32
static av_always_inline av_const uint32_t av_bswap32(uint32_t x)
{return AV_BSWAP32C(x);
}
#endif#define CRC_INIT_TABLE_ONCE(id) ff_thread_once(&id ## _once_control, id ## _init_table_once)DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_8_ATM, 0, 8, 0x07)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_8_EBU, 0, 8, 0x1D)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI, 0, 16, 0x8005)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_16_CCITT, 0, 16, 0x1021)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_24_IEEE, 0, 24, 0x864CFB)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE, 0, 32, 0x04C11DB7)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE_LE, 1, 32, 0xEDB88320)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI_LE, 1, 16, 0xA001)int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size)
{unsigned i, j;uint32_t c;if (bits < 8 || bits > 32 || poly >= (1LL << bits))return AVERROR(EINVAL);if (ctx_size != sizeof(AVCRC) * 257 && ctx_size != sizeof(AVCRC) * 1024)return AVERROR(EINVAL);for (i = 0; i < 256; i++) {if (le) {for (c = i, j = 0; j < 8; j++)c = (c >> 1) ^ (poly & (-(c & 1)));ctx[i] = c;} else {for (c = i << 24, j = 0; j < 8; j++)c = (c << 1) ^ ((poly << (32 - bits)) & (((int32_t) c) >> 31));ctx[i] = av_bswap32(c);}}ctx[256] = 1;
#if !CONFIG_SMALLif (ctx_size >= sizeof(AVCRC) * 1024)for (i = 0; i < 256; i++)for (j = 0; j < 3; j++)ctx[256 * (j + 1) + i] =(ctx[256 * j + i] >> 8) ^ ctx[ctx[256 * j + i] & 0xFF];
#endifreturn 0;
}const AVCRC *av_crc_get_table(AVCRCId crc_id)
{
#if !CONFIG_HARDCODED_TABLESswitch (crc_id) {case AV_CRC_8_ATM: CRC_INIT_TABLE_ONCE(AV_CRC_8_ATM); break;case AV_CRC_8_EBU: CRC_INIT_TABLE_ONCE(AV_CRC_8_EBU); break;case AV_CRC_16_ANSI: CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI); break;case AV_CRC_16_CCITT: CRC_INIT_TABLE_ONCE(AV_CRC_16_CCITT); break;case AV_CRC_24_IEEE: CRC_INIT_TABLE_ONCE(AV_CRC_24_IEEE); break;case AV_CRC_32_IEEE: CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE); break;case AV_CRC_32_IEEE_LE: CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE_LE); break;case AV_CRC_16_ANSI_LE: CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI_LE); break;default: ;}
#endifreturn av_crc_table[crc_id];
}uint32_t av_crc(const AVCRC *ctx, uint32_t crc,const uint8_t *buffer, size_t length)
{const uint8_t *end = buffer + length;#if !CONFIG_SMALLif (!ctx[256]) {while (((intptr_t) buffer & 3) && buffer < end)crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);while (buffer < end - 3) {crc ^= av_le2ne32(*(const uint32_t *) buffer); buffer += 4;crc = ctx[3 * 256 + ( crc & 0xFF)] ^ctx[2 * 256 + ((crc >> 8 ) & 0xFF)] ^ctx[1 * 256 + ((crc >> 16) & 0xFF)] ^ctx[0 * 256 + ((crc >> 24) )];}}
#endifwhile (buffer < end)crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);return crc;
}int main()
{uint8_t cur_section_buf[28] = {0x02, 0xB0, 0x1D, 0x00, 0x01, 0xC1, 0x00, 0x00, 0xE1, 0x00, 0xF0, 0x00, 0x1B, 0xE1, 0x00, 0xF0, 0x00, 0x0F, 0xE1, 0x01, 0xF0, 0x06, 0x0A, 0x04, 0x75, 0x6E, 0x64, 0x00};int crc = av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, cur_section_buf, sizeof(cur_section_buf));printf("crc:%d\n", crc);return 0;
}
在Linux系统上编译,运行,打印十进制的2011725064,即0x77,0xE8, 0x7D,0x08。可以看到跟上述“CRC在线计算”的计算结果是相符的,只是高低位顺序不一样而已:

(二)通过av_crc函数判断CRC校验是否正确
av_crc函数还有一个用途是用来判断一段包含CRC校验的数据中,CRC校验是否正确。我们改写上面的man.cpp:
uint8_t cur_section_buf[32] = {0x02, 0xB0, 0x1D, 0x00, 0x01, 0xC1, 0x00, 0x00, 0xE1, 0x00, 0xF0, 0x00, 0x1B, 0xE1, 0x00, 0xF0, 0x00, 0x0F, 0xE1, 0x01, 0xF0, 0x06, 0x0A, 0x04, 0x75, 0x6E, 0x64, 0x00, 0x08, 0x7D, 0xE8, 0x77}; int crc_valid = !av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, cur_section_buf, sizeof(cur_section_buf));printf("crc_valid:%d\n", crc_valid);
重新编译,运行,打印“1”表示CRC校验正确,打印|“0”表示校验不正确:
![]()
相关文章:
FFmpeg源码中,计算CRC校验的实现
一、CRC简介 CRC(Cyclic Redundancy Check),即循环冗余校验,是一种根据网络数据包或电脑文件等数据产生简短固定位数校核码的快速算法,主要用来检测或校核数据传输或者保存后可能出现的错误。CRC利用除法及余数的原理,实现错误侦…...
Android笔记【14】结合LaunchedEffect实现计时器功能。
一、问题 cy老师第五次作业 结合LaunchedEffect实现计时器功能。要求:动态计时,每秒修改时间,计时的时间格式为“00:00:00”(小时:分钟:秒)提交源代码的文本和运行截图…...
kubectl 和 kubeconfig 基本原理
云原生学习路线导航页(持续更新中) kubernetes学习系列 快捷连接 Kubernetes架构原则和对象设计(一) 本文介绍kubectl的几个常用命令,kubconfig文件基本属性,并开启kubectl debug日志分析其背后基本原理 …...
LVGL笔录----动画
最近在搞LVGL动画内容,发现网上能参考的资源太少了。现将自己学习到的内容做个笔录,仅供自己记录,若对你有帮助,那么最好不过,共勉! 首先,我是在CodeBlock上仿真 #define PI 3.14159265359stat…...
【LeetCode热题100】BFS解决FloodFill算法
这篇博客主要记录了使用BFS解决FloodFill算法的几道题目,包括图像渲染、岛屿数量、岛屿的最大面积、被包围的区域。 class Solution {using PII pair<int, int>; public:vector<vector<int>> floodFill(vector<vector<int>>& im…...
设计模式の软件设计原则
文章目录 前言一、聚合&组合&继承&依赖1.1、继承1.2、组合1.3、聚合1.4、依赖 二、单一职责原则2.1、单一职责原则反面案例2.2、单一职责原则反面案例的改进 三、接口隔离原则3.1、接口隔离原则反面案例3.2、接口隔离原则反面案例的改进 四、依赖倒转原则4.1、依赖…...
Linux centos7 下载MySQL5.7仓库的命令
wget 是一个非常强大的命令行工具,用于从网络上下载文件。它是 Linux 和其他 Unix-like 系统中常用的工具之一。wget 命令的各个参数有着不同的含义,下面是您提供的命令 wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.onarch.r…...
CSS flex布局踩坑小记:flex-basis属性之0px与0%的差异 (赞)
原文出处:CSS flex布局踩坑小记:flex-basis属性之0px与0%的差异_flex-basis 0%-CSDN博客 讲述flex容器被撑大的原因(误用:flex-basis: 0%;)及解决方法(用:flex-basis: 0px;)...
华硕主板不能开启
正常流程: [主機板]BIOS如何設置主機板整合圖形(內顯)和獨立顯示卡同時顯示輸出 | 官方支援 | ASUS 台灣 如果开启了CSR兼容性模式,在BIOS里面,就必须关掉,才能支持多显示器,如下图显示的标识才会出现。...
室联人形机器人:家政服务任务结构化、技术要点、深入应用FPGA的控制系统框架设计(整合版)
目录: 0 引言 1 人形机器人对室内家政服务任务的结构化 1.1人形机器人在室内家政服务中的比较优势 1.1.1 人形机器人拟人性的7个维度 1.1.2 拟人性在室内家政服务工作中的比较优势 1.1.3 潜在的重要用户:宠物爱好者 1.2 居所室内环境的特征与结构…...
OpenAI 发布 o1 LLM,推出 ChatGPT Pro
OpenAI正式发布了专为复杂推理而构建的 OpenAI o1大型语言模型(LLM)。 该公司还推出了 ChatGPT Pro,这是一项每月 200 美元的套餐,包括无限制访问 OpenAI o1、o1-mini、GPT-4o 和高级语音对话。 OpenAI o1 从 9 月 12 日起在 ChatGPT 中推出预览版&…...
【MySQL】存储过程和触发器
MySQL存储过程和触发器 一、存储过程的介绍二、存储过程的相关操作2.1创建存储过程2.2查看存储过程2.4调用存储过程2.5删除存储过程 三、变量3.1系统变量3.2用户定义变量3.3局部变量 四、存储过程中的关键字4.1 if4.2参数4.3case4.4 while4.5repeat4.6 loop4.7游标4.8条件处理程…...
QT4和 QT5 槽函数连接的区别
正常连接方式 //QT4官方用列QLabel *label new QLabel;QScrollBar *scrollBar new QScrollBar;QObject::connect(scrollBar, SIGNAL(valueChanged(int)),label, SLOT(setNum(int)));//QT5官方用列QLabel *label new QLabel;QLineEdit *lineEdit new QLineEdit;QObject::c…...
使用 PyTorch 和 Horovod 来编写一个简单的分布式训练 demo
使用 PyTorch 和 Horovod 来编写一个简单的分布式训练 demo,可以帮助你理解如何在多GPU或多节点环境中高效地训练深度学习模型。Horovod 是 Uber 开发的一个用于分布式训练的框架,它支持 TensorFlow、Keras、PyTorch 等多个机器学习库。下面是一个基于 P…...
SQL复杂查询功能介绍及示例
文章目录 1. 多表连接(JOIN)功能介绍应用场景示例查询及初始表格customers 表(未查询前)orders 表(未查询前)INNER JOIN 示例LEFT JOIN 示例 2. 子查询(Subquery)功能介绍应用场景示…...
shell基础用法
shell基础知识 shell中的多行注释 :<<EOF read echo $REPLY # read不指定变量,则默认写入$REPLY EOF # :<<EOF ...EOF 多行注释,EOF可以替换为!# 等文件目录和执行目录 echo $0$0 # ./demo.sh echo $0的realpath$(realpath…...
C#设计模式--策略模式(Strategy Pattern)
策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需…...
【opencv入门教程】15. 访问像素的十四种方式
文章选自: 一、像素访问 一张图片由许多个点组成,每个点就是一个像素,每个像素包含不同的值,对图像像素操作是图像处理过程中常使用的 二、访问像素 void Samples::AccessPixels1(Mat &image, int div 64) {int nl imag…...
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
导航: 本文一些内容需要聚簇索引、非聚簇索引、B树、覆盖索引、索引下推等前置概念,虽然本文有简单回顾,但详细可以参考下文的【MySQL高级篇】 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/谷粒商城/学成在线设计模…...
根据html的段落长度设置QtextBrowser的显示内容,最少显示一个段落
要根据 HTML 段落的长度设置 QTextBrowser 的显示内容,并确保至少显示一个段落,可以通过以下步骤来实现: 加载 HTML 内容:首先,你需要加载 HTML 内容到 QTextBrowser 中。可以通过 setHtml() 方法来设置 HTML。 计算段…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...
麒麟系统使用-进行.NET开发
文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的,如果需要进行.NET开发,则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET,所以要进…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...
k8s从入门到放弃之Pod的容器探针检测
k8s从入门到放弃之Pod的容器探针检测 在Kubernetes(简称K8s)中,容器探测是指kubelet对容器执行定期诊断的过程,以确保容器中的应用程序处于预期的状态。这些探测是保障应用健康和高可用性的重要机制。Kubernetes提供了两种种类型…...
