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

音视频入门基础:MPEG2-PS专题(5)——FFmpeg源码中,解析PS流中的PES流的实现

一、引言

从《音视频入门基础:MPEG2-PS专题(3)——MPEG2-PS格式简介》中可以知道,PS流由一个个pack(包装)组成。一个pack = 一个pack_header + 一个或多个PES_packet。pack_header中还可能存在system header。

但是pack_header和system header中并没有什么重要信息,所以FFmpeg源码在解析PS流时会跳过pack_header和system header,直接解析PES packet。

FFmpeg源码中通过mpegps_read_pes_header函数解析PS流中的PES packet。

二、mpegps_read_pes_header函数的定义

mpegps_read_pes_header函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/mpeg.c中:

/* read the next PES header. Return its position in ppos* (if not NULL), and its start code, pts and dts.*/
static int mpegps_read_pes_header(AVFormatContext *s,int64_t *ppos, int *pstart_code,int64_t *ppts, int64_t *pdts)
{MpegDemuxContext *m = s->priv_data;int len, size, startcode, c, flags, header_len;int pes_ext, ext2_len, id_ext, skip;int64_t pts, dts;int64_t last_sync = avio_tell(s->pb);error_redo:avio_seek(s->pb, last_sync, SEEK_SET);
redo:/* next start code (should be immediately after) */m->header_state = 0xff;size      = MAX_SYNC_SIZE;startcode = find_next_start_code(s->pb, &size, &m->header_state);last_sync = avio_tell(s->pb);if (startcode < 0) {if (avio_feof(s->pb))return AVERROR_EOF;// FIXME we should remember header_statereturn FFERROR_REDO;}if (startcode == PACK_START_CODE)goto redo;if (startcode == SYSTEM_HEADER_START_CODE)goto redo;if (startcode == PADDING_STREAM) {avio_skip(s->pb, avio_rb16(s->pb));goto redo;}if (startcode == PRIVATE_STREAM_2) {if (!m->sofdec) {/* Need to detect whether this from a DVD or a 'Sofdec' stream */int len = avio_rb16(s->pb);int bytesread = 0;uint8_t *ps2buf = av_malloc(len);if (ps2buf) {bytesread = avio_read(s->pb, ps2buf, len);if (bytesread != len) {avio_skip(s->pb, len - bytesread);} else {uint8_t *p = 0;if (len >= 6)p = memchr(ps2buf, 'S', len - 5);if (p)m->sofdec = !memcmp(p+1, "ofdec", 5);m->sofdec -= !m->sofdec;if (m->sofdec < 0) {if (len == 980  && ps2buf[0] == 0) {/* PCI structure? */uint32_t startpts = AV_RB32(ps2buf + 0x0d);uint32_t endpts = AV_RB32(ps2buf + 0x11);uint8_t hours = ((ps2buf[0x19] >> 4) * 10) + (ps2buf[0x19] & 0x0f);uint8_t mins  = ((ps2buf[0x1a] >> 4) * 10) + (ps2buf[0x1a] & 0x0f);uint8_t secs  = ((ps2buf[0x1b] >> 4) * 10) + (ps2buf[0x1b] & 0x0f);m->dvd = (hours <= 23 &&mins  <= 59 &&secs  <= 59 &&(ps2buf[0x19] & 0x0f) < 10 &&(ps2buf[0x1a] & 0x0f) < 10 &&(ps2buf[0x1b] & 0x0f) < 10 &&endpts >= startpts);} else if (len == 1018 && ps2buf[0] == 1) {/* DSI structure? */uint8_t hours = ((ps2buf[0x1d] >> 4) * 10) + (ps2buf[0x1d] & 0x0f);uint8_t mins  = ((ps2buf[0x1e] >> 4) * 10) + (ps2buf[0x1e] & 0x0f);uint8_t secs  = ((ps2buf[0x1f] >> 4) * 10) + (ps2buf[0x1f] & 0x0f);m->dvd = (hours <= 23 &&mins  <= 59 &&secs  <= 59 &&(ps2buf[0x1d] & 0x0f) < 10 &&(ps2buf[0x1e] & 0x0f) < 10 &&(ps2buf[0x1f] & 0x0f) < 10);}}}av_free(ps2buf);/* If this isn't a DVD packet or no memory* could be allocated, just ignore it.* If we did, move back to the start of the* packet (plus 'length' field) */if (!m->dvd || avio_skip(s->pb, -(len + 2)) < 0) {/* Skip back failed.* This packet will be lost but that can't be helped* if we can't skip back*/goto redo;}} else {/* No memory */avio_skip(s->pb, len);goto redo;}} else if (!m->dvd) {int len = avio_rb16(s->pb);avio_skip(s->pb, len);goto redo;}}if (startcode == PROGRAM_STREAM_MAP) {mpegps_psm_parse(m, s->pb);goto redo;}/* find matching stream */if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||(startcode >= 0x1e0 && startcode <= 0x1ef) ||(startcode == 0x1bd) ||(startcode == PRIVATE_STREAM_2) ||(startcode == 0x1fd)))goto redo;if (ppos) {*ppos = avio_tell(s->pb) - 4;}len = avio_rb16(s->pb);pts =dts = AV_NOPTS_VALUE;if (startcode != PRIVATE_STREAM_2){/* stuffing */for (;;) {if (len < 1)goto error_redo;c = avio_r8(s->pb);len--;/* XXX: for MPEG-1, should test only bit 7 */if (c != 0xff)break;}if ((c & 0xc0) == 0x40) {/* buffer scale & size */avio_r8(s->pb);c    = avio_r8(s->pb);len -= 2;}if ((c & 0xe0) == 0x20) {dts  =pts  = get_pts(s->pb, c);len -= 4;if (c & 0x10) {dts  = get_pts(s->pb, -1);len -= 5;}} else if ((c & 0xc0) == 0x80) {/* mpeg 2 PES */flags      = avio_r8(s->pb);header_len = avio_r8(s->pb);len       -= 2;if (header_len > len)goto error_redo;len -= header_len;if (flags & 0x80) {dts         = pts = get_pts(s->pb, -1);header_len -= 5;if (flags & 0x40) {dts         = get_pts(s->pb, -1);header_len -= 5;}}if (flags & 0x3f && header_len == 0) {flags &= 0xC0;av_log(s, AV_LOG_WARNING, "Further flags set but no bytes left\n");}if (flags & 0x01) { /* PES extension */pes_ext = avio_r8(s->pb);header_len--;/* Skip PES private data, program packet sequence counter* and P-STD buffer */skip  = (pes_ext >> 4) & 0xb;skip += skip & 0x9;if (pes_ext & 0x40 || skip > header_len) {av_log(s, AV_LOG_WARNING, "pes_ext %X is invalid\n", pes_ext);pes_ext = skip = 0;}avio_skip(s->pb, skip);header_len -= skip;if (pes_ext & 0x01) { /* PES extension 2 */ext2_len = avio_r8(s->pb);header_len--;if ((ext2_len & 0x7f) > 0) {id_ext = avio_r8(s->pb);if ((id_ext & 0x80) == 0)startcode = ((startcode & 0xff) << 8) | id_ext;header_len--;}}}if (header_len < 0)goto error_redo;avio_skip(s->pb, header_len);} else if (c != 0xf)goto redo;}if (startcode == PRIVATE_STREAM_1) {int ret = ffio_ensure_seekback(s->pb, 2);if (ret < 0)return ret;startcode = avio_r8(s->pb);m->raw_ac3 = 0;if (startcode == 0x0b) {if (avio_r8(s->pb) == 0x77) {startcode = 0x80;m->raw_ac3 = 1;avio_skip(s->pb, -2);} else {avio_skip(s->pb, -1);}} else {len--;}}if (len < 0)goto error_redo;if (dts != AV_NOPTS_VALUE && ppos) {int i;for (i = 0; i < s->nb_streams; i++) {if (startcode == s->streams[i]->id &&(s->pb->seekable & AVIO_SEEKABLE_NORMAL) /* index useless on streams anyway */) {ff_reduce_index(s, i);av_add_index_entry(s->streams[i], *ppos, dts, 0, 0,AVINDEX_KEYFRAME /* FIXME keyframe? */);}}}*pstart_code = startcode;*ppts        = pts;*pdts        = dts;return len;
}

该函数的作用就是:解析PS流中的一个PES packet,将其PES packet header里面的信息解析出来。

形参s:既是输入型参数也是输出型参数,指向一个AVFormatContext类型的变量。s->pb包含需要被解析的PS流的二进制数据。mpegps_read_pes_header函数解析的是s->pb->buf_ptr指向的PS流数据中的下一个PES packet。

形参ppos:输出型参数,*ppos为读取到的PES packet相对于文件首的偏移字节数。

形参pstart_code:输出型参数。如果读取到了PES packet,且该PES packet里面包含的不是的private_stream_1,*pstart_code为其PES packet header中的stream_id属性的值加0x100。

形参ppts:输出型参数。*ppts为从该PES packet的PES packet header中读取到的pts。

形参pdts:输出型参数。*pdts为从该PES packet的PES packet header中读取到的dts。

返回值:解析成功,返回该PES packet去掉PES packet header后的大小(即基本码流ES数据的大小)。解析失败,返回一个负数。

三、mpegps_read_pes_header函数的内部实现分析

mpegps_read_pes_header函数中,首先通过find_next_start_code函数找到s->pb->buf_ptr指向的PS流数据中的下一个pack header的起始码 或 下一个system header的起始码 或 下一个PES packet header的起始码:

redo:/* next start code (should be immediately after) */m->header_state = 0xff;size      = MAX_SYNC_SIZE;startcode = find_next_start_code(s->pb, &size, &m->header_state);last_sync = avio_tell(s->pb);if (startcode < 0) {if (avio_feof(s->pb))return AVERROR_EOF;// FIXME we should remember header_statereturn FFERROR_REDO;}

如果找到的是pack header的起始码 或 system header的起始码 或 找到的PES packet中包含padding_stream数据,通过goto语句跳转,然后重新通过find_next_start_code函数查找下一个起始码:

    if (startcode == PACK_START_CODE)goto redo;if (startcode == SYSTEM_HEADER_START_CODE)goto redo;if (startcode == PADDING_STREAM) {avio_skip(s->pb, avio_rb16(s->pb));goto redo;}

如果找到了符合要求的PES packet header的起始码,通过avio_tell函数读取该PES packet相对于文件首的偏移字节数,赋值给*ppos。关于avio_tell函数的用法可以参考:《FFmpeg源码:avio_tell函数分析》:

    /* find matching stream */if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||(startcode >= 0x1e0 && startcode <= 0x1ef) ||(startcode == 0x1bd) ||(startcode == PRIVATE_STREAM_2) ||(startcode == 0x1fd)))goto redo;if (ppos) {*ppos = avio_tell(s->pb) - 4;}

