ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

海思HiMPP开发必懂:MMZ内存与系统内存的区别及使用技巧

海思HiMPP开发必懂:MMZ内存与系统内存的区别及使用技巧 海思平台搞视频方案开发的朋友几乎都绕不开HiMPPHi Media Process Platform这套SDK。我最初在海思芯片上调视频通路时第一件让我栽跟头的事就是MMZ内存和系统内存的区别。那时候刚从其它平台转过来以为只要是malloc出来的内存就都能拿给硬件用结果图像花屏、系统卡死、VPSS报错轮番来了一遍最后查到头发现就是内存类型用错了。写这篇东西就是想把MMZ内存这套机制用大白话讲清楚顺便用一个我实际调过的例程把“什么时候用MMZ内存、什么时候用系统内存、为什么不能用错”一次说明白。不管你是在做IPC、行车记录仪、双目避障还是边缘盒子只要你用的是海思的芯片这篇都值得你花几分钟看完。1. 先从MMZ的设计初衷说起很多刚接触海思平台的兄弟会把MMZ理解成“一块特殊的内存”其实这话只对了一半。MMZ的全称是Media Memory Zone从名字就能看出来它是专门给多媒体业务用的内存区域。海思芯片为什么非要单独划分这么一块区域而不是像PC那样所有内存统一管理这要从多媒体硬件的几个特性来分析。1.1 硬件模块需要一个“连续”的物理空间海思芯片里跑视频编解码、ISP、VPSS这些模块的不是CPU而是专用的硬件加速器。这些硬件在搬运数据时通常不经过CPU的MMU页表而是直接通过总线访问物理内存。这意味着它们要的内存必须是物理地址连续的一块区域。系统内存由Linux内核管理虽然对用户态来说看起来是连续的虚拟地址空间但底层对应的物理页面往往是分散的。你malloc 8MB内核大概率拆成几十个甚至上百个4KB物理页散落在各处。CPU能通过页表把这些页面拼成虚拟地址连续的假象但硬件加速器不认这一套它只认物理地址。你把分散的物理地址丢给编码器编码器取数直接就乱了花屏、报错都是轻的严重时直接死机。而MMZ内存由海思SDK独立管理它从系统物理内存里专门隔离出一段连续的大块区域硬件模块可以很容易通过物理地址连续访问。1.2 多媒体数据量大频繁搬运会拖垮CPU视频一帧1080p的YUV420数据大约是3MB左右如果所有数据都要CPU copy搬运那CPU占用率会被拖到惨不忍睹。HiMPP的设计思路是让硬件模块直接操作内存通过指针传递而不是数据拷贝这样就得有一块硬件和软件都能直接访问的共享内存池MMZ就是干这个的。1.3 Cache一致性一个容易被忽视的坑海思芯片的CPU和硬件加速器都能访问同一块物理内存但如果这块内存走的Cache策略不对软件写进去的数据硬件读不到硬件写好的数据软件读出来是脏的。MMZ内存默认使用非Cache或写透write-through的映射方式就是为了保证CPU和硬件之间看到的数据是一致的。系统内存默认是带Cache的硬件写数据后Cache没刷新你往上一读看到的其实是旧数据。注意Cache一致性问题不是说所有场景下都必然出现问题它的随机性很强有时候10次对9次错的那一次会让你查到头秃。MMZ把这层脏活累活都替你处理了这就是它存在的另一个关键理由。1.4 多进程、多模块共享的需要海思方案的软件架构往往是多个进程协同工作ISP进程采集、智能分析进程跑算法、编码进程做处理、RTSP进程推流。这几个模块如果各自拿各自的内存数据交互就得走进程间拷贝效率极低。MMZ设计上支持多模块引用同一块物理地址IPC、智能分析、编码可以零拷贝地共享同一帧数据这是系统内存做不到的。理解了设计初衷再去看MMZ API思路就会清晰很多。2. 正确打开MMZ内存的方式海思SDK里针对MMZ内存提供了一套独立API封装在mpi_mmz.h这个头文件里。常用的是这几个HI_MPI_MMZ_Open(HI_VOID); HI_MPI_MMZ_Alloc(MMZ_BUFFER_S *pBuf, HI_CHAR *pBufName, MMZ_MPA_S *pMpa); HI_MPI_MMZ_AllocEx(MMZ_BUFFER_S *pBuf, HI_CHAR *pBufName, MMZ_MPA_S *pMpa); HI_MPI_MMZ_Map(HI_ULONG u64PhyAddr, MMZ_MPA_S *pMpa, HI_VOID **pVirtAddr); HI_MPI_MMZ_Flush(HI_ULONG u64PhyAddr, MMZ_MPA_S *pMpa); HI_MPI_MMZ_Close(HI_VOID);简单地捋一下它们的关系HI_MPI_MMZ_Open初始化MMZ模块在程序最开始调用。如果MMZ已经有别的模块在用Open内部计数会增加不会重新初始化。HI_MPI_MMZ_Alloc分配一块MMZ内存。返回的结构体MMZ_BUFFER_S里有物理地址和大小但没有用户态虚拟地址。HI_MPI_MMZ_Map把刚分配到的物理地址映射到用户态虚拟地址空间映射之后你才能在代码里直接访问这块内存。HI_MPI_MMZ_Flush做Cache刷新如果需要CPU和硬件交替访问同一块MMZ内存在处理完数据后要调用它保证一致性。2.1 开内存一个经典示例下面是一个完整的MMZ内存申请和使用流程HI_S32 s32Ret; MMZ_BUFFER_S stBuf; MMZ_MPA_S stMpa; memset(stBuf, 0, sizeof(stBuf)); memset(stMpa, 0, sizeof(stMpa)); stMpa.u32BufSize 1920 * 1080 * 3 / 2; // 1080P YUV420大小 stMpa.u32Alignment 64; // 64字节对齐性能更好 stMpa.bCached HI_TRUE; // 缓存属性根据需求设置 s32Ret HI_MPI_MMZ_Open(); if (s32Ret ! HI_SUCCESS) { printf(MMZ Open failed: %#x\n, s32Ret); return s32Ret; } s32Ret HI_MPI_MMZ_Alloc(stBuf, test_buffer, stMpa); if (s32Ret ! HI_SUCCESS) { printf(MMZ Alloc failed: %#x\n, s32Ret); return s32Ret; } HI_VOID *pVirtAddr NULL; s32Ret HI_MPI_MMZ_Map(stBuf.u64PhyAddr, stMpa, pVirtAddr); if (s32Ret ! HI_SUCCESS) { printf(MMZ Map failed: %#x\n, s32Ret); return s32Ret; } // 到这里就可以使用 pVirtAddr 读写数据了物理地址为 stBuf.u64PhyAddr // 如果要把这块内存交给硬件模块传递物理地址 stBuf.u64PhyAddr // 不再使用时注意调Map后再用HI_MPI_MMZ_Unmap释放虚拟映射 HI_MPI_MMZ_Unmap(stBuf.u64PhyAddr, stMpa, pVirtAddr); // 释放MMZ内存 HI_MPI_MMZ_Free(stBuf.u64PhyAddr);2.2 很多人忽略的两个关键点第一MMZ_Alloc返回的结构体里只有物理地址和内存名没有用户态虚拟地址必须通过MMZ_Map才能拿到可访问的指针。第二MMZ_Map的映射是一次性的两个不同进程需要对同一物理地址做映射时要各自调用一次HI_MPI_MMZ_Map不能直接在进程间传递虚拟地址。操作提示建议给每块MMZ内存起一个有意义的名字比如vgs_buffer_chn0。这么做最直观的好处是排查内存泄漏时通过cat /proc/media-mem能直接看出哪块内存是谁分配的、块头多大、挂在哪个模块下。起名虽然不花几秒钟但在后期Debug阶段能省下几小时。2.3 MMZ内存的两个独特属性物理连续和按需映射物理连续这点前面说过了这里重点说一下按需映射。MMZ内存分配出来的时候只有物理地址没有被映射进任何进程的虚拟地址空间硬件模块可以自由使用但CPU访问不到。只有当你需要CPU去读写这块内存的内容时才通过MMZ_Map把它映射进当前进程。这个设计带来的隐含能力是多个进程可以同时引用同一块物理地址各自按需映射互相没有干扰。比如ISP进程采集到一帧画面物理地址放进一个共享结构里智能分析进程、编码进程、RTSP推流进程都拿这个物理地址各自做Map访问全程零拷贝。这是系统内存完全做不到的高效协同方式。3. 一个例子讲透从画图板到显示通道前面讲了那么多理论不落地的理解都是虚的。下面我用一个实际会踩坑的例子把MMZ内存和系统内存的区别讲透彻。3.1 场景描述我当时的场景是这样的在Hi3516EV200上调试一个简单的视频显示demo功能是通过VGSVideo Graphics Subsystem把一个RGB565格式的图片叠加到视频画面上然后通过VOVideo Output显示出来。VGS的叠加输入需要的是物理连续内存。我当时图省事直接用malloc申请了一块系统内存填充图像数据想把这块内存的虚拟地址直接传给VGS接口。结果编译倒是通过了运行起来画面就是不对VGS输出全黑。有的板子还会在连续多次调用后直接段错误。3.2 错误做法直接用malloc的系统内存HI_VOID *pSrcVirAddr NULL; pSrcVirAddr malloc(1920 * 1080 * 2); // RGB565, 一帧 if (NULL pSrcVirAddr) { printf(malloc failed\n); return HI_FAILURE; } // 在这里往 pSrcVirAddr 填充图像数据 FillImage(pSrcVirAddr); // 传给VGS VGS_Handle hVgs; VGS_IMAGE_S stSrcImage; memset(stSrcImage, 0, sizeof(stSrcImage)); stSrcImage.enPixelFormat PIXEL_FORMAT_RGB_565; stSrcImage.u32Width 1920; stSrcImage.u32Height 1080; stSrcImage.u64PhyAddr (HI_U64)(HI_ULONG)pSrcVirAddr; // 这里直接把虚拟地址当成物理地址用 stSrcImage.u32Stride 1920 * 2; s32Ret HI_MPI_VGS_BeginJob(hVgs); if (s32Ret ! HI_SUCCESS) { printf(VGS BeginJob failed: %#x\n, s32Ret); return s32Ret; } s32Ret HI_MPI_VGS_AddRectTask(hVgs, stSrcImage, stDstImage); // ... 后面提交任务这段代码我后来回想起来都觉得脸疼。你有没有注意到我用了(HI_U64)(HI_ULONG)pSrcVirAddr这个强转这就是根源所在。malloc返回的虚拟地址是进程的线性地址空间跟物理地址完全是两码事。我又不是在内核态也没有通过DMA API做地址转换直接拿这个地址当物理地址用等于告诉硬件“请你去读一个不存在的物理地址”。硬件模块读的是物理总线地址用户态malloc返回的虚拟地址要经过CPU页表才能变成物理地址。硬件加速器不经过CPU页表却拿到用户态虚拟地址去读数据结果就是读了个寂寞黑屏、花屏、数据错乱都说得通。3.3 正确做法使用MMZ内存正确的思路是使用MMZ分配物理连续内存再通过MMZ_Map拿虚拟地址做CPU侧填充MMZ_BUFFER_S stMMZBuf; MMZ_MPA_S stMMZMpa; HI_VOID *pSrcVirAddr NULL; HI_MPI_MMZ_Open(); memset(stMMZMpa, 0, sizeof(stMMZMpa)); stMMZMpa.u32BufSize 1920 * 1080 * 2; stMMZMpa.u32Alignment 64; stMMZMpa.bCached HI_FALSE; // 硬件访问建议关Cache // 分配一块MMZ内存 s32Ret HI_MPI_MMZ_Alloc(stMMZBuf, overlay_rgb565, stMMZMpa); if (s32Ret ! HI_SUCCESS) { printf(MMZ Alloc failed: %#x\n, s32Ret); return s32Ret; } // 映射到用户态拿虚拟地址 s32Ret HI_MPI_MMZ_Map(stMMZBuf.u64PhyAddr, stMMZMpa, pSrcVirAddr); if (s32Ret ! HI_SUCCESS) { printf(MMZ Map failed: %#x\n, s32Ret); return s32Ret; } // 填充图像数据 FillImage(pSrcVirAddr); // 传给VGS用MMZ返回的物理地址 stSrcImage.u64PhyAddr stMMZBuf.u64PhyAddr; // 这回是真正的物理地址了 // ... 后面代码一样 // 用完记得Unmap和Free HI_MPI_MMZ_Unmap(stMMZBuf.u64PhyAddr, stMMZMpa, pSrcVirAddr); HI_MPI_MMZ_Free(stMMZBuf.u64PhyAddr);改动不大核心差异就是留给VGS的地址从malloc的虚拟地址换成了MMZ分配的物理地址。3.4 换完之后发生了什么换用MMZ内存后画面立刻正常VGS叠加显示稳定连续跑了几百帧也没有复现之前的花屏。通过这个例子你应该能直观理解为什么标题说“MMZ内存与系统内存傻傻分不清”是新手最容易踩的坑。哪怕你代码逻辑写得再对只要地址搞错硬件就永远不配合你工作。3.5 这个例子的真相MMZ映射之后等于什么有兄弟可能会问“你这里MMZ_Map之后不是也拿到了虚拟地址吗那跟系统内存malloc出来的虚拟地址有什么区别”好问题。区别不在于是不是虚拟地址而在于这个虚拟地址对应的物理属性MMZ映射出来的虚拟地址对应的物理内存是固定、连续、且已经在MMZ管理器中登记好的这块内存在分配之后内核不会再挪动它页表映射关系稳定硬件随时可以通过同一个物理地址访问到它。malloc出来的虚拟地址对应的物理内存在内核的管理下可能随时被换出、换入、甚至被其它进程占走就算你通过某些方式查到了物理地址也没法保证下一次访问时这块物理页还是你的。用个生活化的比喻系统内存像是快捷酒店的房间你今天住这间明天酒店可能把房间分配给别人你拿着一张过期的房卡再去开那扇门自然就出问题了。而MMZ内存像是你自己买的房子产权固定、地段固定、随时可以去住今天去、明天去、后天去房产证上都是你的名字。4. 自查清单几句话判断你用对内存没有我知道很多人项目赶得急没有时间读完整的MPP开发文档。这里直接给你一个自查清单按顺序过一遍大部分内存问题都能定位个八九不离十。4.1 从代码上自查硬件模块VGS、VPSS、VENC、VDEC、TDE的数据buffer是不是都是用HI_MPI_MMZ_Alloc分配的给硬件模块传地址的参数是不是从MMZ_BUFFER_S结构体里取的u64PhyAddr而不是自己malloc出来的虚拟地址如果需要CPU和硬件交替读写同一块MMZ内存是否在合适位置调用了HI_MPI_MMZ_Flush是否有进程间传虚拟地址的情况传物理地址才是正确姿势。MMZ内存用完有没有Free如果只Map没Unmap、只Alloc没Free长时间运行内存会越耗越少。4.2 从现象上反推现象可能原因排查方向画面全黑或花屏传给硬件的物理地址不对检查是否用了malloc地址运行一段时间后内存耗尽MMZ内存泄漏cat /proc/media-mem 看谁占着不放图像偶尔错位、颜色混乱Cache一致性问题检查MMZ是否需要FlushbCached配置是否合理系统启动时MMZ初始化失败MMZ大小配置不足检查内核cmdline里的mmedia_size参数硬件任务提交失败内存对齐不满足或地址非法检查u32Alignment是否符合模块要求4.3 从工具上验证海思SDK提供了一些调试接口用法很简单# 查看MMZ整体使用情况和各块内存详情 cat /proc/media-mem # 查看当前系统内存分配情况 cat /proc/meminfo/proc/media-mem的输出里有每块MMZ内存的名字、物理地址、大小、分配者。如果怀疑内存泄漏先跑一晚上程序再cat一下看看哪块内存的地址一直在增长就能顺着名字找到代码里对应的分配点。这个方法在实机上验证过非常高效。4.4 常见误区MMZ不是万能的也有一个反向误区需要提醒有些人理解了MMZ之后就把所有内存操作都换成MMZ编码器输入、显示叠加、算法buffer全部都塞进MMZ结果MMZ划分的内存不够用到处报MF_ALLOC_FAILED。系统内存也有它的价值。纯CPU逻辑处理的数据、临时变量、小尺寸的配置文件buffer用系统内存完全合理且高效。只有需要硬件模块参与的“多媒体数据”才应该放MMZ。一张图帮你记忆数据流经硬件模块VGS/VPSS/VENC/VDEC/TDE/SVP→ 用MMZ数据只和CPU相关拷贝、解析、存储逻辑→ 用系统内存硬件模块和CPU频繁互访的共享buffer → 用MMZ注意Cache刷新DMA外设访问的系统内存 → 如果要绕过CPU直接搬运建议也要用MMZ或做专门的DMA映射这个划分原则我用到现在几乎覆盖了所有海思方案的内存设计场景。5. 常见问题与排查技巧实录整理一些我在实际调试中踩过、或者帮同事排查过的典型问题希望能让你少走几个弯路。5.1 MMZ_Alloc失败mf: malloc failed!这个报错有几种可能第一MMZ内存没分够。启动参数里的mmedia_size设小了。比如你同时开了两路1080p编码又要VGS叠加还要VPSS多路输出MMZ需求会比单路大很多。修改方法是在内核启动参数里调大mmedia_size例如从默认值扩展到256MB甚至更大具体数值按业务需求估算。注意不要无脑加到512MB因为MMZ是从系统内存里切走的MMZ大了系统内存就小了要平衡。第二MMZ内存被泄漏耗光了。这时候先别调大mmedia_size先查代码。cat /proc/media-mem看还有多少剩余哪个名字占了多块大buffer。最常见的泄漏点是每帧都Alloc但不Free或者Free之前没有Unmap导致的引用计数异常。第三分配的size超过了MMZ支持的单块上限。MMZ不支持分散的、不连续的大块内存分配。比如某个模块需要一块64MB连续buffer而MMZ里刚好没有这么大的连续空洞也会分配失败。这种情况需要用MemInfo命令确认碎片情况。5.2 MMZ_Map返回失败报MMM_BUF_NOT_FOUND之类的错误常见原因是物理地址传错了。有些人直接拿了HI_MPI_MMZ_Alloc返回的u64PhyAddr再去Map一般没问题。但如果你传的是从某个没分配成功的buffer结构体里取出来的乱值Map自然找不到对应内存块。还有种情况是这个物理地址已经被释放了但你手里还攒着这个地址。多层模块共享同一块MMZ内存时一定要理清生命周期A模块释放之后B模块再去Map几乎必失败。遇到这类问题我一般直接在Map前打印一下传入的物理地址和/proc/media-mem里的数值做一下对比马上就能看出来是不是地址失效了。5.3 硬件访问正常CPU读出来全是0xFF这种情况多半是Cache一致性问题。MMZ内存如果配置了bCached HI_TRUE硬件往里面写数据之后CPU侧的Cache还保留着旧状态读出来的就是旧值或垃圾值。解决办法主要有两种把这个MMZ buffer的bCached配置成HI_FALSE。适合硬件和CPU频繁访问的场景比如编码器输出码流后CPU需要解析。缺点是CPU读写这个buffer的性能会下降因为它每次读写都直接走内存不走Cache。保留bCached HI_TRUE在硬件写完后调用HI_MPI_MMZ_Flush刷新Cache。适合CPU不频繁读写的场景比如CPU偶尔校验一下数据。实际操作建议对性能敏感的视频帧数据我的习惯是关掉Cache用bCached HI_FALSE。对偶尔被CPU访问的数据比如编码输出的SPS/PPS部分开Cache并在读之前Flush。5.4 mmap文件和DMA搬运之间有数据错乱的坑有次在RK平台的同事帮我看了一个问题他在海思上用fwrite把编码器输出的H264码流写到SD卡结果偶尔出现几帧花屏。原因出在他用malloc了一块系统内存做中转编码器DMA搬运过来的数据在经过CPU侧Cache时没刷新fwrite读了一部分脏数据。后来我让他在编码数据从MMZ内存拷贝到系统内存时先调用HI_MPI_MMZ_Flush保证MMZ里的数据更新到物理内存再memcpy。这样处理之后基本没有复现过花屏。这类问题最坑的地方在于它不会100%出现。负载轻的时候跑几个小时没问题负载一高、缓存刷新时机变了问题就闪现。排查思路就一句话任何硬件写入、软件读取的操作之间一定要有显式的同步点Flush或关Cache。5.5 多进程通信传经还是传地址很多海思方案不是单进程而是ISP进程、人脸算法进程、编码进程用IPC机制通信。我看到不少项目组会在进程间传一块malloc出来的指针然后在另一个进程里强转成结构体访问结果各种崩溃。正确的姿势应该是在共享内存里放一块MMZ buffer的物理地址各进程各调一次HI_MPI_MMZ_Map把自己的虚拟地址映射出来再用这个虚拟地址访问。这样避免了进程间虚拟地址空间的错乱也避免了不必要的内存拷贝。如果进程间传递的只是消息头不涉及视频数据那也可以用标准的IPC机制但数据部分必须通过MMZ物理地址来共享。传送buffer的物理地址时建议把buffer名字也一起传过去这样接收端可以在/proc/media-mem里查证地址合法性。5.6 一个排查工具经验备忘排查内存问题的时间分配我个人的习惯是前期花20%时间看代码逻辑80%时间用/proc/media-mem和边界打印定位。因为内存问题很多都是运行时状态问题光靠人眼读代码很难看出端倪。我推荐的做法是写一个简单的脚本每次在关键步骤前后cat一次/proc/media-mem自动diff。把分配、释放的时间和内存块变化对齐一旦出现异常增长脚本输出就直接锁定有问题的分配点。另外海思的MPP日志其实也能帮上忙很多版本支持通过export MPP_LOG_LEVEL参数调整日志级别。如果遇到崩溃或异常退出把MPP日志调高后复现一遍输出的信息往往比你在代码里加的printf更详细。日志级别设置方法可以在SDK的文档里查到每个版本略有差异具体以你的SDK为准。6. 设计MMZ内存布局时的几条经验最后聊一聊产品设计阶段怎么做MMZ内存布局。很多人是边写代码边发现内存不够再回头调参数这样其实很被动。如果你正在做一个新方案不妨先按下面的思路做一次内存测算。6.1 先统计多媒体通路需要的内存块把方案里每个模块的数据流捋一遍列出所有需要物理连续内存的地方。比如传感器输出RAW图VPSS处理需要几路输出buffer每路编码器需要几帧参考帧buffer、码流bufferVGS叠加位图或旋转需要的临时buffer人脸检测、智能分析模块需要的特征图buffer移动侦测、音频处理等其它模块把这些buffer的size逐项列成一个表格乘以需要的帧数、路数得到一个基础需求值。然后在这个基础值上乘以1.2到1.5的系数作为MMZ的预留余量。这个余量是为了应付动态场景下的峰值内存占用。6.2 根据系统总内存倒推mmedia_size比如你的方案是1GB内存的板子算出来的MMZ需求是400MB系统内存需求大约是300MB左右那么mmedia_size设成512MB是比较稳妥的剩余空间给Linux内核和应用程序。如果发现两者加起来超过总内存就要考虑裁剪路数、降低分辨率或者减少buffer帧数了。再提醒一次mmedia_size不只是给MMZ用的有的海思SDK版本里部分系统共享内存、硬件模块的内部SRAM缓冲等也会从这段区域里划走。所以不要只算业务buffer要把SDK固定开销也算进去最稳妥的方式是先在板上跑一个空democat /proc/media-mem看基础占用再在这个基础上加业务buffer。6.3 按模块设置MMZ内存分配顺序程序运行时MMZ内存的分配顺序也会影响碎片化程度。建议把最重要的、需要大块连续buffer的模块优先分配比如编码器参考帧、ISP raw buffer然后再分配小块的buffer。如果反过来小块buffer先占掉了大块连续区域中间的空隙大buffer分配就可能失败。这块没有硬性规定但在实际项目中按这个思路安排分配顺序MMZ的分配失败率会低很多。6.4 内存焦虑的另一个解VB Pool海思平台除了直接使用MMZ API最常用的内存管理方式是VBVideo Buffer池。VB池本质上是基于MMZ封装的一套更高级的内存管理机制由MPP统一管理支持业务模块间的高效共享和引用计数。如果你在做的是标准视频通路建议优先使用VB Pool而不是自己手动Alloc/Free MMZ buffer。我在自己的项目里一般这么用视频帧数据都走VB PoolVGS叠加的位图、编码器的码流buffer等非帧数据用直接MMZ API分配。这样既有灵活度又有稳定度。VB Pool需要关注的是池的大小设置和空闲情况下是否允许用户态申请buffer这些参数可以在各模块的属性配置里调整SDK文档里有详细说明。提示不要把VB Pool理解成越多越好。VB Pool占用的是MMZ空间池子开太大容易导致其它非VB模块内存不足。建议用多路小池子替代单一大池子按通道数划分可以有效降低多路跑满时的竞争。7. 最后再说点实际的写这篇文章不是要把MMZ讲成什么高深技术而是想提醒第一次做海思平台开发的兄弟多媒体内存管理这关避不开也别想绕过。你可以在前期花半小时理解这套机制也可以在后期花三个通宵排查一个花屏bug两者的成本完全不成比例。我自己的开发习惯是拿到一个新板子第一件事就是跑通内存分配/释放的闭环测试把MMZ API、VB Pool、物理地址传递这些基础动作全部验证一遍再往上叠加业务。这条路走一遍之后后面遇到的很多问题都能准确定位到是“逻辑问题”还是“内存问题”不会像无头苍蝇一样乱撞。给一个新手的操作建议先把本文第3节的例子在你自己的板子上用不同分辨率、不同像素格式各跑一遍再把第4节的自查清单打印出来贴屏幕旁边。这样做完之后MMZ内存你基本就算入门了。最后再分享一个小技巧当你在开发板上遇到不明原因的花屏时不要急着查图像格式、不要急着查编码参数先看一眼你的buffer是从哪里来的。大部分情况下真相就藏在这四个字里——用错内存了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进