音视频开发之旅(64) - 部分android手机编码的视频在ios上无法播放_base media / version 2-程序员宅基地

技术标签: 音视频开发之旅  音视频  android  ios  

目录

  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:
  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则是脚痛医脚的方案。

五、 资料

  1. H.264 and H.265 (HEVC)(https://help.shopmoment.com/article/135-video-formats-for-ios)

  2. ffmpeg 常用命令(https://www.cnblogs.com/frost-yen/p/5848781.html)

  3. H264编码profile & level控制(https://www.cnblogs.com/tinywan/p/6402007.html)

  4. Using ffprobe to get info from a file in a nice JSON format(https://gist.github.com/nrk/2286511)

  5. Android MediaCodec参数笔记(https://blog.csdn.net/matrix_laboratory/article/details/80929845)

  6. Android MediaCodec h264编码无法设为high profile(https://www.codeleading.com/article/78273619345/)

六、收获

通过本篇的学习

  1. 分析解决部分android设备上编码的视频,ios不能播放的问题

  2. 进一步熟悉Profile、Level的意义

感谢你的阅读

关注公众号“音视频开发之旅”,回复“源码”,获取音视频开发相关代码,一起学习成长。

欢迎交流

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011570979/article/details/122659469

智能推荐

Verilog 对assign和always的一点理解-程序员宅基地

文章浏览阅读1.3k次。assign 用于描述组合逻辑always@(敏感事件列表) 用于描述时序逻辑敏感事件 上升沿 posedge,下降沿 negedge,或电平敏感事件列表中可以包含多个敏感事件,但不可以同时包括电平敏感事件和边沿敏感事件,也不可以同时包括同一个信号的上升沿和下降沿,这两个事件可以合并为一个电平敏感事件。在新的verilog2001中“,”和“or”都可以用来分割敏感事..._assign能描述时序逻辑吗

Javascript 获取数组长度, 对象成员个数, 字符串数_js对象元素个数-程序员宅基地

文章浏览阅读8.7k次。1.应用场景获取数组长度, 对象成员个数, 字符串数. 2.学习/操作 TBD 3.问题/补充 TBD 4.参考https://www.cnblogs.com/sunnywindycloudy/p/7382226.html //js获取数组长度,对象成员个数、字符串数 后续补充......_js对象元素个数

Go ElasticSearch 游标查询Scroll_golang es 滚动查询-程序员宅基地

文章浏览阅读1.8k次。scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询。游标查询会取某个时间点的快照数据。 查询初始化之后索引上的任何变化会被它忽略。 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引视图一样。Go olivere elastic基础:golang中使用elasticsearch之olivere elastic汇总环境Go mod包:github.com/olivere/elastic/v7Elasticsearch版本:v7游标查询do, e_golang es 滚动查询

Spring的注解 @Bean用法-程序员宅基地

文章浏览阅读2.7w次,点赞21次,收藏65次。随着SpringBoot的流行,基于注解式开发的热潮逐渐覆盖了基于XML纯配置的开发,而作为Spring中最核心的bean当然也能够使用注解的方式进行表示。所以本篇就来详细的讨论一下作为Spring中的Bean到底都有哪些用法。@Bean 基础声明Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。S_@bean

用python创建窗口_win32gui_struct.packdev_broadcast_deviceinterface-程序员宅基地

文章浏览阅读2.5k次,点赞3次,收藏2次。利用win32库可以做到这点,直接上代码import win32api, win32con, win32gui import win32gui_struct import ctypes from ctypes import * GUID_DEVINTERFACE_USB_DEVICE = "{A5DCBF10-6530-11D2-901F-00C04FB951ED}" class..._win32gui_struct.packdev_broadcast_deviceinterface

IO标准库和系统调用接口_a short item count-程序员宅基地

文章浏览阅读167次。标准库接口fopen/fread/fwrite/fseek/fcloseFILE *fopen(const char *path, const char *mode);size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);int fseek(FILE *stream, long_a short item count

随便推点

ic启动器我的世界_我的世界HMCL启动器-程序员宅基地

文章浏览阅读958次。软件介绍我的世界HMCL启动器的最新版本,自定义您的游戏客户端玩家可以自己安装mod,修改功能,操作起来就会更加的方便,影响游戏的启动速度,因此我的世界HMCL启动器非常适合您,快来下载吧。我的世界HMCL启动器介绍我的世界HMCL启动器是为我的世界(Minecraft)玩家们精心准备的一款游戏启动器,能够快速启动游戏客户端,帮助玩家获得更好的游戏体验。HMCL启动器组最新版包含了Minecraf..._authlib-injector报错

SpringBoot2 | SpringBoot启动流程源码分析(二)-程序员宅基地

文章浏览阅读120次。版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本文链接:https://blog.csdn.net/woshilijiuyi/article/details/82350057微信公众号:吉姆餐厅ak学习更多源码知识,欢迎关注。SpringBoot2 | SpringBoot启动流程源码分析(一)SpringBoot2 | S...

Python异常模块与包-程序员宅基地

文章浏览阅读1.3k次,点赞44次,收藏19次。模块定义别名import 模块名 as 别名# 功能定义别名from 模块名 import 功能 as 别名注意:模块的导入一般都在开头的地方写好,再到下面写功能代码Python自定义模块是指用户自己编写的一个Python脚本文件,其中包含了一些函数、类或变量等,可以供其他Python程序使用。创建一个以.py为后缀的Python脚本文件,例如module.py。在脚本文件中定义函数、类或变量等。在其他Python程序中,通过import语句导入自定义模块,并使用其中的函数、类或变量等。

04-GeoServer WFS服务发布及参数设置_geoserver发布wfs-程序员宅基地

文章浏览阅读4.2k次,点赞2次,收藏12次。Web Feature Service服务(WFS)1. WFS服务简介1.1 WFS服务基础知识 WFS服务:Web Feature Service,网络要素服务,是指用户在分布式的环境下通过HTTP对地理要素进行插入,更新,删除,检索和发现服务。该服务根据HTTP客户请求返回要素级的GML(Geography Markup Language、地理标识语言)数据,并提供对要素的增加、修改、删除等事务操作,是对Web地图服务的进一步深入。WFS通过OGC Filter构造查询条件,支..._geoserver发布wfs

第一次接触Client-Server,使用CSocket的问题_mfc socket通信解决重复发送问题-程序员宅基地

文章浏览阅读453次。第一次使用CSocket,遇到的问题不少。比如:1. A客户端主动发送数据至服务器,且会一直进行。那么,服务端处理A时,就会影响到其他客户端的数据接收。则需要开一个线程来处理A发送的数据,就会出现问题:创建线程时该传递什么参数?如何传?有试过,直接将Receive(&ndata,sizeof(ndata)-1); ndata数组直接传递过去,但是在线程那边不太好接收,因为线程时在socke_mfc socket通信解决重复发送问题

正确保存你的Maple文件-程序员宅基地

文章浏览阅读1.4k次。在使用Maple时可以快速地输入各种数学样式和运算符号进行工程计算,计算后需要将文件进行保存,下面介绍Maple软件保存文件的方法。   1.要保存文件时,点击“文件”菜单“exit”或快捷键“alt+F4”或点击窗口右上角×,这时系统要提示:是否存盘?点击“是”,则自动存盘。如果是第一次使..._maple文件保存什么格式