目录
-
问题描述
-
问题分析
-
问题原因
-
问题解决
-
资料
-
收获
一、问题描述
用小米11 录制视频上传后,在iPhone的Safari浏览器或者应用的H5中无法播放,而android设备上的确实可以正常播放。
同样的操作,在一些其他android的手机上传的视频,在ios端却可以正常播放。
二、问题分析
拿到(能播放和不能播放)两个视频。分析下这两个视频有什么差异。
通过MediaInfo查看两个视频的基本信息
不能播放的视频
{
"@type": "Video",
"StreamOrder": "1",
"ID": "2",
"Format": "AVC",
"Format_Profile": "High",
"Format_Level": "6",
"Format_Settings_CABAC": "Yes",
"Format_Settings_RefFrames": "1",
}
能播放的视频
{
"@type": "Video",
"Format": "AVC",
"Format_Profile": "Main",
"Format_Level": "3.1",
"Format_Settings_CABAC": "Yes",
}
可以很明显地看到两个视频的Profile和Level不同。
通过ffmpeg把不能播放的视频进行转码,修改profile和level的值,进行本地快速验证。
ffmpeg -i input.mp4 -profile:v main -level 4.2 output.mp4
ffmpeg -i input.mp4 -profile:v high -level 5.1 output.mp4
ffmpeg -i input.mp4 -profile:v high -level 6 output.mp4
发现只有level大于等于6,在ios上就不能播放。
三、问题原因
我们先来看下Profile和Level是什么?
3.1 Profile
H.264有四种画质级别,分别是baseline, extended, main, high:
1、Baseline Profile:基本画质。支持I/P 帧,只支持无交错(Progressive)和CAVLC;
2、Extended profile:进阶画质。支持I/P/B/SP/SI 帧,只支持无交错(Progressive)和CAVLC;(用的少)
3、Main profile:主流画质。提供I/P/B 帧,支持无交错(Progressive)和交错(Interlaced), 也支持CAVLC 和CABAC 的支持;
4、High profile:高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、 无损视频编码和更多的YUV 格式;
H.264 Baseline profile、Extended profile和Main profile都是针对8位样本数据、4:2:0格式(YUV)的视频序列。
在相同配置情况下,High profile(HP)可以比Main profile(MP)降低10%的码率。
根据应用领域的不同,Baseline profile多应用于实时通信领域,Main profile多应用于流媒体领域,High profile则多应用于广电和存储领域。
维基百科上有对应的表:
3.2 level
level是一组特定的约束,表示一个profile所需的编解码性能。指定编解码器可能使用的最大图像分辨率、帧率和比特率。一个符合给定level的解码器必须能够解码为该level和所有低level编码的所有比特流。
在MediaCodecInfo中有定义各种类型的值
综上,我们可以知道Profile和Level是影响着视频编码的质量和效率。有些设备不支持高Profile、level的编码。所以需要综合考虑兼容性和视频质量。
通过查看代码发现,android端的视频编码时,仅对Profile做了阀值限制,Level没有判断处理,就像小米11手机对应解码器信息返回的Level是6,这样编码出来的视频在某些不支持的该level解码的设备上就无法解码播放。
3.3 扩展
另外通过查找资源,发现android在7.0之前的系统,编码时profile只能是Baseline,如果不是,系统会强制地设置为Baseline。猜测也是为了兼容性吧。虽然更高的压缩比带来很好的收益,但是兼容性(普适性)是优先考虑的点。
使用MediaCodec编码时,通常是这样设置的
先配置MediaCodecInfo的profile信息 然后赋给MediaFormat
codec.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
最后设置给MediaCodec
编码得到的码流都是baseline profile的
这是由于android sdk本身的限制导致的
ACodec.cpp里的对应处理如下:
if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) {
ALOGW("Use baseline profile instead of %d for AVC recording",
h264type.eProfile);
h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
}
在Android 7.0之后才移除了这段代码。也就是说要想使用MP要求Android系统版本在7.0及其以上。
四、问题解决方案
4.1 方案一:用户上传后先经过服务端的转码后,再下发给用户
用户上传的视频可能差别很大(封装格式、编码格式、采样率、码率、帧率、分辨率、YUV存储格式等等),有些播放器可能对某些视频支持的不太好。比如:有些设备不支持HEVC的解码、有些解码器不支持MPEG-4的编码格式、有些播放器只支持YUV420P的格式、有些播放器对于帧率很高的视频卡顿、有些播放器不支持播放4K等高清视频 等等
针对上述情况,一种比较通用的做法是。用户上传后先经过服务端的转码后,再下发给其他用户。进而保证视频在各种设备上的兼容性。
4.2 方案二、编码时的profie和level设置上线阀值
profile 最大值设置为 MediaCodecInfo.CodecProfileLevel.AVCProfileHigh
Level 最大值设置为 MediaCodecInfo.CodecProfileLevel.AVCLevel52
当从解码器中遍历的MediaCodecInfo.CodecProfileLevel的profie和level上述阀值时,用上述最大值。进而保证在各种设备的兼容性。
方案1是更加通用的解决方案,方案2则是脚痛医脚的方案。
五、 资料
-
H.264 and H.265 (HEVC)(https://help.shopmoment.com/article/135-video-formats-for-ios)
-
ffmpeg 常用命令(https://www.cnblogs.com/frost-yen/p/5848781.html)
-
H264编码profile & level控制(https://www.cnblogs.com/tinywan/p/6402007.html)
-
Using ffprobe to get info from a file in a nice JSON format(https://gist.github.com/nrk/2286511)
-
Android MediaCodec参数笔记(https://blog.csdn.net/matrix_laboratory/article/details/80929845)
-
Android MediaCodec h264编码无法设为high profile(https://www.codeleading.com/article/78273619345/)
六、收获
通过本篇的学习
-
分析解决部分android设备上编码的视频,ios不能播放的问题
-
进一步熟悉Profile、Level的意义
感谢你的阅读
关注公众号“音视频开发之旅”,回复“源码”,获取音视频开发相关代码,一起学习成长。
欢迎交流