读取PES packet header中的PES_packet_length属性,赋值给变量len。关于avio_rb16函数的用法可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》:

    len = avio_rb16(s->pb);

读取PES packet header中的PES_scrambling_control、PES_priority、data_alignment_indicator、copyright、original_or_copy属性,赋值给变量c:

        c = avio_r8(s->pb);

如果上述读取到的PES_packet_length属性后面的值为"10((c & 0xc0) == 0x80为真),表示读取到的PES packet header的格式正确:

 执行大括号内的内容:

else if ((c & 0xc0) == 0x80) {
//...
}

读取PTS_DTS_flags、ESCR_flag、ES_rate_flag、DSM_trick_mode_flag、additional_copy_info_flag、PES_CRC_flag、PES_extension_flag这7个属性,赋值给变量flags:

        flags      = avio_r8(s->pb);

读取PES_header_data_length属性,赋值给变量header_len:

        header_len = avio_r8(s->pb);

如果PTS_DTS_flags属性的值为'10',表示PES packet header中会存在PTS,读取PTS;值为'11'时,表示PES packet header中会同时存在PTS和DTS,读取PTS和DTS,分别赋值给变量dts和pts:

        if (flags & 0x80) {dts         = pts = get_pts(s->pb, -1);header_len -= 5;if (flags & 0x40) {dts         = get_pts(s->pb, -1);header_len -= 5;}}

