当前位置: 首页 > 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. 图像处理参数…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...