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

【FFmpeg之如何新增一个硬件解码器】

FFmpeg之如何新增一个硬件解码器

  • 前言
  • 一、config配置
  • 二、解码器定义
    • 1.目录结构
    • 2.数据结构
  • 三、解码流程
    • 1、初始化mediacodec_decode_init
    • 2、帧接收mediacodec_receive_frame
      • 2.1 解码上下文MediaCodecH264DecContext
      • 2.2 发包AVPacket到解码器 -- ff_mediacodec_dec_send
      • 2.3 接收解码后数据AVFrame -- ff_mediacodec_dec_receive
    • 3、刷新缓冲区mediacodec_decode_flush
    • 4、关闭解码器mediacodec_decode_close
  • 四、回顾与总结

前言

  最近在鸿蒙上开发音视频相关功能,在适配好SDL2之后,接入FFmpeg软解即可播放音视频,然对于4k大码率的视频,播放时却非常卡顿。于是乎琢磨着在鸿蒙上加一个FFmpeg的硬解码,PS:鸿蒙官方目前只提供解码相关 SDK。不过应该如何入手呢?想到这个鸿蒙跟安卓还是有点类似,解码都是异步回调机制,于是先捋一遍安卓下硬解码流程吧。

一、config配置

  首先MediaCodec是Android平台提供的底层音视频编解码API,支持解码的格式有

  •   视频:H.264(AVC)、H.265(HEVC)、VP8、VP9、MPEG-4、AV1
  •   音频:AAC、MP3、Opus
    下面以H.264格式为例,在configure文件中有以下两行:
h264_mediacodec_decoder_deps="mediacodec"
h264_mediacodec_decoder_select="h264_mp4toannexb_bsf h264_parser"

  第一行"mediacodec"表示该解码器依赖 Android 的 MediaCodec API,在配置阶段,configure 脚本会通过配置的NDK路径,检测系统中是否存在这些依赖项;
  第二行声明该解码器的关联组件,当 h264_mediacodec_decoder 被启用时,configure 会自动启用以下组件:

  •   h264_mp4toannexb_bsf:将 H.264 码流从 MP4 封装格式(AVCC)转换为 Annex B 格式。【这个后面会用到,稍后再讲~】
  •   h264_parser:H.264 码流解析器,用于解析码流中的 NALU 单元。.

  在命令行指定一下相关参数即可开启硬件解码:

./configure \--target-os=android \--arch=arm64 \--enable-cross-compile \--sysroot=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot \--enable-mediacodec \--enable-decoder=h264_mediacodec

  生成的MakeFile文件中有一行

OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o

二、解码器定义

1.目录结构

  在libavcodec目录下,mediacodec相关的文件有12个,如下:
在这里插入图片描述

2.数据结构

  首先我们可以看下mediacodecdec.c中DECLARE_MEDIACODEC_VDEC相关的宏定义,

