当前位置: 首页 > news >正文

C/C++ H264文件解析

C++实现H264文件以及一段H264码流解析,源码如下:

h264Parse.h:

#ifndef _H264PARSE_H_
#define _H264PARSE_H_#include <fstream>class H264Parse
{
public:int open_file(const std::string &filename);/*** @brief 从文件中读取一个nalu,包含起始码* @param buf 存放nalu的缓冲区* @param size 缓冲区大小* @param len nalu的长度* @param n 每次读取多少个字节* @return  -1 失败    0 已到文件末尾     1 成功获取到一个nalu*/int read_nalu(uint8_t *buf, uint32_t size, uint32_t &len, uint32_t n);void close_file();// 获取起始码长度static int get_startCode_len(const uint8_t *ptr);static const uint8_t *find_startCode_pos(const uint8_t *pbuf, uint32_t len);/*** @brief 从一段h264码流中分割nalu,包含起始码* @param stream h264码流* @param streamLen 码流大小* @param nalu Pointer to the extracted nalu* @param naluLen nalu的长度* @param record Pointer用于记录状态,第一次分割时把 *record 赋值为NULL* @return  -1 失败    0 已分割完     1 成功获取到一个nalu*/static int nalu_tok(const uint8_t *stream, uint32_t streamLen, const uint8_t **nalu,uint32_t &naluLen, const uint8_t **record);private:std::fstream h264File;int read_start_code(uint8_t *buf);int adjust_filePointer_pos(uint32_t totalRead, uint32_t naluLen);
};#endif // _H264PARSE_H_

h264Parse.cpp:

#include "h264Parse.h"
#include <iostream>
#include <cstring>int H264Parse::open_file(const std::string &filename)
{h264File.open(filename, std::ios::in | std::ios::binary);if (!h264File.is_open()){std::cout << "Failed to open the H.264 file." << std::endl;return -1;}return 0;
}int H264Parse::get_startCode_len(const uint8_t *ptr)
{if (ptr[0] == 0x00 && ptr[1] == 0x00){if (ptr[2] == 0x01)return 3;else if (ptr[2] == 0x00 && ptr[3] == 0x01)return 4;}return -1; // 无效的起始码
}// 读取起始码,并返回其长度
int H264Parse::read_start_code(uint8_t *buf)
{// 读取前4个字节来判断起始码长度h264File.read(reinterpret_cast<char *>(buf), 4);if (h264File.gcount() < 4){return -1;}return get_startCode_len(buf);
}// 寻找NALU的起始码位置
const uint8_t *H264Parse::find_startCode_pos(const uint8_t *pbuf, uint32_t len)
{const uint8_t *p = pbuf;if (len < 3)return NULL;for (uint32_t i = 0; i < len - 3; ++i){if ((p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01) ||(p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x00 && p[3] == 0x01)){return p;}p++;}// 检查最后3字节是不是起始码if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01)return p;return NULL;
}// 调整文件指针位置
int H264Parse::adjust_filePointer_pos(uint32_t totalRead, uint32_t naluLen)
{int offset = -(totalRead - naluLen);if (!h264File.eof()){h264File.seekg(offset, std::ios::cur);}else{h264File.clear(); // 达到文件末尾了要先清除 eof 标志h264File.seekg(offset, std::ios::end);}if (h264File.fail()){std::cout << "seekg failed!" << std::endl;return -1;}return 0;
}int H264Parse::read_nalu(uint8_t *buf, uint32_t size, uint32_t &len, uint32_t n)
{uint32_t totalRead = 0;int startCodeLength = read_start_code(buf);if (startCodeLength == -1){printf("read_start_code failed.\n");return -1;}totalRead += 4; // 已经读取了4字节的长度while (true){if (size < totalRead + n){std::cout << "Buffer size is too small: size=" << size<< ", needed=" << totalRead + n << std::endl;return -1;}h264File.read(reinterpret_cast<char *>(buf + totalRead), n);std::streamsize bytesRead = h264File.gcount();if (bytesRead <= 0){std::cout << "Failed to read from file!" << std::endl;return -1;}uint32_t searchStart = (totalRead > 4) ? totalRead - 3 : startCodeLength;const uint8_t *naluEnd = find_startCode_pos(buf + searchStart,bytesRead + (totalRead > 4 ? 3 : 0));totalRead += bytesRead;if (naluEnd != nullptr){len = naluEnd - buf;if (adjust_filePointer_pos(totalRead, len) < 0)return -1;break;}// 是否读取到文件末尾if (h264File.peek() == std::char_traits<char>::eof()){len = totalRead;return 0; // NALU完整读取}}memset(buf + len, 0, size - len); // 清空剩余部分return 1;                         // 成功读取
}void H264Parse::close_file()
{h264File.close();
}int H264Parse::nalu_tok(const uint8_t *stream, uint32_t streamLen, const uint8_t **nalu,uint32_t &naluLen, const uint8_t **record)
{const uint8_t *current = (record && *record) ? *record : stream;uint32_t offset = static_cast<uint32_t>(current - stream);if (offset >= streamLen){return -1; // 当前记录位置超出缓冲区}int scLen = get_startCode_len(current);if (scLen == -1 || (current + scLen) > (stream + streamLen)){return -1; // 无效的起始码或起始码长度超出缓冲区}// 查找下一个起始码的位置const uint8_t *next_start = find_startCode_pos(current + scLen, streamLen - offset - scLen);if (next_start){*nalu = current;naluLen = static_cast<uint32_t>(next_start - current);*record = next_start;return 1; // 成功获取到一个 NALU}else{// 最后一个 NALU*nalu = current;naluLen = streamLen - offset;*record = NULL; // 重置记录指针return 0;       // 分割完毕}
}

测试:

#include <iostream>
#include <vector>
#include "h264Parse.h"void test1()
{int ret;int number = 0;H264Parse h264;uint8_t buf[1024 * 1024];uint32_t len = 0;h264.open_file("/home/tl/work/app/res/output.h264");while ((ret = h264.read_nalu(buf, sizeof(buf), len, 1024 * 2)) != -1){printf("number: %d nalu len: %u\n", number, len - h264.get_startCode_len(buf));number++;if (ret == 0)break;}if (ret == -1){std::cout << "read_nalu failed." << std::endl;}h264.close_file();
}// 辅助函数:打印 NALU 信息
void print_nalu(const uint8_t *nalu, uint32_t len, int index)
{std::cout << "NALU " << index << ": Length = " << len << " bytes, Data = ";for (uint32_t i = 0; i < len; ++i){printf("%02X ", nalu[i]);}std::cout << std::endl;
}void test2()
{// 构造一个模拟的 H.264 码流缓冲区,包含多个 NALU// 起始码格式:0x000001 (3 字节) 和 0x00000001 (4 字节)// NALU 内容:随机填充的字节数据std::vector<uint8_t> buffer;// NALU 1: 3 字节起始码 + 5 字节数据std::vector<uint8_t> nalu1 = {0x00, 0x00, 0x01, 0x65, 0x88, 0x84, 0x21, 0xA0};buffer.insert(buffer.end(), nalu1.begin(), nalu1.end());// NALU 2: 4 字节起始码 + 6 字节数据std::vector<uint8_t> nalu2 = {0x00, 0x00, 0x00, 0x01, 0x41, 0x9A, 0x5C, 0xD4, 0x00, 0x11};buffer.insert(buffer.end(), nalu2.begin(), nalu2.end());// NALU 3: 3 字节起始码 + 4 字节数据std::vector<uint8_t> nalu3 = {0x00, 0x00, 0x01, 0x06, 0x05, 0xFF, 0xEE};buffer.insert(buffer.end(), nalu3.begin(), nalu3.end());// NALU 4: 3 字节起始码 + 3 字节数据 (测试末尾)std::vector<uint8_t> nalu4 = {0x00, 0x00, 0x01, 0x07, 0xAD, 0xBE};buffer.insert(buffer.end(), nalu4.begin(), nalu4.end());// 输出构建的缓冲区(可选)std::cout << "Constructed H.264 Buffer: ";for (size_t i = 0; i < buffer.size(); ++i){printf("%02X ", buffer[i]);}std::cout << "\n\n";const uint8_t *pnalu = nullptr;uint32_t nale_len = 0;const uint8_t *pRecord = NULL; // 初始时为 NULLint ret;int nalu_index = 1;// 循环分割并打印每个 NALUwhile ((ret = H264Parse::nalu_tok(buffer.data(), buffer.size(), &pnalu, nale_len, &pRecord)) != -1){print_nalu(pnalu, nale_len, nalu_index);nalu_index++;if (ret == 0)break;}if (ret == -1){std::cout << "Error occurred during NALU tokenization." << std::endl;}
}// 主函数
int main()
{test1();// test2();return 0;
}

相关文章:

C/C++ H264文件解析

C实现H264文件以及一段H264码流解析&#xff0c;源码如下&#xff1a; h264Parse.h: #ifndef _H264PARSE_H_ #define _H264PARSE_H_#include <fstream>class H264Parse { public:int open_file(const std::string &filename);/*** brief 从文件中读取一个nalu&…...

【Windows】电脑端口明明没有进程占用但显示端口被占用(动态端口)

TOC 一、问题 重启电脑后&#xff0c;启用某个服务显示1089端口被占用。 查看是哪个进程占用了&#xff1a; netstat -aon | findstr "1089"没有输出&#xff0c;但是换其他端口&#xff0c;是可以看到相关进程的&#xff1a; 现在最简单的方式是给我的服务指定另…...

Redis 持久化 问题

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 持久化 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 持久化 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis & …...

vivado 配置

配置 配置指的是将特定应用数据加载到 FPGA 器件的内部存储器的进程。 赛灵思 FPGA 配置数据储存在 CMOS 配置锁存 (CCL) 中&#xff0c;因此配置数据很不稳定&#xff0c;且在每次 FPGA 器件上电后都必须重 新加载。 赛灵思 FPGA 器件可通过配置引脚&#xff0c;自行…...

Java如何实现PDF转高质量图片

大家好&#xff0c;我是 V 哥。在Java中&#xff0c;将PDF文件转换为高质量的图片可以使用不同的库&#xff0c;其中最常用的库之一是 Apache PDFBox。通过该库&#xff0c;你可以读取PDF文件&#xff0c;并将每一页转换为图像文件。为了提高图像的质量&#xff0c;你可以指定分…...

itemStyle.normal.label is deprecated, use label instead.

itemStyle.normal.label is deprecated, use label instead. normal’hierarchy in label has been removed since 4.0. All style properties are configured in label directly now. 错误写法&#xff1a; itemStyle: {normal: {// color: #00E0FF, // 设置折线点颜色 labe…...

如何在 Linux VPS 上保护 MySQL 和 MariaDB 数据库

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 有许多在 Linux 和类 Unix 系统上可用的 SQL 数据库语言实现。MySQL 和 MariaDB 是在服务器环境中部署关系型数据库的两个流行选项…...

CSS 样式 box-sizing: border-box; 用于控制元素的盒模型如何计算宽度和高度

文章目录 box-sizing: border-box; 的含义默认盒模型 (content-box)border-box 盒模型 在微信小程序中的应用示例 在微信小程序中&#xff0c;CSS 样式 box-sizing: border-box; 用于控制元素的盒模型如何计算宽度和高度。具体来说&#xff0c; box-sizing: border-box; 会改…...

预训练 BERT 使用 Hugging Face 和 PyTorch 在 AMD GPU 上

Pre-training BERT using Hugging Face & PyTorch on an AMD GPU — ROCm Blogs 2024年1月26日&#xff0c;作者&#xff1a;Vara Lakshmi Bayanagari. 这篇博客解释了如何从头开始使用 Hugging Face 库和 PyTorch 后端在 AMD GPU 上为英文语料(WikiText-103-raw-v1)预训练…...

鸿蒙是必经之路

少了大嘴的发布会&#xff0c;老实讲有点让人昏昏入睡。关于技术本身的东西&#xff0c;放在后面。 我想想来加把油~ 鸿蒙发布后褒贬不一&#xff0c;其中很多人不太看好鸿蒙&#xff0c;一方面是开源性、一方面是南向北向的利益问题。 不说技术的领先点&#xff0c;我只扯扯…...

Java项目实战II基于微信小程序的马拉松报名系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 马拉松运动…...

家用wifi的ip地址固定吗?换wifi就是换ip地址吗

在探讨家用WiFi的IP地址是否固定&#xff0c;以及换WiFi是否就意味着换IP地址这两个问题时&#xff0c;我们首先需要明确几个关键概念&#xff1a;IP地址、家用WiFi网络、以及它们之间的相互作用。 一、家用WiFi的IP地址固定性 家用WiFi环境中的IP地址通常涉及两类&#xff1a…...

codeforces _ 补题

C. Ball in Berland 传送门&#xff1a;Problem - C - Codeforces 题意&#xff1a; 思路&#xff1a;容斥原理 考虑 第 i 对情侣组合 &#xff0c;男生为 a &#xff0c;女生为 b &#xff0c;那么考虑与之匹配的情侣 必须没有 a | b &#xff0c;一共有 k 对情侣&#x…...

DataSophon集成ApacheImpala的过程

注意: 本次安装操作系统环境为Anolis8.9(Centos7和Centos8应该也一样) DataSophon版本为DDP-1.2.1 整合的安装包我放网盘了: 通过网盘分享的文件&#xff1a;impala-4.4.1.tar.gz等2个文件 链接: https://pan.baidu.com/s/18KfkO_BEFa5gVcc16I-Yew?pwdza4k 提取码: za4k 1…...

深入探讨TCP/IP协议基础

在当今数字化的时代&#xff0c;计算机网络已经成为人们生活和工作中不可或缺的一部分。而 TCP/IP 协议作为计算机网络的核心协议&#xff0c;更是支撑着全球互联网的运行。本文将深入探讨常见的 TCP/IP 协议基础&#xff0c;带你了解计算机网络的奥秘。 一、计算机网络概述 计…...

《Windows PE》7.4 资源表应用

本节我们将通过两个示例程序&#xff0c;演示对PE文件内图标资源的置换与提取。 本节必须掌握的知识点&#xff1a; 更改图标 提取图标资源 7.4.1 更改图标 让我们来做一个实验&#xff0c;替换PE文件中现有的图标。如果手工替换&#xff0c;一定是先找到资源表&#xff0c;…...

【重生之我要苦学C语言】猜数字游戏和关机程序的整合

今天来把学过的猜数字游戏和关机程序来整合一下 如果有不明白的可以看往期的博客 废话不多说&#xff0c;上代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <time.h> #include <stdlib.h> #include <string.h> void…...

基于centos7脚本一键部署gpmall商城

基于centos7脚本一键部署单节点gpmall商城&#xff0c;该商城可单节点&#xff0c;可集群&#xff0c;可高可用集群部署&#xff0c;VMware17&#xff0c;虚拟机IP&#xff1a;192.168.200.100 将软件包解压到/root目录 [rootlocalhost ~]# ls dist …...

Mac book英特尔系列?M系列?两者有什么区别呢

众所周知&#xff0c;Mac book有M系列&#xff0c;搭载的是苹果自研的M芯片&#xff0c;也有着英特尔系列&#xff0c;搭载的是英特尔的处理器&#xff0c;虽然从 2020 年开始&#xff0c;苹果公司逐步推出了自家研发的 M 系列芯片&#xff0c;并逐渐将 MacBook 产品线过渡到 M…...

Python unstructured库详解:partition_pdf函数完整参数深度解析

Python unstructured库详解&#xff1a;partition_pdf函数完整参数深度解析 1. 简介2. 基础文件处理参数2.1 文件输入参数2.2 页面处理参数 3. 文档解析策略3.1 strategy参数详解3.2 策略选择建议 4. 表格处理参数4.1 表格结构推断 5. 语言处理参数5.1 语言设置 6. 图像处理参数…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要&#xff1a;在消费市场竞争日益激烈的当下&#xff0c;传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序&#xff0c;探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式&#xff0c;分析沉浸式体验的优势与价值…...

C++11 constexpr和字面类型:从入门到精通

文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...

Excel 怎么让透视表以正常Excel表格形式显示

目录 1、创建数据透视表 2、设计 》报表布局 》以表格形式显示 3、设计 》分类汇总 》不显示分类汇总 1、创建数据透视表 2、设计 》报表布局 》以表格形式显示 3、设计 》分类汇总 》不显示分类汇总...

【向量库】Weaviate概述与架构解析

文章目录 一、什么是weaviate二、High-Level Architecture1. Core Components2. Storage Layer3. 组件交互流程 三、核心组件1. API Layer2. Schema Management3. Vector Indexing3.1. 查询原理3.2. 左侧&#xff1a;Search Process&#xff08;搜索流程&#xff09;3.3. 右侧&…...