iOS 编码的视频右侧出现绿条

问题现象

iOS竖屏下,编码的码流在Android, Windows的终端解码偶现右侧出现绿线或黑条。

绿屏.jpg

问题定位

解码端排查

当看到这个现象只在某些终端出现时,第一感觉应该是解码端的问题。因此,我们首先对解码端进行排查。

第一步,保存解码端收到的码流。

第二步,使用StreamEye分析码流,发现解码端收到的码流就存在了绿条,并且出现绿条的视频码流的分辨率为180x320和540x960。

因为解码端收到的码流就已经有问题了,所以问题又转到了编码端来排查。

编码端排查

第一步,保存编码后的视频。排除中间服务器的影响,这也是我们定位问题的原则,首先确定问题是不是在自己的模块,不能别人说是你的问题就是你的问题,要有自己的判断。

第二步,使用StreamEye分析码流,发现编码后的码流确实也存在绿条。查看编码参数的配置,分析哪些可能有关系,但是一直未发现问题。接下只能编码的前一个模块走,分析采集模块了。

第三步,保存采集后的yuv数据。

第四步,使用7yuv工具进行分析yuv数据,发现也是有绿条。接下来就把主要精力放在了采集这个模块。但是排查采集模块的时,走了很多弯路,但是也收获了不少。

采集模块排查

首先第一感觉是yuv中为什么会出现绿条呢?后来突然想起苹果有一个方法CVPixelBufferGetBytesPerRowOfPlane获取的值与yuv的宽度不一样,这个函数获取到数据包含了一些填充数据,目的是为了内存对齐。由于之前对这个了解的不是很清楚,然后网上搜索了一些关于这方面的内容,得到了一个跨距(stride)的概念。跨距是指图像中的一行图像数据所占的存储空间的长度,它是一个大于等于图像宽度的内存对齐的长度。这样每次读取的时候以此为基准读取数据的时候就能内存对齐。参考链接:https://www.jianshu.com/p/68e05ad85490。不同手机内存对齐的位数是不一样的,测试发现iPhone6是16位对齐,iPhone6s是64位对齐。因此,yuv的右侧有绿条或黑条也就是正常了,这时问题又陷入了僵局。

7yuv分析图.jpg

只能猜想是不是系统编码器内部缩放yuv时,没处理好跨距。现在只能怀疑一切了。因此就想到使用libyuv先自己做缩放然后,再送人编码器。

测试代码如下:

//缩放yuv到当前编码分辨率
    /*
    CVPixelBufferLockBaseAddress(imageBuf, kCVPixelBufferLock_ReadOnly);
    const uint8_t *src_y = CVPixelBufferGetBaseAddressOfPlane(imageBuf, 0);
    int src_stride_y = (int)CVPixelBufferGetBytesPerRowOfPlane(imageBuf, 0);
    const uint8_t *src_uv = CVPixelBufferGetBaseAddressOfPlane(imageBuf, 1);
    int src_stride_uv = (int)CVPixelBufferGetBytesPerRowOfPlane(imageBuf, 1);
    int src_width = (int)CVPixelBufferGetWidth(imageBuf);
    int src_height = (int)CVPixelBufferGetHeight(imageBuf);
    CVPixelBufferUnlockBaseAddress(imageBuf, kCVPixelBufferLock_ReadOnly);

    OSStatus status;
    CVPixelBufferRef pixelBuffer = NULL;
    int dst_width = _width;
    int dst_height = _height;
    status = CVPixelBufferCreate(kCFAllocatorDefault, dst_width, dst_height, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBuffer);
    CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
    int dst_stride_y = (int)CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
    int dst_stride_uv = (int)CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
    uint8_t *dst_y = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    uint8_t *dst_uv = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    int ret = NV12Scale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, src_height, dst_y, dst_stride_y, dst_uv, dst_stride_uv, dst_width, dst_height, kFilterBox);
    if (ret != 0) {
        ILog(@"[%@] encodeFrame scale fail, [%dx%d]->[%dx%d]",self, src_width, src_height, dst_width, dst_height);
    }
    CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);

但是,经过测试发现libyuv缩放编码后仍然有绿条。

问题又一次陷入了僵局,没有头绪。后来在用7yuv工具分析时,突然看到缩放前的yuv的真实数据宽度好像是704(其实是当时看错了),并不是720,这让问题又有了一丝希望。

冲着这个方向,怀疑是不是由于横竖屏旋转(横屏情况下是正常的)时,导致yuv的真实数据出现了问题。但是通过启动时直接设置为竖屏对比分析yuv数据,也是有问题,故排除了苹果内部旋转。其实这个验证是不严谨的,因为setSessionPreset时,只支持横屏的格式,比如,AVCaptureSessionPreset1280x720。而我们只是启动时将AVCaptureConnection的setVideoOrientation设置为了AVCaptureVideoOrientationPortrait这个,其内部应该还是做了旋转。严谨的方式应该是使用libyuv自己做旋转,才能排查。

但是在这个过程中突然发现yuv的有效数据是正常的,就是720宽,而跨距是768。开始由于对7yuv工具上边的标尺使用不当,后来发现点击标尺上的箭头可以看到当前的刻度值是多少,哎,悲剧呀。

采集模块出来的数据,有效数据的宽度正确,有效数据到跨距结束的部分,本来就应该被填充一些无效数据,可能就是绿色或黑色。这下问题排除是采集模块的问题了,这是只能又转向编码模块了。

编码模块排查

继续用7yuv分析yuv数据,发现一个细节yuv缩放后,180到184这个段的数据是纯绿色或纯黑色的,184到192的数据是不确定的,有绿色,有黑色。且184正好是8的倍数。怀疑是不是送入编码器的有效数据需要是8的倍数呢?

因此,尝试将手动缩放的分辩率改为184x320,即缩放后的yuv是184x320,进行测试,发现编码后还是存在绿条或黑条。

最后,想起了编码分辨率需要是8的倍数的说法,尝试将编码分辨率改为184x320,测试发现编码后的数据没有绿条了。这是由于
编码h264视频流的时候,h264的编码宏块大小16x16,帧内编码时宏块还拆成8x8子块。

总结

这个排查过程中,有以下几个收获:

  • 对跨距的理解更深刻了,之前只是知道一个yuv真实数据和填充数据的事情。
  • 对编码器的分辨率宽度设置一定是8的倍数或16的倍数,这个概念记忆深刻。
  • 对7yuv工具的使用更加熟悉。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一、视频采集输出数据格式YUV&RGB RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)...
    lyking阅读 2,038评论 0 3
  • 本文讲的是谈论关于Android视频编码的那些坑,Android的视频相关的开发,大概一直是整个Android生态...
    福later阅读 4,600评论 0 7
  • 夜莺2517阅读 128,155评论 1 9
  • 版本:ios 1.2.1 亮点: 1.app角标可以实时更新天气温度或选择空气质量,建议处女座就不要选了,不然老想...
    我就是沉沉阅读 7,451评论 1 6
  • 我是一名过去式的高三狗,很可悲,在这三年里我没有恋爱,看着同龄的小伙伴们一对儿一对儿的,我的心不好受。怎么说呢,高...
    小娘纸阅读 3,817评论 4 7

友情链接更多精彩内容