#define DECLARE_MEDIACODEC_VCLASS(short_name)                   \
static const AVClass ff_##short_name##_mediacodec_dec_class = { \.class_name = #short_name "_mediacodec",      /* 注册到 FFmpeg 的类名 */ \.item_name  = av_default_item_name,           /* 默认的对象名称生成器 */ \.option     = ff_mediacodec_vdec_options,     /* 解码器配置选项指针 */ \.version    = LIBAVUTIL_VERSION_INT,          /* 版本号对齐校验 */ \
};#define DECLARE_MEDIACODEC_VDEC(short_name, full_name, codec_id, bsf)         \
DECLARE_MEDIACODEC_VCLASS(short_name)  /* 先声明 AVClass */                     \
const FFCodec ff_ ## short_name ## _mediacodec_decoder = {                    \.p.name         = #short_name "_mediacodec",          /* 解码器名称 */       \CODEC_LONG_NAME(full_name " Android MediaCodec decoder"), /* 长描述 */     \.p.type         = AVMEDIA_TYPE_VIDEO,                /* 媒体类型:视频 */   \.p.id           = codec_id,                          /* FFmpeg 编解码ID */  \.p.priv_class   = &ff_##short_name##_mediacodec_dec_class, /* 私有类指针 */ \.priv_data_size = sizeof(MediaCodecH264DecContext),   /* 私有数据区大小 */    \.init           = mediacodec_decode_init,            /* 初始化回调函数 */    \FF_CODEC_RECEIVE_FRAME_CB(mediacodec_receive_frame), /* 帧接收回调 */      \.flush          = mediacodec_decode_flush,           /* 冲刷缓冲区回调 */    \.close          = mediacodec_decode_close,           /* 关闭解码器回调 */    \.p.capabilities = AV_CODEC_CAP_DELAY |               /* 支持延迟输出 */      \AV_CODEC_CAP_AVOID_PROBING |       /* 避免格式探测 */      \AV_CODEC_CAP_HARDWARE,             /* 硬件加速标志 */      \.caps_internal  = FF_CODEC_CAP_NOT_INIT_THREADSAFE, /* 非线程安全初始化 */   \.bsfs           = bsf,                              /* 关联的比特流过滤器 */ \.hw_configs     = mediacodec_hw_configs,            /* 硬件配置信息表 */      \.p.wrapper_name = "mediacodec",                     /* 封装器名称 */        \
};#if CONFIG_H264_MEDIACODEC_DECODER
/* 实例化 H.264 解码器结构体 */
DECLARE_MEDIACODEC_VDEC(h264,       /* 短名称 */"H.264",     /* 标准名称 */AV_CODEC_ID_H264, /* FFmpeg 编码ID */"h264_mp4toannexb") /* MP4 到 Annex-B 格式转换器 */
#endif

  其中先声明一个AVClass 结构体,这个是FFmpeg 的类系统核心,用于统一管理编解码器的元数据,注册FFCodec解码器;
  然后就是在解码器中注册四个回调函数:mediacodec_decode_init(初始化)、mediacodec_receive_frame(帧接收)、mediacodec_decode_flush(刷新缓冲区)、mediacodec_decode_close(关闭解码器)
  最后可以看到capabilities属性中的AV_CODEC_CAP_HARDWARE标志,这个就是开启硬件加速,比较关键,如果没有这个标志那么就是用的软解了。

三、解码流程

1、初始化mediacodec_decode_init

  mediacodec_decode_init里面主要做了两件事:
  一、设置FFAMediaFormat媒体格式的一些属性,如MIME类型、context的宽和高等:

	// h264对应的MIME为"video/avc"ff_AMediaFormat_setString(format, "mime", codec_mime);ff_AMediaFormat_setInt32(format, "width", avctx->width);ff_AMediaFormat_setInt32(format, "height", avctx->height);

  最后通过调用NDK里面的相关函数设置(没有NDK的话就通过jni去调java的? 没细究 0.o)
  二、ff_mediacodec_dec_init,其中首先通过

s->codec_name = ff_AMediaCodecList_getCodecNameByType(mime, profile, 0, avctx);
s->codec = ff_AMediaCodec_createCodecByName(s->codec_name, s->use_ndk_codec);

  获取解码器名称并创建解码器,然后再配置解码器并启动

status = ff_AMediaCodec_configure(s->codec, format, s->surface, NULL, 0);
status = ff_AMediaCodec_start(s->codec);

  上述函数均有NDK和JNI两套调用逻辑。

2、帧接收mediacodec_receive_frame

  mediacodec_receive_frame是从解码器中获取解码后的视频帧,里面的核心流程是异步处理输入和输出缓冲区(通过队列管理)。当ff_mediacodec_dec_send被调用时,AVPacket数据会被放入输入队列,等待解码器处理。解码后的数据则从输出队列中取出,即ff_mediacodec_dec_receive函数负责从输出队列获取解码后的帧AVFrame。

