音视频开发之旅(64) - 部分android手机上编码的视频在ios上无法播放

1,297 阅读5分钟

目录

  1. 问题描述
  2. 问题分析
  3. 问题原因
  4. 问题解决
  5. 资料
  6. 收获

一、问题描述

用小米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: 
  1Baseline Profile:基本画质。支持I/P 帧,只支持无交错(Progressive)和CAVLC; 
  2Extended profile:进阶画质。支持I/P/B/SP/SI 帧,只支持无交错(Progressive)和CAVLC;(用的少) 
  3Main profile:主流画质。提供I/P/B 帧,支持无交错(Progressive)和交错(Interlaced), 也支持CAVLCCABAC 的支持; 
  4High profile:高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、 无损视频编码和更多的YUV 格式; 
   
   H.264 Baseline profileExtended profileMain profile都是针对8位样本数据、4:2:0格式(YUV)的视频序列。
   
  在相同配置情况下,High profileHP)可以比Main profileMP)降低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则是脚痛医脚的方案。

五、 资料

  1. H.264 and H.265 (HEVC)
  2. ffmpeg 常用命令
  3. H264编码profile & level控制
  4. Using ffprobe to get info from a file in a nice JSON format
  5. Android MediaCodec参数笔记_
  6. Android MediaCodec h264编码无法设为high profile

六、收获

通过本篇的学习

  1. 分析解决部分android设备上编码的视频,ios不能播放的问题
  2. 进一步熟悉Profile、Level的意义

感谢你的阅读
欢迎关注公众号“音视频开发之旅”,一起学习成长。
欢迎交流