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工具的使用更加熟悉。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

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