ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

一个偶现的MP4录制问题解决过程记录

2022-02-24 23:00:27  阅读:260  来源: 互联网

标签:int 录制 0x00 0x01 0x03 偶现 MP4 0xff unsigned


文章目录

前言

LG发现了一个偶现的录制问题,为了上线必须解决。这里记录一下问题解决的过程。

问题的现象是这样的,偶然出现录制的视频会出现无法播放的问题,而且那个视频有些手机能播,有些不能播,很诡异。

解决思路

由于使用的是MediaPlayer播放出现问题,正好在我自己编译的Pixel 3(Android 11)上也无法播放,那就在上面调试查看出错的地方吧。找到报错的地方后,就可以定位到是解析MP4文件中具体哪块数据的时候出现的问题。然后再去看生成这块数据的代码,阅读理解相关逻辑,看看哪里出错了。暂时就想到这,开始行动。

1. 定位MP4中哪块数据出现问题

由于MP4数据还是很多的,要一个个排查不现实。所以就根据报错的日志来定位问题。

a. 查看adb错误日志

$ adb logcat *:E 

02-24 17:59:30.569   352   352 E Utils   : did not find width and/or height
02-24 17:59:30.570   352   352 E Utils   : did not find width and/or height
02-24 17:59:30.583   354  1906 E Utils   : b/23680780
02-24 17:59:30.584  1878  1896 E MediaPlayerNative: error (1, -22)
02-24 17:59:30.634  1878  1878 E MediaPlayer: Error (1,-22)

发现错误出现在 E Utils : b/23680780

根据进程号354,发现是mediaserver进程

$ adb shell ps | grep 354
media           354      1   93744  26112 binder_thread_read  0 S mediaserver

b. 定位AOSP报错的源码位置

通过在AOSP源码搜索b/23680780定位到源文件位置在/home/kevin/ExtraSpace/aosp/frameworks/av/media/libstagefright/Utils.cpp

这里面有好几处都打印了这个日志,那就没什么好说的,把全部都设置上断点

然后调试。点击播放有问题的MP4,发现断点停在解析hvcc的地方
请添加图片描述

c. 用gdb打印MP4中的hvcc

其实不用gdb打印,也可以通过MP4分析工具查看,只是我这里在调试,所以就直接用gdb输出了。至于MP4分析工具还是用挺多,我这里讲一下Linux上。推荐使用MediaParser,这个原来的在qt6上编译有问题,我修复了。需要的话自己下载编译就行了。

好了回归正题。为了后续方便,这里称呼:

good表示正常的mp4的hvcc,bad表示有问题的mp4的hvcc

将两个打印出来,方便后续对比。

(gdb) x/110x data
good hvcC

0xee100380:	0x01	0x01	0x40	0x00	0x00	0x00	0x80	0x00
0xee100388:	0x00	0x00	0x00	0x00	0x7b	0xf0	0x00	0xfc
														(numOfArrays==3)
0xee100390:	0xfd	0xf8	0xf8	0x00	0x00	0x0f	0x03	0x20
0xee100398:	0x00	0x01	0x00	0x17	0x40	0x01	0x0c	0x01

bad hvcC

0xea580e10:	0x01	0x00	0x01	0x03	0x00	0x00	0x00	0x18
0xea580e18:	0x00	0x10	0x00	0x00	0x2d	0x00	0x00	0x00
														(numOfArrays==255溢出)
0xea580e20:	0xff	0xff	0xff	0xff	0xff	0xff	0xff	0x20
0xea580e28:	0x00	0x01	0x00	0x18	0x40	0x01	0x0c	0x01

bad2 hvcC(这是后面又复现的有问题的MP4文件)
0xe6d40c10:	0x01	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0xe6d40c18:	0x00	0x00	0x00	0x00	0x00	0x49	0x00	0xe7
														(numOfArrays==0)
0xe6d40c20:	0xff	0xa4	0x00	0x00	0x00	0x00	0x00	0x20
0xe6d40c28:	0x00	0x01	0x00	0x18	0x40	0x01	0x0c	0x01