2.1 解码上下文MediaCodecH264DecContext

  不过在这之前首先来看下解码器上下文MediaCodecH264DecContext这个关键类的数据结构设计:

// H264 MediaCodec解码器上下文:
typedef struct MediaCodecH264DecContext {//AVClass 集成到FFmpeg的类系统中,用于日志记录、私有选项配置及参数解析AVClass *avclass;//MediaCodecDecContext指向通用的MediaCodec解码器上下文,h264只是其中的一个特化MediaCodecDecContext *ctx;//当输入数据包过大,无法一次性写入硬件缓冲区时,剩余数据暂存于此,等待后续处理AVPacket buffered_pkt;//延迟刷新解码器的标志,确保所有已提交数据被处理后再执行刷新int delay_flush;int amlogic_mpeg2_api23_workaround; // ?// NDK API更高效,减少Java层交互开销,适用于高性能需求场景int use_ndk_codec;
} MediaCodecH264DecContext;//通用的MediaCodec解码器上下文,管理硬件解码器状态:
typedef struct MediaCodecDecContext {AVCodecContext *avctx; //FFmpeg编解码上下文,用于日志和配置信息atomic_int refcount;atomic_int hw_buffer_count;char *codec_name;FFAMediaCodec *codec;FFAMediaFormat *format;void *surface; // FFANativeWindow * 渲染表面(用于零拷贝)int started;int draining;int flushing;int eos;int width;int height;int stride;int slice_height;int color_format;int crop_top;int crop_bottom;int crop_left;int crop_right;int display_width;int display_height;uint64_t output_buffer_count;ssize_t current_input_buffer;bool delay_flush;atomic_int serial;bool use_ndk_codec;
} MediaCodecDecContext;

  在提交缓冲区给解码器解码的过程中,一般来说可以通过Surface(上面的void *surface就是指向渲染表面)或DMA 缓冲区共享技术实现零拷贝,减少 CPU 与 GPU 间的数据传输避免解码过慢。

2.2 发包AVPacket到解码器 – ff_mediacodec_dec_send

// 尝试获取输入缓冲区索引
index = ff_AMediaCodec_dequeueInputBuffer(codec, input_dequeue_timeout_us);
// 获取输入缓冲区的内存地址data和容量size
data = ff_AMediaCodec_getInputBuffer(codec, index, &size);
// 提交输入缓冲区到解码器
status = ff_AMediaCodec_queueInputBuffer(codec, index, 0, size, pts, 0);

2.3 接收解码后数据AVFrame – ff_mediacodec_dec_receive

//: 从解码器输出队列中获取缓冲区索引
index = ff_AMediaCodec_dequeueOutputBuffer(codec, &info, output_dequeue_timeout_us);
...
if (info.size) {// Surface 模式:通过 ANativeWindow 直接渲染到 Surface,无需拷贝数据。if (s->surface) {if ((ret = mediacodec_wrap_hw_buffer(avctx, s, index, &info, frame)) < 0) {av_log(avctx, AV_LOG_ERROR, "Failed to wrap MediaCodec buffer\n");return ret;}// ByteBuffer 模式:将 MediaCodec 的 ByteBuffer 数据复制到 AVFrame->data} else {data = ff_AMediaCodec_getOutputBuffer(codec, index, &size);if (!data) {av_log(avctx, AV_LOG_ERROR, "Failed to get output buffer\n");return AVERROR_EXTERNAL;}if ((ret = mediacodec_wrap_sw_buffer(avctx, s, data, size, index, &info, frame)) < 0) {av_log(avctx, AV_LOG_ERROR, "Failed to wrap MediaCodec buffer\n");return ret;}}s->output_buffer_count++;return 0;
} else {status = ff_AMediaCodec_releaseOutputBuffer(codec, index, 0);if (status < 0) {av_log(avctx, AV_LOG_ERROR, "Failed to release output buffer\n");}
}
...
if (ff_AMediaCodec_infoOutputBuffersChanged(codec, index)) {ff_AMediaCodec_cleanOutputBuffers(codec); // 清理旧缓冲区
}