如果PES_extension_flag属性的值为1,表示PES packet header有PES_extension域,读取PES_extension域:

        if (flags & 0x01) { /* PES extension */pes_ext = avio_r8(s->pb);header_len--;/* Skip PES private data, program packet sequence counter* and P-STD buffer */skip  = (pes_ext >> 4) & 0xb;skip += skip & 0x9;if (pes_ext & 0x40 || skip > header_len) {av_log(s, AV_LOG_WARNING, "pes_ext %X is invalid\n", pes_ext);pes_ext = skip = 0;}avio_skip(s->pb, skip);header_len -= skip;if (pes_ext & 0x01) { /* PES extension 2 */ext2_len = avio_r8(s->pb);header_len--;if ((ext2_len & 0x7f) > 0) {id_ext = avio_r8(s->pb);if ((id_ext & 0x80) == 0)startcode = ((startcode & 0xff) << 8) | id_ext;header_len--;}}}

最后返回从该PES packet的PES packet header中读取到的pts、dts、该PES packet去掉PES packet header后的大小等信息:

    *pstart_code = startcode;*ppts        = pts;*pdts        = dts;return len;

四、FFmpeg源码中,解析TS流中的PES流的实现

FFmpeg源码中,解析TS流中的PES流所使用的函数不一样,是通过mpegts_push_data函数进行解析的,具体可以参考:《音视频入门基础:MPEG2-TS专题(19)——FFmpeg源码中,解析TS流中的PES流的实现》。