d. 根据ISO/IEC 14496-15文档,阅读hvcc

这里面对比发现bad.mp4的hvcc的确有问题。

这是ISO文档中对应的数据组成

aligned(8) class HEVCDecoderConfigurationRecord { 
    unsigned int(8) configurationVersion = 1; 

    //1 byte (第2个byte)
    unsigned int(2) general_profile_space; 
    unsigned int(1) general_tier_flag; 
    unsigned int(5) general_profile_idc; 

    //4 bytes (第3~6个byte)
    unsigned int(32) general_profile_compatibility_flags; 

    //6 byte (第7~12个byte)
    unsigned int(48) general_constraint_indicator_flags; 

    //(第13个byte)
    unsigned int(8) general_level_idc; 

    //(第14~15个byte)
    bit(4) reserved = ‘1111’b; 
    unsigned int(12) min_spatial_segmentation_idc;

    //(第16个byte)
    bit(6) reserved = ‘111111’b; 
    unsigned int(2) parallelismType; 

    //(第17个byte)
    bit(6) reserved = ‘111111’b; 
    unsigned int(2) chroISO/IEC 23008-2 ma_format_idc; 

    //(第18个byte)
    bit(5) reserved = ‘11111’b; 
    unsigned int(3) bit_depth_luma_minus8; 

    //(第19个byte)
    bit(5) reserved = ‘11111’b; 
    unsigned int(3) bit_depth_chroma_minus8; 

    //(第20~21个byte)
    bit(16) avgFrameRate; 

    //(第22个byte)
    bit(2) constantFrameRate; 
    bit(3) numTemporalLayers; 
    bit(1) temporalIdNested; 
    unsigned int(2) lengthSizeMinusOne; 

    //(第23个byte)
    unsigned int(8) numOfArrays; 

    for (j=0; j < numOfArrays; j++) { 
        //1个byte
        bit(1) array_completeness; 
        unsigned int(1) reserved = 0; 
        unsigned int(6) NAL_unit_type; 

        //2个byte
        unsigned int(16) numNalus; 

        for (i=0; i< numNalus; i++) { 
            //2个byte
            unsigned int(16) nalUnitLength; 

            bit(8*nalUnitLength) nalUnit; 
        }
    } 
}

根据ISO文档,解析hvcc数据,发现bad在[general_profile_space, numOfArrays]之间是不正常的。

numOfArrays应该是3(vps,sps,pps,一共3个)

good

0xee101350:	0x01	0x01	0x40	0x00	0x00	0x00	0x80	0x00
0xee101358:	0x00	0x00	0x00	0x00	0x7b	0xf0	0x00	0xfc
													  (numOfArrays)(32,vps)
0xee101360:	0xfd	0xf8	0xf8	0x00	0x00	0x0f	0x03	0x20
           (numNalus == 1) (nalUnitLength==23)(nalUnit
0xee101368:	0x00	0x01	0x00	0x17	0x40	0x01	0x0c	0x01
0xee101370:	0xff	0xff	0x01	0x40	0x00	0x00	0x03	0x00
0xee101378:	0x80	0x00	0x00	0x03	0x00	0x00	0x03	0x00
								)(33,sps)  (numNalus == 1)(nalUnitLength==33)
0xee101380:	0x7b	0xac	0x09	0x21	0x00	0x01	0x00	0x21
			(nalUnit
0xee101388:	0x42	0x01	0x01	0x01	0x40	0x00	0x00	0x03
0xee101390:	0x00	0x80	0x00	0x00	0x03	0x00	0x00	0x03
0xee101398:	0x00	0x7b	0xa0	0x02	0x80	0x80	0x2d	0x16
0xee1013a0:	0x5a	0xe4	0xb2	0xb6	0x6b	0x95	0x44	0xd8
				) (34,pps)(numNalus == 1)(nalUnitLength==8)(nalUnit
0xee1013a8:	0x02	0x22	0x00	0x01	0x00	0x3d	0x44	0x01
														)