3、刷新缓冲区mediacodec_decode_flush

  mediacodec_decode_flush在视频播放中,当用户跳转进度时,需要清空之前的解码数据,这时候就会调用flush函数。

static void mediacodec_decode_flush(AVCodecContext *avctx)
{MediaCodecH264DecContext *s = avctx->priv_data;//av_packet_unref是FFmpeg中释放AVPacket资源的函数,//这里是释放MediaCodecH264DecContext 中缓存的未完全提交到硬件解码器的 AVPacket 数据包av_packet_unref(&s->buffered_pkt);//清空解码器的输入/输出缓冲区若,解码器正在处理数据(Executing 状态),flush会强制停止当前操作ff_mediacodec_dec_flush(avctx, s->ctx);
}

一般来说一下四种场景会调用flush:
  a、视频播放器跳转进度:
    用户拖动进度条时,需清空当前解码队列,避免旧数据与新位置的数据混合。
  b、处理解码错误
    当解码器因数据错误进入异常状态时,通过刷新重置其状态,恢复解码能力。
  c、格式动态切换
    切换分辨率或码率时,需先清空原有数据,再重新配置解码器。
  d、结束流或重新初始化
    在流结束或重新初始化解码器前,确保资源正确释放。

4、关闭解码器mediacodec_decode_close

  通过引用计数管理声明周期,计数为0时依次删除MediaCodec、MediaFormat和Surface等对象,最后删除MediaCodecDecContext。

static void ff_mediacodec_dec_unref(MediaCodecDecContext *s)
{...// 原子操作:引用计数减1,若原值为1(减后为0),则释放资源if (atomic_fetch_sub(&s->refcount, 1) == 1) {ff_AMediaCodec_delete(s->codec); ff_AMediaFormat_delete(s->format);ff_mediacodec_surface_unref(s->surface, NULL);}
}

四、回顾与总结

  综上所述,FFmpeg添加一个硬件解码器的关键步骤如下:

步骤关键操作
1. 配置编译修改 configure 和 Makefile,添加新解码器选项
2. 定义结构体注册 AVCodec,实现编解码器上下文
3. 初始化与配置创建 MediaCodec 实例,设置格式参数
4. 数据传递实现 send_packet 和 receive_frame,适配硬件缓冲区
5. 资源管理处理刷新、关闭和引用计数,确保无内存泄漏

  整体来说,硬件解码核心流程并不复杂,主要是要对NDK中的接口调用以及处理,相比于软件解码来说主要是在数据在CPU和GPU之间传输的不同,虽然硬件解码后支持GPU直接渲染,无需数据回传,不过若需要CPU处理(如滤镜),还需将数据从显存拷贝到系统内存。因此现代播放器常结合两者,优先尝试硬件解码,失败时回退到软件解码。鸿蒙平台和安卓很类似,无非是NDK不同罢了,不过NDK里面怎么写的那就不得而知了。(-_->

相关文章:

【FFmpeg之如何新增一个硬件解码器】

FFmpeg之如何新增一个硬件解码器 前言一、config配置二、解码器定义1.目录结构2.数据结构 三、解码流程1、初始化mediacodec_decode_init2、帧接收mediacodec_receive_frame2.1 解码上下文MediaCodecH264DecContext2.2 发包AVPacket到解码器 -- ff_mediacodec_dec_send2.3 接收…...

P3385 【模板】负环

P3385 【模板】负环 - 洛谷 题目描述 给定一个 n 个点的有向图&#xff0c;请求出图中是否存在从顶点 1 出发能到达的负环。 负环的定义是&#xff1a;一条边权之和为负数的回路。 输入格式 本题单测试点有多组测试数据。 输入的第一行是一个整数 T&#xff0c;表示测试数…...

破解透明物体抓取难题,地瓜机器人CASIA 推出几何和语义融合的单目抓取方案|ICRA 2025

概述 近日&#xff0c;全球机器人领域顶会ICRA 2025&#xff08;IEEE机器人与自动化国际会议&#xff09;公布论文录用结果&#xff0c;地瓜机器人主导研发的DOSOD开放词汇目标检测算法与MODEST单目透明物体抓取算法成功入选。前者通过动态语义理解框架提升复杂场景识别准确率…...

深度学习编译器(整理某survey)

一、深度学习框架 TensorFlow PyTorch MXNet ONNX:定义了一个统一的表示&#xff0c;DL models的格式方便不同框架之间的转换模型 二、深度学习硬件 通用硬件&#xff08;CPU、GPU&#xff09;&#xff1a;通过硬件和软件优化支持深度学习工作负载 GPU:通过多核架构实现高…...

【计算机网络入门】应用层

目录 1.网络应用模型 1.1 C/S模型&#xff08;客户端服务器模型&#xff09; 1.2 P2P模型&#xff08;对等模型&#xff09; 2. DNS系统 2.1 域名 2.2 域名解析流程 3. FTP文件传输协议 4. 电子邮件系统 4.1 SMTP协议 4.2 pop3协议 4.3 IMAP协议 4.4 基于万维网的电…...

@PostConstruct注解的作用

PostConstruct 注解功能是在一个类的所有依赖被注入完成后&#xff0c;才会被执行的方法。这种方法通常用于类的初始化&#xff0c;初始化过程中可以进行一些资源加载、连接建立、或其他必要的配置工作。PostConstruct 方法仅被调用一次&#xff0c;通常修饰符是public或者prot…...

HTML + CSS 题目

1.说说你对盒子模型的理解? 一、是什么 对一个文档进行布局的时候&#xff0c;浏览器渲染引擎会根据标准之一的css基础盒模型&#xff0c;将所有元素表示为一个个矩形的盒子。 一个盒子由四个部分组成: content&#xff0c;padding&#xff0c;border&#xff0c;margin 下…...

通过多线程获取RV1126的AAC码流

目录 一RV1126多线程获取音频编码AAC码流的流程 1.1AI模块的初始化并使能 1.2AENC模块的初始化 ​​​​​​​1.3绑定AI模块和AENC模块 ​​​​​​​1.4多线程获取每一帧AAC码流 ​​​​​​​1.5每个AAC码流添加ADTSHeader头部 ​​​​​​​1.6写入具体每一帧AAC的…...

sql sqlserver的进程资源查看,杀掉多余进程

主要是由三个表组成 sys.sysprocesses、sys.dm_exec_sessions、sys.dm_exec_requests 后面两个在2008版本后使用&#xff0c;主要使用sys.dm_exec_sessions SELECT spid AS 会话ID, -- 进程&#xff08;会话&#xff09;的ID blocked AS 被阻塞的会话…...

自然语言处理:朴素贝叶斯

介绍 大家好&#xff0c;博主又来和大家分享自然语言处理领域的知识了。按照博主的分享规划&#xff0c;本次分享的核心主题本应是自然语言处理中的文本分类。然而&#xff0c;在对分享内容进行细致梳理时&#xff0c;我察觉到其中包含几个至关重要的知识点&#xff0c;即朴素…...

Pytorch实现之LSRGAN,轻量化SRGAN超分辨率SAR

简介 简介:在SRGAN的基础上设计了一个轻量化的SRGAN模型结构,通过DSConv+CA与残差结构的设计来减少参数量,同时利用SeLU激活函数构造。与多类SRGAN改进不同的是,很少使用BN层。 论文题目:Lightweight Super-Resolution Generative Adversarial Network for SAR Images(…...

学习记录-缺陷

目录 一、缺陷的判定标准 二、缺陷产生的原因 三、缺陷的生存周期 四、软件缺陷描述及提交要素 1.缺陷的核心内容 2.缺陷的提交要素 五、软件缺陷类型 一、缺陷的判定标准 二、缺陷产生的原因 三、缺陷的生存周期 注入bug > 发现bug > 清除bug 四、软件缺陷描述及提…...

文件压缩与解压工具7-Zip的安装和使用(免费)

一.介绍 7-Zip 是一款开源的文件压缩与解压缩工具&#xff0c;支持多种压缩格式&#xff0c;如 7z、ZIP、RAR、TAR 等。它具有高压缩比&#xff0c;尤其是其独有的 7z 格式&#xff0c;并集成了文件管理器。7-Zip 适用于 Windows 系统&#xff0c;提供命令行版本&#xff0c;…...

如何实现对用户密码的加密

摘要算法&#xff1a; 同样的明文&#xff0c;经过同样的摘要算法&#xff0c;得到的结果是一样的 验证方法&#xff1a;验证经过摘要算法处理后的结果&#xff0c;如果密文一样那么就认为明文是一样的 //数据库存储的一定是密文&#xff0c;用户输入的是明文&#xff1b;把…...

MySQL基础四(JDBC)

JDBC(重点) 数据库驱动 程序会通过数据库驱动&#xff0c;和数据库打交道。 sun公司为了简化开发人员对数据库的统一操作&#xff0c;提供了一个Java操作数据库的规范。这个规范由具体的厂商去完成。对应开发人员来说&#xff0c;只需要掌握JDBC接口。 熟悉java.sql与javax.s…...

审批流AntV框架蚂蚁数据可视化X6饼图(注释详尽)

大家好&#xff0c;这次使用的是AntV的蚂蚁数据可视化X6框架&#xff0c;类似于审批流的场景等&#xff0c;代码如下&#xff1a; X6框架参考网址&#xff1a;https://x6.antv.vision/zh/examples/showcase/practices#bpmn 可以进入该网址&#xff0c;直接复制下方代码进行调试…...

用Python之requests库调用大模型API实现多轮对话

文章目录 1. 多轮对话实现概述2. 多轮对话全上下文实现3. 多轮对话最近上下文的链式实现4. 总结 1. 多轮对话实现概述 多轮对话功能可以让大模型“拥有记忆”&#xff0c;满足如追问、信息采集等需要连续交流的场景。 AI大模型API 不会记录您的对话历史记录。如果您需要让大模…...

《异步江湖:XHR、Promise 与 Event Loop 的恩怨情仇》

XMLHttpRequest XMLHttpRequest&#xff08;简称 XHR&#xff09;是浏览器提供的一个 JavaScript 对象&#xff0c;用于在客户端和服务器之间发送 HTTP 请求。它是实现 AJAX&#xff08;Asynchronous JavaScript and XML&#xff09; 技术的核心工具&#xff0c;允许网页在不…...

【极客时间】浏览器工作原理与实践-2 宏观视角下的浏览器 (6讲) - 2.5 渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?

https://time.geekbang.org/column/article/118205 2.5 渲染流程&#xff08;上&#xff09;&#xff1a;HTML、CSS和JavaScript&#xff0c;是如何变成页面的&#xff1f; 2.4讲了导航相关的流程&#xff0c;那导航被提交后又会怎么样呢&#xff1f; 就进入了渲染阶段。 这…...

蓝桥杯第15届真题解析

由硬件框图可以知道我们要配置LED 和按键、lcd&#xff0c;解决lcd引脚冲突 LED 先配置LED的八个引脚为GPIO_OutPut&#xff0c;锁存器PD2也是&#xff0c;然后都设置为起始高电平&#xff0c;生成代码时还要去解决引脚冲突问题 按键 按键配置&#xff0c;由原理图按键所对引…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...