相关文章:

音视频入门基础:MPEG2-PS专题(5)——FFmpeg源码中,解析PS流中的PES流的实现

一、引言 从《音视频入门基础&#xff1a;MPEG2-PS专题&#xff08;3&#xff09;——MPEG2-PS格式简介》中可以知道&#xff0c;PS流由一个个pack&#xff08;包装&#xff09;组成。一个pack 一个pack_header 一个或多个PES_packet。pack_header中还可能存在system header…...

【问题记录】npm create vue@latest报错

1&#xff0c;错误日志 npm error code EPERM npm error syscall mkdir npm error path D:\Program Files\nodejs\node_cache\_cacache npm error errno EPERM npm error FetchError: Invalid response body while trying to fetch https://registry.npmjs.org/create-vue: EP…...

OpenGL材质系统和贴图纹理

上一篇文章当中笔者为大家介绍了风氏光照模型&#xff0c;相信大家也发现了光照着色器当中有设置有很多控制光照强度的参数&#xff0c;而所谓的材质系统就是我们可以人为的去调节这些参数&#xff0c;让一个物体的反光效果能够更加接近我们现实生活当中的一些物体。 材质系统…...

Markdown中类图的用法

Markdown中类图的用法 前言语法详解基本流程图几何图形节点默认的节点分离节点的ID与内容节点形状圆角形节点的语法圆形节点的语法。非对称节点语法菱形节点的语法。六角形节点的语法。平行四边形节点的语法。梯形节点的语法。 连接线基本的连接线语法。无向线段连接线。点状连…...

钓鱼攻击(Phishing)详解和实现 (网络安全)

钓鱼攻击&#xff08;Phishing&#xff09;详解和实现 钓鱼攻击是一种社会工程学攻击&#xff0c;攻击者通过伪装成可信任的实体诱使受害者泄露敏感信息&#xff0c;如用户名、密码、信用卡号等。以下详细介绍钓鱼攻击的原理、种类、实现方式&#xff0c;以及防御措施。 一、钓…...

window11 wsl mysql8 错误分析:1698 - Access denied for user ‘root‘@‘kong.mshome.net‘

&#x1f6a8; 错误分析&#xff1a;1698 - Access denied for user rootkong.mshome.net 这个错误是因为 MySQL 的 root 用户 使用 auth_socket 插件进行身份验证&#xff0c;而不是使用密码。因此&#xff0c;当你尝试从 远程主机 连接时&#xff0c;MySQL 会拒绝访问。 ✅ …...

C++线程同步之条件变量

C线程同步之条件变量 文章目录 C线程同步之条件变量什么是条件变量&#xff08;Condition Variable&#xff09;&#xff1f;条件变量的主要用途常见的应用场景C11中的条件变量condition_variable的使用方法std::condition_variable的使用步骤典型的使用示例&#xff1a;生产者…...

如何实现多条件搜索

我们先来看多条件查询的样式是什么样的&#xff01; 给查询按钮添加点击事件&#xff0c;然后获取到对应输入框中的值 然后通过filter过滤&#xff0c;对获取到的数据进行筛选 &#xff0c;然后调用渲染函数将过滤搜索到的数据在页面中显示出来。 这就是进行多条件搜索出来的效…...

深入MySQL复杂查询优化技巧

在上一篇文章中&#xff0c;我们介绍了 MySQL 的关联关系理论与基础实践。本篇文章将进一步探讨 MySQL 复杂查询的优化技巧&#xff0c;帮助开发者应对大型数据集和高并发场景中的性能挑战。我们将涵盖索引设计、查询计划分析、分区技术以及事务管理的优化。 一、索引优化 索引…...

Fabric环境部署-Git和Node安装

一.安装Git&#xff08;v2.43.0&#xff09; Git 是一个开源的分布式版本管理系统&#xff08;也是全球最大的开源软件存储服务器&#xff09;&#xff0c;用于敏捷高效地处理任何或小或大的项目。搭建区块链需要使用Git&#xff0c;因为区块链的开发和部署需要使用版本控制工…...

如何弥补开源大语言模型解决推理任务的不足

在实际应用中&#xff0c;大语言模型&#xff08;LLM&#xff09;可以通过与其他专门的推理技术结合&#xff0c;克服其在严格逻辑推理、深度推理或因果推理领域的不足。以下是几种有效的结合方式&#xff0c;分别从不同角度解决LLM在推理中的局限性。 一、结合符号推理系统 …...

Ubuntu 下载安装 Consul1.17.1