0xee1013b0:	0xc0	0xe3	0x0f	0x03	0x32	0x40


bad
				  (不正常的数据
0xea580a90:	0x01	0x00	0x01	0x03	0x00	0x00	0x00	0x18
0xea580a98:	0x00	0x10	0x00	0x00	0x2d	0x00	0x00	0x00
														 (numOfArrays))(32,vps)
0xea580aa0:	0xff	0xff	0xff	0xff	0xff	0xff	0xff	0x20
		   (numNalus==1)(nalUnitLength==24)(nalUnit
0xea580aa8:	0x00	0x01	0x00	0x18	0x40	0x01	0x0c	0x01
0xea580ab0:	0xff	0xff	0x01	0x60	0x00	0x00	0x03	0x00
0xea580ab8:	0x00	0x03	0x00	0x00	0x03	0x00	0x00	0x03
										)  (33,sps)(numNalus == 1)(nalUnitLength==41
0xea580ac0:	0x00	0x96	0xac	0x09	0x21	0x00	0x01	0x00
				) (nalUnit
0xea580ac8:	0x29	0x42	0x01	0x01	0x01	0x60	0x00	0x00
0xea580ad0:	0x03	0x00	0x00	0x03	0x00	0x00	0x03	0x00
0xea580ad8:	0x00	0x03	0x00	0x96	0xa0	0x05	0x02	0x01
0xea580ae0:	0x69	0x63	0x6b	0x92	0x4c	0x9a	0xe5	0x9c
0xea580ae8:	0x02	0x00	0x00	0x07	0xd2	0x00	0x00	0x9c
						) (34,pps) (numNalus==1)(nalUnitLength==7)(nalUnit
0xea580af0:	0x68	0x10	0x22	0x00	0x01	0x00	0x07	0x44
														)
0xea580af8:	0x01	0xe0	0x76	0xb0	0x26	0x40

2. 在代码中定位问题

定位代码问题还是花了不少时间。具体源码分析这里就不展开了,参考意义不大。过程概括起来就以下这些步骤

  1. 阅读相关代码,理解numOfArrays是如何生成并写入到MP4中的,发现这块并没有什么问题

  2. 怀疑是数据问题。让测试复现,拿到原始数据,发现生成的MP4可以播放。(测试用了差不多2天才复现,辛苦了)

  3. 怀疑是内存相关的错误。(刚开始没想到这个,后来边阅读源码边思考,终于想到了可能是这个问题)

    1. 读取未初始化过的变量
    2. 野指针/悬垂指针读写
    3. 错误的指针类型转换
    4. 从已分配内存块的尾部进行读/写(数组等类型读写越界)
    5. 不匹配地使用 malloc/new/new[] 和 free/delete/delete[]
    6. 等等
  4. 于是先尝试使用valgrind做检查,发现读取未初始化过的变量,并且这块就是hvcc生成赋值的地方!!!
    在这里插入图片描述

    上面的圈起来的变量和另外一个变量(没截图),没有初始化。(其实这里圈起来的最后一个变量错了,但是不是问题的原因)

  5. 应该就是这里了,但是是什么样的值才会导致问题呢?仔细阅读了代码,并测试验证后。确定是uint8 m_spsCount = 255(0xff)的时候会出现问题

总结

花时间比较多,原因还是因为没能第一时间想到可能是读取未初始化变量的问题,经验不足导致。不过后面排查完其它错误之后终于发现了真正的错误原因。

通过这个问题的解决,并结合以往解决偶现问题的经验。偶现的问题,要么就是某些特殊输入导致,要么就是进程在偶现问题的那个时刻的状态(也就是依赖的相关变量)有问题,导致程序并没按照看起来的那样执行。

总的来说遇到问题,不断的排除可能的原因,那离真正的原因就不远了。

标签:int,录制,0x00,0x01,0x03,偶现,MP4,0xff,unsigned
来源: https://blog.csdn.net/lylwo317/article/details/123122921

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有