RV1106这颗芯片最近两年在低成本IPC、可视门铃、工业相机里的出镜率非常高,硬件集成度也确实是同价位里少有的。很多刚接触这块芯片的工程师拿到开发板后的第一件事,就是想把摄像头的画面采集下来,再编码成H264走网络推流或落盘,但一上手就发现链路比想象中长:V4L2取流、ISP的3A处理、RGA格式转换、MPP硬件编码器,每个环节都会冒出一堆文档里没写明白的坑。
这篇文章就是把“RV1106实现H264视频流捕获与编码”这条链路完整拆开,从方案选型、环境准备、核心代码流程,到参数调优和问题排查,把我在实际项目里踩过、填过的坑都摊开来讲。无论你是刚入门嵌入式视觉的开发者,还是已经在做产品化开发、想快速跑通通路的工程师,这篇文章都能给你一条可以少走弯路的参考路径。
1. 项目概述:一块“小而能”的视觉芯片
1.1 RV1106到底是什么,H264编码为什么值得重视
RV1106是瑞芯微推出的视觉处理芯片,集成了一颗算力不高的NPU、一颗ISP、一个RGA图像加速模块,以及H264/H265的硬件编码器。它被大量用在摄像头、门铃、可视对讲、低功耗电池相机这类产品上,核心卖点就是低成本、低功耗、够用。
“够用”怎么理解?以H264编码为例,如果靠CPU去做软件编码,比如在Cortex-A7级别的核上跑x264软编,1080p@30几乎不可能实时;但RV1106内置的硬件编码器可以在CPU占用很低的情况下完成同级别的编码任务。这意味着系统可以把剩下的CPU资源拿去跑AI检测、跑网络协议栈、跑UI,这种分工方式就是嵌入式产品的常态。
所以,H264视频流捕获与编码对RV1106来说不是可选项,而是产品落地的基础能力。H264本身也是目前视频存储和传输领域兼容性最好的编码格式,从浏览器播放、手机App预览到NVR录像,几乎所有终端都认H264。相比之下,H265虽然压缩率更好,但授权费用和终端兼容性问题让它在低端IPC市场里推进得没那么快,很多方案仍然是H264为主、H265为辅。
1.2 完整链路长什么样
在动手写代码之前,脑子里必须有整条链路的全貌。RV1106上做H264编码,数据流大致是这样:
- 摄像头Sensor通过MIPI CSI接口或DVP接口把裸的RAW/Bayer数据交给ISP;
- ISP负责完成黑电平校正、去噪、坏点处理、自动曝光、自动白平衡等,输出YUV格式图像;
- 应用层通过V4L2接口或RKMPI接口拿到YUV帧;
- 如果采集到的YUV格式、分辨率与编码器输入要求不一致,需要经过RGA做格式转换或缩放;
- 帧数据交给MPP(Media Process Platform)里的VEPU硬件编码器,输出H264裸流;
- 裸流可以写文件、也可以封装成MP4或走RTSP推流。
从开发视角看,这个过程可以划分为两个大块:采集端(V4L2/RKMPI + ISP + RGA)和编码端(MPP Encoder)。本文的重点放在V4L2 + RGA + MPP这条最通用的应用层路径上,因为RKMPI是瑞芯微封装好的更上层接口,用起来更省事,但理解V4L2 + MPP的底层逻辑,能让你在遇到问题时定位更快。
RV1106有个特点需要提前说明:它的通用性不如树莓派这类跑完整Linux发行版的板子,开发环境通常基于Buildroot裁剪,所以一些在PC上随手可用的工具链、调试手段都要提前准备好。后面会专门讲。
2. 方案选型:为什么用V4L2+MPP,而不是直接在驱动里做
2.1 V4L2是Linux视频采集的“标准答案”
嵌入式Linux下做视频采集,绕不开V4L2。V4L2(Video4Linux2)是内核提供的视频设备框架,Sensor、ISP、USB摄像头、HDMI采集卡都可以抽象成V4L2设备节点,应用层统一用open/ioctl/mmap这套标准方式操作。
选择V4L2的一个重要原因是可移植性。你今天在RV1106上写好的采集代码,明天换到RV1126、RK3588甚至其它厂商芯片的Linux SDK上,接口层面几乎不用改,需要改的只是设备节点、格式参数这些配置项。这对产品迭代和多平台适配来说非常关键。
另一个原因是调试方便。V4L2生态成熟,v4l2-ctl工具可以快速查看设备支持的分辨率、像素格式、帧率,还能直接抓一帧原始图像检查ISP输出是否正确。在排查“到底是采集端坏了还是编码端坏了”这种问题时,这个能力能省下一大半力气。
2.2 MPP是硬件编码器的官方“翻译官”
V4L2本身也支持通过encoder节点直接做硬件编码(VIDIOC_ENCODER),但在RV1106这种瑞芯微平台下,我更推荐用MPP库。原因有三点:
第一,MPP是瑞芯微官方维护的媒体处理库,它对硬件编码器的能力封装完整,接口设计更贴近业务场景。比如你要设置码率控制模式、GOP大小、Profile等级,MPP都有明确的参数结构体,而不是散落在不同驱动的私有ioctl里。
第二,MPP对零拷贝、异步编码等高性能路径有原生支持。在做1080p@30以上高帧率编码时,数据搬运开销会让CPU很吃力;MPP允许你直接引用V4L2的dma-buf,让编码器从同一块物理内存里读取数据,省掉一次copy,这个细节在低功耗产品上很重要。
第三,资料和社区沉淀多。瑞芯微SDK里提供了MPP的完整源码和示例,GitHub上也有大量基于MPP的推流项目。遇到问题去搜,能找到很多现成答案。相比之下,直接用V4L2 encoder节点的人少,遇到问题基本只能自己啃驱动。
2.3 数据搬运方式:普通拷贝还是零拷贝
在采集端和编码端之间传数据,有两条路。
路径A:V4L2 mmap映射 + memcpy拷贝到MPP buffer
采集到的帧存放在内核分配的缓冲区里,应用层mmap到用户空间,然后memcpy到MPP申请的内存中,编码器再读取。这个方案逻辑最简单,代码好写,但每一帧都多了一次完整的图像数据拷贝。以1080p NV12为例,一帧是1920×1080×1.5 ≈ 3110400字节,每秒30帧就是约93MB/s的拷贝量,对CPU是实实在在的负担。
路径B:V4L2 dma-buf直接引用给MPP
V4L2采集到的buffer本质上是dma-buf,MPP可以把同一块dma-buf直接作为编码器输入,硬件DMA读取,全程不需要CPU介入拷贝数据。这样CPU只负责控制流的处理,数据流完全走硬件,省电、降延迟、降CPU占用,一举三得。
我在实际项目中的建议是:**1080p@30以下,先用路径A跑通逻辑;想上60帧、或者做低功耗产品,再切换到路径B。**不要把零拷贝当成为第一版就追求的目标,先保证功能正确,再优化性能,这个顺序能减少很多不必要的调试痛苦。
3. 环境准备:让板子先“看见”图像
3.1 SDK构建的最小必要配置
RV1106开发通常使用瑞芯微官方SDK,里面包含U-Boot、Kernel、Buildroot、应用层demo等。第一次编译SDK时间会比较久,但这不是重点,重点是确认几个关键模块确实编译进了系统:
- kernel:打开V4L2相关配置、Sensor驱动、ISP驱动(rkisp)、RGA驱动、VEPU编码器驱动;
- Buildroot:选中mpp库、rga库、rkaiq工具链,以及v4l2-utils、ffmpeg这类调试工具。
如果你用的是官方发布的完整镜像,这些默认都带好了,直接烧录即可。但如果你是自定义Buildroot,很容易漏掉rga或im2d的库,导致后面跑RGA转格式时链接报错。我的习惯是编完系统后先检查/usr/lib下有没有libmpp.so、librga.so、librkaiq.so,同时确认/dev/video0、/dev/dri/renderD128之类的节点是否存在,缺哪个补哪个。
3.2 采集设备自检三板斧
在写任何代码之前,先用工具确认采集链路是通的。这一步能帮你避开“代码没问题但硬件链路没起”的低级错误。
第一步,确认Sensor有没有被正确驱动。跑一下dmesg | grep -i sensor,或media-ctl -p查看媒体拓扑,能看到sensor实体和ISP实体说明驱动加载成功。
第二步,确认V4L2设备节点可用。输入v4l2-ctl --list-devices查看硬件设备名对应的/dev/videoX节点,再执行v4l2-ctl -d /dev/video0 --list-formats-ext --list-framesizes查看该节点支持的分辨率和像素格式。
第三步,抓一帧看看。执行v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=NV12 --stream-mmap --stream-count=1 --stream-to=frame.raw,把原始帧导出来,用7yuv或ffplay按NV12格式预览。如果画面是正常场景,说明采集链路OK;如果黑屏或花屏,先检查ISP和Sensor配置,别急着往后走。
我个人建议,初次调试最好先接USB摄像头而不是接入板载MIPI Sensor。USB摄像头在V4L2下的驱动路径成熟,不需要RKAIQ参与也能输出图像,可以把“V4L2取流→MPP编码”这条核心链路先跑通,再回头接MIPI Sensor,到时候你只需要关注ISP侧的问题,定位范围会小很多。
4. 核心流程实现:从V4L2帧到H264码流
4.1 第一步:V4L2初始化与Buffer准备
采集端代码的核心是申请一组缓冲区,内核把采集到的帧写入这些缓冲区,应用层通过mmap映射到用户空间后读取。以标准MMAP模式为例,关键步骤是:
int fd = open("/dev/video0", O_RDWR); struct v4l2_format fmt = {0}; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 1920; fmt.fmt.pix.height = 1080; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; fmt.fmt.pix.field = V4L2_FIELD_NONE; ioctl(fd, VIDIOC_S_FMT, &fmt); struct v4l2_requestbuffers req = {0}; req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl(fd, VIDIOC_REQBUFS, &req); // 逐个QUERYBUF + mmap,再QBUF入队 for (int i = 0; i < 4; i++) { struct v4l2_buffer buf = {0}; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ioctl(fd, VIDIOC_QUERYBUF, &buf); mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); ioctl(fd, VIDIOC_QBUF, &buf); } enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd, VIDIOC_STREAMON, &type);这里有个必须注意的细节:VIDIOC_S_FMT设置后最好再读一次实际生效的fmt.fmt.pix,因为驱动可能会对格式做调整,比如宽高对齐。后面往MPP传帧时,必须以实际生效的分辨率而不是你请求的分辨率为准,否则很容易出现“宽了一行的花屏”。
4.2 第二步:MPP编码器初始化和参数配置
MPP的编码接口分初始化、配置、编码循环、销毁几个阶段。初始化时通过mpp_create创建上下文,再用mpp_init指定为编码模式和H264编码协议:
MppCtx ctx; MppApi *mpi; MppEncCfg cfg; mpp_create(&ctx, &mpi); mpp_init(ctx, MPP_CTX_ENC, MPP_VIDEO_CodingAVC); mpp_enc_cfg_init(&cfg); mpp_enc_cfg_set_s32(cfg, "prep:width", 1920); mpp_enc_cfg_set_s32(cfg, "prep:height", 1080); mpp_enc_cfg_set_s32(cfg, "prep:format", MPP_FMT_NV12); mpp_enc_cfg_set_s32(cfg, "rc:mode", MPP_ENC_RC_MODE_CBR); mpp_enc_cfg_set_s32(cfg, "rc:bps", 4 * 1024 * 1024); // 4Mbps mpp_enc_cfg_set_s32(cfg, "rc:bps_max", 4 * 1024 * 1024); mpp_enc_cfg_set_s32(cfg, "rc:bps_min", 4 * 1024 * 1024); mpp_enc_cfg_set_s32(cfg, "rc:gop", 60); // 2s一个IDR mpp_enc_cfg_set_s32(cfg, "rc:fps", 30); mpp_enc_cfg_set_s32(cfg, "codec:type", MPP_VIDEO_CodingAVC); mpp_enc_cfg_set_s32(cfg, "codec:profile", 100); // Main Profile mpp_enc_cfg_set_s32(cfg, "codec:level", 40); // Level 4.0 mpi->control(ctx, MPP_ENC_SET_CFG, cfg);关于参数,有几个容易踩坑的地方:
- 码率控制模式:CBR适合带宽受限的场景,比如RTSP推流;VBR适合存储场景,画面静止时节省码率;AVBR则是在保证最低质量的前提下动态分配。第一次测试用CBR最稳,画面剧烈变化时不会突然飙码率导致卡顿。
- GOP大小:GOP是I帧间隔,通常按帧率×秒数来算。GOP越小,关键帧越多,seek响应越快,但码率浪费越多;GOP越大,压缩效率越高,但播放器拖进度条要等的关键帧会更远。我这里设置60,加30fps就是2秒一个IDR,监控场景比较平衡。
- 宽高:MPP配置时写的是“输入图像”的大小,同时编码器内部有16像素对齐的要求。RV1106编码器实际能处理的对齐方式和其它芯片可能有差异,后面单独讲。
4.3 第三步:编码主循环
编码主循环做的事情可以概括为:从V4L2取一帧,交给MPP编码,取回码流包,写文件或推流。
while (running) { // 从V4L2取帧 struct v4l2_buffer buf = {0}; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ioctl(fd, VIDIOC_DQBUF, &buf); void *frame_data = buffers[buf.index].start; MppFrame frame = NULL; mpp_frame_init(&frame); mpp_frame_set_width(frame, 1920); mpp_frame_set_height(frame, 1080); mpp_frame_set_fmt(frame, MPP_FMT_NV12); mpp_frame_set_buf_size(frame, 1920 * 1080 * 3 / 2); mpp_frame_set_pts(frame, pts_counter++); // 把V4L2帧数据填充到MPP buffer // 使用mpp_buffer_get或mpp_buffer_import,再memcpy MppBuffer mpp_buf = NULL; mpp_buffer_get(NULL, &mpp_buf, 1920 * 1080 * 3 / 2); memcpy(mpp_buffer_get_ptr(mpp_buf), frame_data, 1920 * 1080 * 3 / 2); mpp_frame_set_buffer(frame, mpp_buf); // 提交编码任务 MppTask task = NULL; mpi->poll(ctx, MPP_PORT_INPUT, MPP_POLL_NON_BLOCK); mpi->dequeue(ctx, MPP_PORT_INPUT, &task); mpp_task_meta_set_frame(task, KEY_INPUT_FRAME, frame); mpp_task_meta_set_packet(task, KEY_OUTPUT_PACKET, packet); mpi->enqueue(ctx, MPP_PORT_INPUT, task); // 等待编码输出 mpi->poll(ctx, MPP_PORT_OUTPUT, MPP_POLL_NON_BLOCK); mpi->dequeue(ctx, MPP_PORT_OUTPUT, &task); MppPacket packet = NULL; mpp_task_meta_get_packet(task, KEY_OUTPUT_PACKET, &packet); fwrite(mpp_packet_get_data(packet), 1, mpp_packet_get_len(packet), fp); mpp_packet_deinit(&packet); mpi->enqueue(ctx, MPP_PORT_OUTPUT, task); // 释放V4L2 buffer ioctl(fd, VIDIOC_QBUF, &buf); }这段代码我做了大量简化,实际工程还要处理MPP输出还没拿到、但V4L2已经来了好几帧的情况。一个稳妥的办法是采集和编码分两个线程,中间用环形缓冲队列做解耦,避免采集线程被编码线程阻塞。对于简单的验证demo,单线程循环也能跑,但帧率一旦上30fps就要注意队列堆积问题。
4.4 码流校验与封装
编码循环跑起来后,你会在输出文件里得到一段.h264裸流。裸流直接拿去播放,很多播放器是能打开的(VLC、ffplay都支持),但不一定支持拖进度条,因为只有IDR帧才是随机访问点。
用ffprobe看一眼能不能解析出视频信息:
ffprobe -v error -show_streams -select_streams v output.h264如果解析正常,能看到 profile、level、宽高、帧率这些字段。需要封装成MP4时,用ffmpeg一条命令就能搞定:
ffmpeg -f h264 -i output.h264 -c copy output.mp4这里建议在编码时把SPS/PPS信息识别出来并保存好。MPP会在每次IDR帧前自动插入SPS/PPS,但对封装工具来说,第一帧就得有完整的SPS/PPS才能正确初始化解码器。我在第一次做封装时踩过坑,直接把裸流塞进MP4,结果发现拖进度条后画面乱码,就是因为部分SPS/PPS没有作为关键帧的前缀保存下来。
5. 参数调优:从“能跑”变成“跑得好”
5.1 编码参数明细
H264编码质量的深水区在参数配置上。RV1106的MPP编码器支持丰富的参数,但核心就那么几个:码率控制、GOP、Profile。上面的示例代码里已经给了CBR + 4Mbps + 60GOP的组合,这是监控场景最保守、最稳定的配置。但不同场景需要不同组合:
| 场景 | 码率控制 | 建议码率 | GOP | Profile |
|---|---|---|---|---|
| RTSP实时预览 | CBR | 2~4Mbps | 帧率×1~2秒 | Main |
| 本地存储 | VBR | 4~8Mbps | 帧率×2~4秒 | Main |
| AI抓拍/事件录像 | AVBR | 1~2Mbps | 帧率×0.5秒 | Baseline |
| 低带宽远程看护 | CBR | 512K~1Mbps | 帧率×1秒 | Baseline |
Baseline Profile在低端播放设备里兼容性更好,但压缩效率低于Main,如果你需要兼顾老旧的H264解码芯片,可以选Baseline。Main Profile支持B帧,理论上压缩率更好,但会带来额外的延迟和CPU解码负担。RV1106编码器对B帧的支持还是有限的,很多场景下它是IPPP结构,所以别抱太大期望,按需选择即可。
5.2 分辨率对齐与缓冲设计的坑
H264编码器的硬件单元通常以宏块(16x16)为单位处理图像。这就意味着,编码器输入图像的宽高必须是16的整数倍,否则硬件会按16对齐来读内存,用不对齐的尺寸传帧就会出现“图像边界处出现绿边”或“内存越界读”这类问题。
解决办法有两个:
- 采集端直接选16对齐分辨率的Sensor输出,比如1920x1080本身就是对齐的,好办;
- 如果Sensor输出的是1280x720(1280和720都是16倍数),没问题;如果输出1366x768之类非对齐尺寸,就得先用RGA把它缩放或裁切到16对齐的尺寸,再送编码器。
Buffer数量的选择也直接影响流畅度。V4L2的buffer数量建议至少4个,太少会导致采集线程等不到空的buffer,挨帧率;MPP侧如果采用“申请、释放”的方式频繁分配buffer,会造成内存碎片和延迟,最好预分配一个buffer池,复用空闲buffer,编完一帧后再回收。
5.3 性能优化实录:从卡顿到流畅
在实际项目中,我把一套1080p@30的RTSP推流从卡顿调到基本流畅,核心做了三件事:
一是把从采集到编码改成双线程流水线。采集线程只管把V4L2的buffer填满,编码线程只管从队列里取帧编码,两件事并行,采集线程不再等编码器吐包。这个改动收益最大,CPU占用从原来的90%多降到50%左右。
二是启用零拷贝。把V4L2的dma-buf直接导入MPP,省掉每帧约90MB/s的memcpy。这步需要V4L2驱动和MPP都支持dma-buf导入,RV1106 SDK里的ISP vi节点和MPP都支持这条路。改造后CPU占用进一步下降,帧率也稳住了。
三是把码率控制从CBR改成AVBR。场景是室内固定机位,长时间静止画面居多。AVBR在画面静止时大幅降码率,动起来再抬回目标档位,既节省存储空间,又避免卡顿。这个改动的效果在录像容量上非常明显,同样的TF卡能多存三分之一到一半的时间。
6. 常见问题排查实录
6.1 花屏、绿屏、图像错位
这是绝大多数新手遇到的第一个大坑。画面上出现斜条纹、绿色底纹,或者上下两部分错位,基本可以锁定为像素格式不匹配或分辨率/内存尺寸不对。
排查方法:
- 用
v4l2-ctl --get-fmt-video确认V4L2实际输出格式,很多sensor驱动默认输出YUYV而不是NV12; - 如果
fmt.fmt.pix.bytesperline不等于width×bpp/8,说明驱动有行对齐,拷贝到MPP buffer时不能按整帧一次memcpy,而是应按行拷贝; - 检查MPP端设置的
prep:width/height是否与V4L2实际输出一致,不要写死。
最容易忽略的是bytesperline。很多CMOS sensor驱动输出时会在每行填充额外的字节,你以为是1920宽,实际一行可能是2048字节。这种按整帧拷贝到编码器,图像必然斜裂。
6.2 图像偏色、过暗、过曝
如果画面色彩不对,基本是ISP侧的3A(自动曝光、自动白平衡、自动对焦)没跑起来。RV1106的3A依赖RKAIQ库,它要从Sensor和ISP读取统计数据,然后计算曝光和增益参数写回。
- 确认RKAIQ是否在系统启动时被正确加载:
ps -ef | grep aiq或者看dmesg | grep rkaiq; - 确认Camera的xml配置(insmod/sensor_cfg)是否选对了sensor型号,不同Sensor的初始化序列完全不同;
- 如果只是调试采集链路,可以直接把sensor调成固定曝光和固定增益,先排除3A干扰,再慢慢调3A参数。
我自己在开发中就遇到过Sensor型号没配对,导致3A数据异常,画面整体发绿,最后在系统日志里看到rkaiq报“sensor id mismatch”,换了配置后才正常。
6.3 编码丢帧、卡顿,CPU占用异常
这类问题的根因大多在数据搬运和队列上。
- 检查V4L2侧QBUF是否及时。如果采集线程没及时把buffer还给驱动,内核采集的脸会丢弃,表现为 captures 计数飞快但处理帧数跟不上;
- 检查MPP侧是否用NON_BLOCK模式轮询输出,如果输出包还没就绪就不断空转,会吃掉大量CPU;
- 如果编码配置里开了
split_enc这种多slice并行编码,注意切片数量不是越多越好,RV1106的硬件线程数有限,开多了线程切换开销反而上去。
一个可用的定位手段是定时打印两边的帧计数:V4L2的DQBUF次数、MPP输出的packet数量,两个数对比,如果前者远大于后者,说明瓶颈在编码端;如果两者都跟不上帧率,说明从采集端就已经丢了。
6.4 播放器打不开H264文件
裸流文件后缀是.h264,播放器打不开的原因通常是:
- 文件里只有IDR和普通P帧,没有在IDR前带SPS/PPS,播放器初始化失败;
- 文件是Variable帧率,但VLC这类播放器默认按固定帧率播放,时间轴对不上;
- 编码时设置了
profile=Baseline但level过高,播放设备解不了。
最简单的验证方式是ffprobe看信息,如果没有任何输出或报错,大概率是SPS/PPS问题。注意MPP输出的第一个packet一般不是图像数据,而是SPS/PPS,很多新手直接把这个包丢弃了,编码流就废了。一定要把SPS/PPS保存起来,在写裸流时放到每个IDR帧前。
写在最后的一点个人体会
做RV1106的H264编码,最容易犯的错误就是一上来就想把全链路一次跑通。我每次带新人,都建议他们分三步走:先把V4L2取流单独跑通、把YUV帧保存下来确认画面正常;再把MPP编码单独跑通、用test pattern或网上下载的YUV文件验证编码器输出;最后才把两段代码拼起来。这样任何一步出问题,你都知道问题出在哪一段,而不是在一堆代码里瞎找。
另一个小技巧是开发过程中尽量多留帧计数日志。不要嫌打印浪费性能,打印可以做成开关,出问题时打开,定位后关掉。V4L2侧的DQBUF次数、MPP poll返回的次数,两个数一对比,整个链路哪里堵了一目了然。
最后,H264编码这条链路跑通之后,后续扩展空间其实很大。你可以把输出码流直接交给RTSP服务端做实时预览,也可以封装成MP4配SD卡存储,甚至可以接上NPU做AI事件检测,只在有事件时编码、平时休眠,彻底发挥RV1106的低功耗特性。先把这一顿饭吃透,后面想怎么加菜,都是水到渠成的事。