下载 wget https://releases.hashicorp.com/consul/1.17.1/consul_1.17.1_linux_amd64.zip解压&#xff1a; unzip -d consul_1.17.1_linux_amd64.zip /opt/module将解压出的二进制文件移动到 /usr/local/bin 目录中以便在系统中全局使用&#xff1a; sudo mv consul /usr/l…...

【数据库系统概论】并发控制--复习

1. 并发控制概述 并发控制是数据库系统处理多个事务同时执行时&#xff0c;保证数据一致性和事务隔离性的关键技术。 1.1并发操作的特点 数据库系统允许多个用户并发访问。典型应用场景&#xff1a; 飞机订票系统银行数据库系统网上购物系统 1.2并发操作可能带来的问题 并…...

MySQL(六)MySQL 案例

1. MySQL 案例 1.1. 设计数据库 1、首先根据相关业务需求(主要参考输出输入条件)规划出表的基本结构   2、根据业务规则进行状态字段设计   3、预估相关表的数据量进行容量规划   4、确定主键   5、根据对相关处理语句的分析对数据结构进行相应的变更。   设计表的时…...

DDcGAN_多分辨率图像融合的双鉴别条件生成对抗网络_y译文马佳义

摘要&#xff1a; 在本文中&#xff0c;我们提出了一种新的端到端模型&#xff0c;称为双鉴别条件生成对抗网络&#xff08;DDcGAN&#xff09;&#xff0c;用于融合不同分辨率的红外和可见光图像。我们的方法建立了一个生成器和两个鉴别器之间的对抗博弈。生成器的目的是基于特…...

[读书日志]从零开始学习Chisel 第一篇:书籍介绍,Scala与Chisel概述,Scala安装运行(敏捷硬件开发语言Chisel与数字系统设计)

简介&#xff1a;从20世纪90年代开始&#xff0c;利用硬件描述语言和综合技术设计实现复杂数字系统的方法已经在集成电路设计领域得到普及。随着集成电路集成度的不断提高&#xff0c;传统硬件描述语言和设计方法的开发效率低下的问题越来越明显。近年来逐渐崭露头角的敏捷化设…...

二、用例图

二、用例图 (一&#xff09;、用例图的基本概念 1、用例图的定义&#xff1a; 用例图是表示一个系统中用例与参与者关系之间的图。它描述了系统中相关的用户和系统对不同用户提供的功能和服务。 用例图相当于从用户的视角来描述和建模整个系统&#xff0c;分析系统的功能与…...

LWIP之一:使用STM32CubeMX搭建基于FreeRTOS的LWIP工程并分析协议栈初始化过程

工程搭建及LWIP协议栈初始化过程 一、使用STM32CubeMX快速生成工程二、修改测试三、LWIP协议栈初始化过程分析3.1 tcpip_init()3.1.1 lwip_init()3.1.1.1 sys_init()3.1.1.2 mem_init()3.1.1.3 memp_init()3.1.1.4 netif_init()3.1.1.5 udp_init()3.1.1.6 tcp_init()3.1.1.7 ig…...

个性化电影推荐系统|Java|SSM|JSP|

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html 5⃣️数据库可…...

UE5AI感知组件

官方解释&#xff1a; AI感知系统为Pawn提供了一种从环境中接收数据的方式&#xff0c;例如噪音的来源、AI是否遭到破坏、或AI是否看到了什么。 AI感知组件&#xff08;AIPerception Component&#xff09;是用于实现游戏中的非玩家角色&#xff08;NPC&#xff09;对环境和其…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...

2.2.2 ASPICE的需求分析

ASPICE的需求分析是汽车软件开发过程中至关重要的一环&#xff0c;它涉及到对需求进行详细分析、验证和确认&#xff0c;以确保软件产品能够满足客户和用户的需求。在ASPICE中&#xff0c;需求分析的关键步骤包括&#xff1a; 需求细化&#xff1a;将从需求收集阶段获得的高层需…...

基于小程序老人监护管理系统源码数据库文档

摘 要 近年来&#xff0c;随着我国人口老龄化问题日益严重&#xff0c;独居和居住养老机构的的老年人数量越来越多。而随着老年人数量的逐步增长&#xff0c;随之而来的是日益突出的老年人问题&#xff0c;尤其是老年人的健康问题&#xff0c;尤其是老年人产生健康问题后&…...

【2D与3D SLAM中的扫描匹配算法全面解析】

引言 扫描匹配(Scan Matching)是同步定位与地图构建(SLAM)系统中的核心组件&#xff0c;它通过对齐连续的传感器观测数据来估计机器人的运动。本文将深入探讨2D和3D SLAM中的各种扫描匹配算法&#xff0c;包括数学原理、实现细节以及实际应用中的性能对比&#xff0c;特别关注…...