通过上图可知,我们的Producer是Video,填充视频帧后,再对纹理进行特效处理(滤镜等),最后再渲染出来。前面我们分析了BufferQueue的工作流程,但是在Producer要填充数据、执行dequeueBuffer操作时,如果有Buffer已经QUEUED,且申请的dequeuedCount大于mMaxDequeuedBufferCount,就不会再继续申请Free Buffer了,Producer就无法DequeueBuffer,也就导致onFrameAvailable无法最终调用,核心源码如下:
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,sp<android::Fence> *outFence, uint32_t width, uint32_t height, PixelFormat format, uint32_t usage,FrameEventHistoryDelta* outTimestamps) { ...... int found = BufferItem::INVALID_BUFFER_SLOT; while (found == BufferItem::INVALID_BUFFER_SLOT) { status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, & found); if (status != NO_ERROR) { return status; } } ...... } status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, int*found) const{ ...... while (tryAgain) { int dequeuedCount = 0; int acquiredCount = 0; for (int s : mCore -> mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { dequeuedCount; } if (mSlots[s].mBufferState.isAcquired()) { acquiredCount; } } // Producers are not allowed to dequeue more than // mMaxDequeuedBufferCount buffers. // This check is only done if a buffer has already been queued if (mCore -> mBufferHasBeenQueued && dequeuedCount >= mCore -> mMaxDequeuedBufferCount) { BQ_LOGE("%s: attempting to exceed the max dequeued buffer count " "(%d)", callerString, mCore -> mMaxDequeuedBufferCount); return INVALID_OPERATION; } } ....... }
5. 码流适配
视频的监控体系发现,Android 9.0的系统出现大量的编解码失败问题,错误信息都是相同的。在MediaCodec的Configure时候出异常了,主要原因是我们强制使用了CQ码流,Android 9.0以前并无问题,但9.0及以后对CQ码流增加了新的校验机制而我们没有适配。核心流程代码如下:
status_t ACodec::configureCodec( const char *mime, const sp<AMessage> &msg) { ....... if (encoder) { if (mIsVideo || mIsImage) { if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) { return INVALID_OPERATION; } } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC) && !msg->findInt32("bitrate", &bitrate)) { return INVALID_OPERATION; } } ....... } static bool findVideoBitrateControlInfo(const sp<AMessage> &msg, OMX_VIDEO_CONTROLRATETYPE *mode, int32_t *bitrate, int32_t *quality) { *mode = getVideoBitrateMode(msg); bool isCQ = (*mode == OMX_Video_ControlRateConstantQuality); return (!isCQ && msg->findInt32("bitrate", bitrate)) || (isCQ && msg->findInt32("quality", quality)); } 9.0前并无对CQ码流的强校验,如果不支持该码流也会使用默认支持的码流, static OMX_VIDEO_CONTROLRATETYPE getBitrateMode(const sp<AMessage> &msg) { int32_t tmp; if (!msg->findInt32("bitrate-mode", &tmp)) { return OMX_Video_ControlRateVariable; } return static_cast<OMX_VIDEO_CONTROLRATETYPE>(tmp); }
关于码流还有个问题,就是如果通过系统的接口isBitrateModeSupported(int mode),判断是否支持该码流可能会出现误判,究其原因是framework层写死了该返回值,而并没有从硬件层或从media_codecs.xml去获取该值。关于码流各硬件厂商支持的差异性,可能谷歌也认为码流的兼容性太碎片化,不建议用非默认的码流。
6. 音频处理
音频处理还括对音频的混音、消声等操作。在混音操作的时候,还要注意音频文件的单声道转换等问题。
其实视频问题总结起来,大部分是都会牵扯到编解码(尤其是使用硬编码),需要大量的适配工作(以上也只是部分问题,碎片化还是很严峻的),所以就需要兜底容错方案,比如加入软编。
线上监控
视频功能引入了埋点、日志、链路监控等技术手段进行线上的监控,我们可以针对监控结果进行降级或维护更新。埋点更多的是产品维度的数据收集,日志是辅助定位问题的,而链路监控则可以做到监控预警。
我们加了拍摄流程、音视频处理、视频上传流程的全链路监控,整个链路如果任何一个节点出问题都认为是整个链路的失败,若失败次数超过阈值就会通过大象或邮件进行报警,我们在适配Andorid 9.0码流问题时,最早发现也是由于链路监控的预警。所有全链路的成功率目标值均为98%,若成功率低于92%的目标阈值就会触发报警,我们会根据报警的信息和日志定位分析,该异常的影响范围,再根据影响范围确定是否热修复或者降级。
我们以拍摄流程为例,来看看链路各核心节点的监控,如下图:
容灾降级
视频功能目前只支持粗粒度的降级策略。我们在视频入口处做了开关控制,关掉后所有的视频功能都无法使用。我们通过线上监控到视频的稳定性和成功率在特定机型无法保证,导致影响用户正常的使用商家端App,可以支持针对特定设备做降级。后续我们可以做更细粒度的降级策略,比如根据P0级功能做降级,或者编解码策略的降级等。
维护更新
视频功能上线后,经历了几个稳定的版本,保持着较高的成功率。但近期收到了Sniffer(美团内部监控系统)的邮件报警,发现视频处理链路的失败次数明显增多,通过Sniffer收集的信息发现大部分都是Android 9.0的问题(也就是上面讲的Android 9.0码流适配的问题),我们在商家端5.2版本进行了修复。该问题解决后,我们的视频处理链路成功率也恢复到了98%以上。
总结和规划
视频功能上线后,稳定性、内存、CPU等一些相关指标数据比较理想。我们建设的监控体系,覆盖了视频核心业务,一些异常报警让我们能够及时发现问题并迅速对异常进行维护更新。但视频技术栈远比本文介绍的要庞大,怎么提高秒播率,怎么提高编解码效率,还有硬编解码过程中可能造成的花屏、绿边等问题都是挑战,需要更深入的研究解决。
未来我们会继续致力于提高视频处理的兼容性和效率,优化现有流程,我们会对音频和视频处理合并处理,也会引入软编和自定义编解码算法。
美团外卖大前端团队将来也会继续致力于提高用户的体验,将在实践过程中遇到的问题进行总结,继续和大家分享。敬请关注。
如果你也对视频技术感兴趣,欢迎加入我们。
参考资料
- Android开发者官网
- Google CTS
- Grafika
- BufferQueue原理介绍
- MediaCodec原理
- 微信Android 视频编码爬过的坑
- mp4文件结构(一)、(二)、(三)、(四)
- AndroidVideoCache 代理策略
- ijkplayer
- mp4parser
- GPUImage
作者简介
金辉、李琼,美团外卖商家终端研发工程师。
招聘信息
,美团外卖商家终端研发团队的主要职责是为商家提供稳定可靠的生产经营工具,在保障稳定的需求迭代的基础之上,持续优化APP、PC和H5的性能和用户体验,并不断优化提升团队的研发效率。团队主要负责的业务主要包括外卖订单、商品管理、门店装修、服务市场、门店运营、三方会话、蓝牙打印、自动接单、视频、语音和实时消息触达等基础业务,支撑整个外卖链路的高可用性及稳定发展。
团队通过架构演进及平台化体系化建设,有效支撑业务发展,提升了业务的可靠性和安全性;通过大规模落地跨平台和动态化技术,加快了业务迭代效率,帮助产品(PM)加快产品方案的落地及上线;通过监控容灾体系建设,有效保障业务的高可用性和稳定性;通过性能优化建设,保证APP的流畅性和良好用户体验。团队开发的技术栈包括Android、iOS、React、Flutter和React Native。
美团外卖商家端研发团队长期招聘Android、iOS、和前端工程师,欢迎有兴趣的同学投简历至:tech@meituan.com(邮件标题注明:美团外卖商家端)