Showing Posts From
Avplayer
HLS及M3U8介绍
背景 MP4是我们常见的视频格式,往往我们在播放服务器视频时直接就是请求的MP4视频源。但其实这样并不好,MP4头文件[ftyp+moov]较大,初始化的播放需要下载完整的头文件并进行解析,之后再下载一定长度的可播视频片段才能进行播放。另外随着视频尺寸的增大头文件也会不断变大,这个初始播放时间也会更长。针对这种情况需要一种能加快视频初始解析的方法,HLS就是苹果提出的用于解决这种问题的方案。HLS HLS为HTTP Live Streaming的缩写,是由苹果公司提出的基于HTTP的流媒体网络传输协议,它可以同时支持直播和点播,还支持多清晰度、音视频双轨、字幕等功能。它的原理是将一整条视频分成多段小的视频,完整的播放是由这一个个片段拼接而成的。 HLS在移动端使用很广泛,当前支持HLS协议的客户端有:iOS 3.0及以上,AVPlayer原生支持HLS Android 3.0及以上 Adobe Flash Player 11.0及以上它的大致原理是这样的:1、采集音视频 2、在服务器编码音视频 3、编码后以MPEG-2的传输串流形式交由切片器(Stream Segmenter) 4、切片器创建索引文件和ts播放列表,索引文件用于指示音视频位置,ts为真实的多媒体片段 5、将上一步资源放到HTTP服务器上 6、客户端请求该索引文件进行播放,可以通过索引文件找到播放内容 参考资料:HTTP Live Streaming Document M3U8 实现HLS的一个关键步骤是上面的第四步,即索引文件和ts播放列表的组织。这里用到的就是M3U8格式。M3U8是Unicode版本的M3U,8代表使用的是UTF-8编码,M3U和M3U8都是多媒体列表的文件格式。 接下来我们以一条WWDC里的视频为例,看下M3U8格式是什么样子的,下文展示的并非M3U8格式所有字段,但会包含常用的字段,也足以帮助我们理解M3U8这一格式。 播放页面为:https://developer.apple.com/videos/play/wwdc2019/507 ,通过Charles进行抓包,我们可以得到视频播放过程中的M3U8文件。在分析这个路径格式前,我们需要知道 M3U8 是有两种格式的,一种是作为主播放列表(Master Playlist)存在,它里面包含了音视频、字幕的一些说明和路径,主列表指示的路径是另一个M3U8文件,即另一个格式,作为播放存在的,它里面也有路径,指示的是片段(ts)文件,片段文件是真正的多媒体内容。 看抓包内容,hls_vod_mvp.m3u8为主列表文件,上面的0640.m3u8为视频列表文件。 M3U8格式说明 有时做测试,或者一些特殊情况时我们可能需要手动修改M3U8文件内容,所以需要对它的格式有一定的了解。该格式的定义写在RFC 8216号文件里,以下是一些注意事项:M3U8文件必须以UTF-8进行编码,不能使用 Byte Order Mark(BOM)字节序, 不能包含 utf-8 控制字符(U+0000 ~ U_001F 和 U+007F ~ u+009F) M3U8文件内容的每一行要么是空行,要么是一个URI,要么是以#开头的字符串,不能出现空白字符。 内置标签都是#EXT开头的字符串,大小写敏感。 URI为内容路径,可以是相对路径也可以是绝对路径Master M3U8 列表文件主M3U8索引文件,一般用于指定多个索引源。我们先分析下该主m3u8文件hls_vod_mvp.m3u8的内容,它的头部是这样的 头部格式 #EXTM3U #EXT-X-VERSION:7 #EXT-X-INDEPENDENT-SEGMENTS#EXTM3U表明该文件是一个M3U格式,所有的M3U格式文件都应该把该内容放置到第一行。 #EXT-X-VERSIOn指示播放列表的兼容版本,当前为7。 #EXT-X-INDEPENDENT-SEGMENTS该标签表明对于一个媒体片段中的所有媒体样本均可独立进行解码,而无须依赖其他媒体片段信息。 字幕格式 再往下的内容是一些字幕说明,字幕内容不是必须的。 #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/eng/prog_index.m3u8" #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subsC",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/engc/prog_index.m3u8"#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Japanese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="jpn",URI="subtitles/jpn/prog_index.m3u8" #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subsC",NAME="Japanese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="jpn",URI="subtitles/jpnc/prog_index.m3u8"#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Chinese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="zho",URI="subtitles/zho/prog_index.m3u8" #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subsC",NAME="Chinese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="zho",URI="subtitles/zhoc/prog_index.m3u8"#EXT-X-MEDIA用于指定相同内容的多语言媒体列表资源。 TYPE为资源类型,可选内容有:AUDIO、VIDEO、SUBTITLES、CLOSED-CAPTIONS。 上面内容设置的是TYPE=SUBTITLES,即为字幕类型。 GROUP-ID为多语言翻译所属组,为必选参数 NAME为翻译流可读的描述信息,该值对应AVMediaSelectionOption的displayName。 DEFAULT,AUTOSELECT,FORCED为三个BOOL值分别对应如果缺少必要信息时是否默认选中该翻译流,用户没有显示设置时播放该播放流,FORCED只针对字幕类型有效,用于标记当前自动选择该翻译流。 LANGUAGE用于指定语言类型,它是根据[ISO 639 语言码](https://www.w3.org/WAI/ER/WD-AERT/iso639.htm “ISO 639 语言码”)标准设置的。系统默认的播放器在选择字幕时,展示的字幕列表名称是根据这个值设定的。 URI为该资源的定位信息,在这里其对应的是一条字幕的M3U8文件。subtitles/eng/prog_index.m3u8是一个相对路径, 通过以上信息,我们可以分析出上面内容的含义为:当前视频支持三种字幕:英文,日文,中文。但每种语言都有两条EXT-X-MEDIA信息,他们的区别是分组不同,一个在subs分组,一个在subsC分组。为啥有两个分组,这个后面再说。 视频格式 再往下看,为视频内容的索引: #EXT-X-STREAM-INF:BANDWIDTH=827299,AVERAGE-BANDWIDTH=747464,CODECS="avc1.64001f,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="program_audio",SUBTITLES="subs" 0640/0640.m3u8 #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=360849,AVERAGE-BANDWIDTH=320932,CODECS="avc1.64001f",RESOLUTION=640x360,URI="0640/0640_I-Frame.m3u8"EXT-X-STREAM-INF:该属性指定了一个备份源,即视频播放路径和一些视频的信息,以下是对应内容的配置: BANDWIDTH为峰值比特率, 827299,为827299bit/s,即最高峰值时每秒消耗流量101KB。 AVERAGE-BANDWIDTH为平均比特率,747464 CODECS为编码信息,avc1.64001f,mp4a.40.2,avc代表的是h264编码格式,后面的64001f,是由16进制表示的编码参数,64,00,1f分别代表三个不同的参数值。mp4a是一种音频编码格式,后面的40.2代表音频的编码参数。 RESOLUTION为视频分辨率,当前一条视频源分辨率为640x360。 FRAME-RATE为最大帧率,29.970 代表当前播放的最大帧率为每秒29.970帧。 AUDIO是音频所在组,program_audio为对应音频组的名称。 SUBTITLES指示对应的字幕分组,subs为对应字幕组的名称。上面的字幕信息有个GROUP-ID,该值与之对应。 URI为内容路径,0640/0640.m3u8对应的就是该视频源的m3u8文件路径。这个可以在抓包信息里看到。 在EXT-X-STREAM-INF下面是EXT-X-I-FRAME-STREAM-INF,表示播放列表文件中包含的多媒体资源的I帧(关键帧)。因为I帧只是一个画面,所以它不包含音频内容,其余参数跟视频内容格式一致。 在之后就是对应不同分别率的视频源,1920x1080、1280x720、960x540、480x270,因为HLS会根据网络情况自行切换清晰度,所以一般会准备多个清晰度以供选择。根据抓包数据分析,播放的第一个片段是640清晰度的,之后的第2-8个片段为480清晰度,再之后又切换到了640清晰度。 音频格式 再往下看是对应音频的索引 #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="program_audio",LANGUAGE="eng",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="audio1/audio1.m3u8"#EXT-X-MEDIA上面出现过,为多语言没提列表。 TYPE=AUDIO,这次类型为音频。 GROUP-ID为分组ID,对应EXT-X-STREAM-INF里的AUDIO内容。 URI=audio1/audio1.m3u8对应音频路径。 不同编码格式的备用源 在该主M3U8文件中我们还能看到一条640分辨率的视频源,它与上面的640分辨率还不一样,它的内容是这样的: #EXT-X-STREAM-INF:BANDWIDTH=1922391,AVERAGE-BANDWIDTH=1276855,VIDEO-RANGE=SDR,CODECS="hvc1.2.4.H150.B0,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="program_audio_0",SUBTITLES="subsC" 0640c/prog_index.m3u8 #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=1922391,AVERAGE-BANDWIDTH=1276855,CODECS="hvc1.2.4.H150.B0",RESOLUTION=640x360,URI="0640c/iframe_index.m3u8"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="program_audio_0",LANGUAGE="eng",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="audioc/prog_index.m3u8"CODECS编码格式为hvc1.2.4.H150.B0,mp4a.40.2,音频编码格式没变,但视频编码格式变了。hvc1是HEVC(H265)编码格式里的一种,它是由苹果推出的新一代视频编码格式,因为兼容性问题很多客户端还无法解析该格式,所以并不是很普及,该格式的视频源出现在这里应该是一种备用。对比相同分辨率的两条内容,还能发现hvc1格式会比avc1格式比特率更高,这说明相同分辨率下hvc1的内容更大,avc1的压缩比更高。 对应hvc1格式的视频源,它的字幕内容分组和音频内容分组也都变了,这也是为什么上面的字幕同一语种会有两份,他们分别对应avc1和hvc1格式的视频源。 M3U8的主列表就这些内容了,该条内容的音视频是分开处理的,其实也可以合在一起。 包含媒体资料的m3u8文件 以0640.m3u8这个文件为例 #EXTM3U #EXT-X-VERSION:4 #EXT-X-TARGETDURATION:7 #EXT-X-MEDIA-SEQUENCE:1 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:6.006, 0640_00001.ts #EXTINF:6.006, 0640_00002.ts #EXTINF:6.006, 0640_00003.ts .... #EXT-X-ENDLIST#EXTM3U,#EXT-X-VERSION,分别为M3U文件头和兼容版本号,这种格式是早期的所以版本号比主文件低一些。 EXT-X-TARGETDURATION代表每个播放片段的最大时长,7,代表7秒,该目录下的片段不能超过7s。 EXT-X-MEDIA-SEQUENCE代表播放列表的第一个片段序号,1,代表播放片段是从1开始的。 #EXTINF代表片段的时长,6.006表示当前片段为6.006s。视频总时长的信息是通过该值累加获取的。 0640_00001.ts为片段的相对路径,ts文件代表一段视频或者音频,它可以是ts,mp4,aac等格式。因为前面已经指定了从1开始,所以这里序号是0640_00001。 #EXT-X-ENDLIST为媒体内容的结束标识,因为m3u8即可以表示点播也可以表示直播,区分点播还是直播就看文件末尾是否有这个标识符。如果没有的话就代表直播,播放会一直持续下去。 音频文件audio1.m3u8,字幕文件pro_index.m3u8的内容也是类似的,区别在于他们的切片内容一个是acc的音频文件,一个是webvtt的字幕文件。 包含切片内容的M3U8也可以作为独立的视频链接存在,这时切片内容就需要同时包含音视频内容了。 文件加密 HLS协议支持加密,如果索引文件中包含了一个密钥文件的信息,那接下来的媒体文件就必须使用密钥解密后才能解密打开了。当前的 HLS 支持使用16-octet 类型密钥的 AES-128 加密。这个密钥格式是一个由这在二进制格式中的16个八进制组的数组打包而成的。 加密的配置模式通常包含三种: 模式一:允许你在磁盘上制定一个密钥文件路径,切片器会在索引文件中插入存在的密钥文件的 URL。所有的媒体文件都使用该密钥进行加密。 模式二:切片器会生成一个随机密钥文件,将它保存在指定的路径,并在索引文件中引用它。所有的媒体文件都会使用这个随机密钥进行加密。 模式三:每 n 个片段生成一个随机密钥文件,并保存到指定的位置,在索引中引用它。这个模式的密钥处于轮流加密状态。每一组 n 个片段文件会使用不同的密钥加密。 参考:HLS-iOS视频播放服务架构深入探究(一)
AVPlayer支持的视频格式
发现很多人对视频格式存在一些误解,之前写的一篇文章讲AVPlayer的支持格式也有一些问题,所以这里单独出一篇文章讲一下,希望大家能明白。基本概念 一个在线视频能够播放,大致是经过了如下步骤:可以总结为:拉数据->解协议->解封装->音视频解码->音视频同步->播放。 下面就针对这几个概念一一做下解释: 播放协议 一般点播采用HTTP,而直播的话,大部分还是采用RTMP或者私有协议,原因是延时会比较小,RTMP本身也是为了直播设计的。常见的流媒体协议:简写 全称 推出机构 目前使用领域HLS HTTP Live Streaming 苹果 多应用于苹果RTP 实时传输协议 IETF 范围较广RTCP 实时传输控制协议 IETF 范围较广RTSP 实时串流协议 RealNetworks等 范围较广RTMP 实时消息协议 Adobe 较流行MMS 串流媒体协议 Microsoft 范围较广RTP/RTSP/RTCP的区别RTSP发起/终结流媒体、RTP传输流媒体数据 、RTCP对RTP进行控制,同步。HLS(Http Live Streaming) HLS是苹果推出,实现的基于HTTP的流媒体传输协议: 优点: 1、通过m3u8索引文件可实现针对当前浏览设备的智能选择播放源, 2、通过m3u8索引文件可实现添加备份索引文件,防止服务器崩溃视频播放失败 3、和http视频一样 不需要太多服务器额外配置 缺点: 1、并非真正实时视频,30s左右时间差 2、需要视频处理 3、因为需要请求索引文件(ts视频文件)请求次数相对较多,对服务器负载较大 视频编码 所谓视频编码方式就是指通过特定的压缩技术,将某个视频格式的文件转换成另一种视频格式文件的方式。它是一种为了减少视频体积,同时保证画面质量的压缩技术,常见的视频编码有:名称 推出机构 推出时间 目前使用领域HEVC(H.265) MPEG/ITU-T 2013 研发中H.264 MPEG/ITU-T 2003 各个领域MPEG4 MPEG 2001 不温不火MPEG2 MPEG 1994 数字电视XviD OpenDivX 2002 流行VP9 Google 2013 研发中VP8 Google 2008 不普及VC-1 Microsoft Inc. 2006 微软H.264H.264又叫AVC,是国际标准化组织(ISO)和国际电信联盟(ITU)共同提出的继MPEG4之后的新一代数字视频压缩格式,它集合了H.263和MPEG4的优点,拥有更高的数据压缩比。在同等的图像质量条件下,H.264的数据压缩比能比H.263高2倍,比MPEG-4高1.5倍。 也是目前苹果支持最好的编码格式。 音频编码 和视频编码类似,音频编码的作用是减少音频中的冗余,同时在保证一定音频质量的条件下作的压缩处理。名称 推出机构 推出时间 目前使用领域AAC MPEG 1997 流媒体AC-3 Dolby Inc. 1992 DVD, 数字电视MP3 MPEG 1993 各个领域(旧)WMA Microsoft Inc. 1999 WindowsAACAAC(高级音频编码技术 Advanced Audio Coding),出现于1997年,是基于MPEG-2的音频编码技术。由Fraunhofer IIS、杜比、苹果、AT&T、索尼等公司共同开发,以取代mp3格式。2000年,MPEG-4标准出台,AAC从新整合了其特性,故现又称MPEG-4 AAC,即m4a。苹果的CoreAudio对AAC有较好的支持。封装格式 我们常见的视频格式MP4, AVI, RMVB, 3GP, MKV都是指视频的封装格式,大部分情况就是视频文件的后缀。常见的组合方式有:名称 简介 常用编码格式 扩展名Flash Video 由Adobe Flash延伸出来的的一种流行网络视频封装格式。随着视频网站的丰富,这个格式已经非常普及。 H.264+MP3 flvAVI(Audio Video Interleave) 比较早的AVI是微软开发的。其含义是Audio Video Interactive,就是把视频和音频编码混合在一起存储。AVI也是最长寿的格式,已经存在10余年了,虽然发布过改版(V2.0于1996年发布),但已显老态。AVI格式上限制比较多,只能有一个视频轨道和一个音频轨道(现在有非标准插件可加入最多两个音频轨道),还可以有一些附加轨道,如文字等。AVI格式不提供任何控制功能。 Xvid+MP3 aviWMV(Windows Media Video) 同样是微软开发的一组数字视频编解码格式的通称,ASF(Advanced Systems Format)是其封装格式。ASF封装的WMV档具有“数字版权保护”功能。 VC-1+WMA wmv/asfMPEG(Moving Picture Experts Group) 是一个国际标准化组织(ISO)认可的媒体封装形式,受到大部分机器的支持。其存储方式多样,可以适应不同的应用环境。MPEG-4档的档容器格式在Part 1(mux)、14(asp)、15(avc)等中规定。MPEG的控制功能丰富,可以有多个视频(即角度)、音轨、字幕(位图字幕)等等。MPEG的一个简化版本3GP还广泛的用于准3G手机上。 H.264+AAC,H263+AAC dat(VCD),vob(DVD), mp4, 3gpMatroska 是一种新的多媒体封装格式,这个封装格式可把多种不同编码的视频及16条或以上不同格式的音频和语言不同的字幕封装到一个Matroska Media档内。它也是其中一种开放源代码的多媒体封装格式。Matroska同时还可以提供非常好的交互功能,而且比MPEG更方便、强大。 各种编码格式的组合 mkvReal Video Real Media(RM) 是由RealNetworks开发的一种档容器。它通常只能容纳Real Video和Real Audio编码的媒体。该档带有一定的交互功能,允许编写脚本以控制播放。RM,尤其是可变比特率的RMVB格式,没有复杂的Profile/Level,制作起来较H.264视频格式简单,非常受到网络上传者的欢迎。此外很多人仍有RMVB体积小高质量的错误认知,这个不太正确的观念也导致很多人倾向使用rmvb,事实上在相同码率下,rmvb编码和H.264这个高度压缩的视频编码相比,体积会较大。 RealVideo+RealAudio rm/rmvbQuickTime File Format 是由苹果公司开发的容器。1998年2月11日,国际标准化组织(ISO)认可QuickTime文件格式作为MPEG-4标准的基础。QuickTime可存储的内容相当丰富,除了视频、音频以外还可支持图片、文字(文本字幕)等。 H.264+AAC mov, qtMP4 mp4格式是H.264编码指定使用的标准封装格式,3GP是MP4格式的一种简化版本,减少了储存空间和较低的频宽需求,让手机上有限的储存空间可以使用。 实际上这些封装格式对应的音频视频编码格式也不是固定的,就拿MP4来说,常见的MP4是由H.264+AAC封装,但是也由Xvid+AAC编码的可能。如果解码器不支持Xvid,则可能会出现无法播放,或者播放播放过程有声音无画面的情况。 苹果支持哪些音视频编码格式 我们可以在手机介绍界面,找到手机支持的视频格式iPhone - Compare Models - Apple 这里我们可以找到对应iPhone7支持的视频编码格式:Video formats supported: HEVC, H.264, MPEG-4 Part 2, and Motion JPEGHEVC 又叫H.265,iOS11+A9芯片才开始对HEVC的支持,iPhone6s及以前的设备不支持HEVC解码。 音频格式用红框标出来了,内容较多,可自行对比。 AVPlayer支持哪些视频格式 苹果设备支持音视频格式并不是就代表AVPlayer也支持那么多格式,确定AVPlayer的支持格式,我们可以查看AVKit中的一个API: //展示当前支持的音视频格式 let asset = AVURLAsset.audiovisualTypes() //打印asset可以得到(已经转过展示格式) asset type ( "audio/aacp", "video/3gpp2", "audio/mpeg3", "audio/mp3", "audio/x-caf", "audio/mpeg", "video/quicktime", "audio/x-mpeg3", "video/mp4", "audio/wav", "video/avi", "audio/scpls", "audio/mp4", "audio/x-mpg", "video/x-m4v", "audio/x-wav", "audio/x-aiff", "application/vnd.apple.mpegurl", "video/3gpp", "text/vtt", "audio/x-mpeg", "audio/wave", "audio/x-m4r", "audio/x-mp3", "audio/AMR", "audio/aiff", "audio/3gpp2", "audio/aac", "audio/mpg", "audio/mpegurl", "audio/x-m4b", "application/mp4", "audio/x-m4p", "audio/x-scpls", "audio/x-mpegurl", "audio/x-aac", "audio/3gpp", "audio/basic", "audio/x-m4a", "application/x-mpegurl" )还有一个方式用来判断当前格式是否可播: //An extended MIME type string such as video/3gpp2; codecs="mp4v.20.9, mp4a.E1" or audio/aac; codecs="mp4a.E1". let playable: Bool = AVURLAsset.isPlayableExtendedMIMEType("video/3gpp2; codecs=\"mp4v.20.9, mp4a.E1\"")这里的MIMEType可以在这里找到 System-Declared Uniform Type Identifiers 总结 由此我们可以下一个总结,AVPlayer支持的: 视频编码格式:H.264、HEVC(iPhone7及以后设备)、MPEG-4。 视频格式(封装格式):.mp4、.mov、.m4v、.3gp、.avi等。 如果想支持更多的视频格式,可以使用使用第三方的框架,常用的视频编码和解码框架有VLC和ffmpeg。 参考文献 视频文件格式 - 维基百科,自由的百科全书 音频编码格式的比较 - 维基百科,自由的百科全书
AVPlayer详解系列(一)参数设置
最近工作内容基本都是围绕视频播放展开的,从AVPlayer到IJKPlayer,期间遇到挺多问题,趟了很多bug,也总结了一些心得。对AVPlayer了解的更多一些,因为涉及点比较多,所以打算做一个系列详尽的写一下这部分内容。希望大家多多支持,有问题的地方欢迎指正。思维导图 先来一张思维导图,作为这篇文章的目录索引:为什么使用AVPlayer: 首先在iOS平台使用播放视频,可用的选项一般有这四个,他们各自的作用和功能如下:使用环境 优点 缺点MPMoviePlayerController MediaPlayer 简单易用 不可定制AVPlayerViewController AVKit 简单易用 不可定制AVPlayer AVFoundation 可定制度高,功能强大 不支持流媒体IJKPlayer IJKMediaFramework 定制度高,支持流媒体播放 使用稍复杂由此可以看出,如果我们不做直播功能AVPlayer就是一个最优的选择。 另外AVPlayer是一个可以播放任何格式的全功能影音播放器 支持视频格式: WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等。 支持音频格式:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。 支持视频格式: MP4,MOV,M4V,3GP,AVI等。 支持音频格式:MP3,AAC,WAV,AMR,M4A等。 详见AVPlayer支持的视频格式 ##如何使用 AVPlayer存在于AVFoundation框架,我们使用时需要导入: #import <AVFoundation/AVFoundation.h> 几个播放相关的参数 在创建一个播放器之前我们需要先了解一些播放器相关的类AVPlayer:控制播放器的播放,暂停,播放速度 AVURLAsset : AVAsset 的一个子类,使用 URL 进行实例化,实例化对象包换 URL 对应视频资源的所有信息。 AVPlayerItem:管理资源对象,提供播放数据源 AVPlayerLayer:负责显示视频,如果没有添加该类,只有声音没有画面我们这片文章就围绕这几个参数展开,光说这些你可能还有点不明白,那我们就围绕一个最简单的播放器做起,一点点扩展功能,在具体讲解这几个参数的作用。 最简单的播放器 根据上面描述,我们知道AVPlayer是播放的必要条件,所以我们可以构建的极简播放器就是: NSURL *playUrl = [NSURL URLWithString:@"http://baobab.wdjcdn.com/14573563182394.mp4"]; self.player = [[AVPlayer alloc] initWithURL:playUrl]; [self.player play];是不是很简单,只有三行代码! 但是它太简单了,仅可以完成音频的播放,连画面都没有。回看上面播放相关类的介绍,是因为缺少AVPlayerLayer;作为一个播放器,我不能只播放一条视频啊,我还想根据需要切换视频,那我们就得把AVPlayerItem也加上。 加上这两个属性之后的播放器是这样的: NSURL *playUrl = [NSURL URLWithString:@"http://baobab.wdjcdn.com/14573563182394.mp4"]; self.playerItem = [AVPlayerItem playerItemWithURL:playUrl]; //如果要切换视频需要调AVPlayer的replaceCurrentItemWithPlayerItem:方法 self.player = [AVPlayer playerWithPlayerItem:_playerItem]; self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; self.playerLayer.frame = _videoView.bounds; //放置播放器的视图 [self.videoView.layer addSublayer:self.playerLayer]; [_player play];现在的播放器稍微完整了一些,我们在自己创建的容器里可以看到画面了! 更多功能 但是它作为一个视频播放器,还是有很多不能让人满意的地方。例如:没有暂停、快进快退、倍速播放等,另外如果遇到url错误是不是还要有播放失败的提示,还有播放完成的相关提示。 为完成这些,我们需要对AVPlayerItem和AVPlayerLayer进一步了解一下。 一、AVPlayer的控制 前面讲过该类是控制视频播放行为的,他的使用比较简单。 播放视频: [self.player play];暂停视频: [self.player pause];更改速度: self.player.rate = 1.5;//注意更改播放速度要在视频开始播放之后才会生效还有一下其他的控制,我们可以调转到系统API进行查看 二、AVPlayerItem的控制 AVPlayerItem作为资源管理对象,它控制着视频从创建到销毁的诸多状态。 1、播放状态 status typedef NS_ENUM(NSInteger, AVPlayerItemStatus) { AVPlayerItemStatusUnknown,//未知 AVPlayerItemStatusReadyToPlay,//准备播放 AVPlayerItemStatusFailed//播放失败 };我们使用KVO监测playItem.status,可以获取播放状态的变化 [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];在监听回调中: - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ if ([object isKindOfClass:[AVPlayerItem class]]) { if ([keyPath isEqualToString:@"status"]) { switch (_playerItem.status) { case AVPlayerItemStatusReadyToPlay: //推荐将视频播放放在这里 [self play]; break; case AVPlayerItemStatusUnknown: NSLog(@"AVPlayerItemStatusUnknown"); break; case AVPlayerItemStatusFailed: NSLog(@"AVPlayerItemStatusFailed") break; default: break; } } } }虽然设置完播放配置我们可以直接调用[self.player play];进行播放,但是更稳妥的方法是在回调收到AVPlayerItemStatusReadyToPlay时进行播放 2、视频的时间信息 在AVPlayer中时间的表示有一个专门的结构体CMTime typedef struct{ CMTimeValue value; // 帧数 CMTimeScale timescale; // 帧率(影片每秒有几帧) CMTimeFlags flags; CMTimeEpoch epoch; } CMTime;CMTime是以分数的形式表示时间,value表示分子,timescale表示分母,flags是位掩码,表示时间的指定状态。 获取当前播放时间,可以用value/timescale的方式: float currentTime = self.playItem.currentTime.value/item.currentTime.timescale;还有一种利用系统提供的方法,我们用它获取视频总时间: float totalTime = CMTimeGetSeconds(item.duration);如果我们想要添加一个计时的标签不断更新当前的播放进度,有一个系统的方法: - (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;方法名如其意, “添加周期时间观察者” ,参数1 interal 为CMTime 类型的,参数2 queue为串行队列,如果传入NULL就是默认主线程,参数3 为CMTime 的block类型。 简而言之就是,每隔一段时间后执行 block。 比如:我们把interval设置成CMTimeMake(1, 10),在block里面刷新label,就是一秒钟刷新10次。 正常观察播放进度一秒钟一次就行了,所以可以这么写: [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:nil usingBlock:^(CMTime time) { AVPlayerItem *item = WeakSelf.playerItem; NSInteger currentTime = item.currentTime.value/item.currentTime.timescale; NSLog(@"当前播放时间:%ld",currentTime); }];3、loadedTimeRange 缓存时间 获取视频的缓存情况我们需要监听playerItem的loadedTimeRanges属性 [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];在KVO的回调里: if ([keyPath isEqualToString:@"loadedTimeRanges"]){ NSArray *array = _playerItem.loadedTimeRanges; CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围 float startSeconds = CMTimeGetSeconds(timeRange.start); float durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度 NSLog(@"当前缓冲时间:%f",totalBuffer); }4、playbackBufferEmpty 监听playbackBufferEmpty我们可以获取当缓存不够,视频加载不出来的情况: [self.playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];在KVO回调里: if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { //some code show loading }5、playbackLikelyToKeepUp playbackLikelyToKeepUp和playbackBufferEmpty 是一对,用于监听缓存足够播放的状态 [self.playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; /* ... */ if([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { //由于 AVPlayer 缓存不足就会自动暂停,所以缓存充足了需要手动播放,才能继续播放 [_player play]; }AVURLAsset 播放视频只需一个url就能进行这样太不安全了,别人可以轻易的抓包盗链,为此我们需要为视频链接做一个请求头的认证,这个功能可以借助AVURLAsset完成。 AVPlayerItem除了可以用URL初始化,还可以用AVAsset初始化,而AVAsset不能直接使用,我们看下AVURLAsset的一个初始化方法: /*! @param URL An instance of NSURL that references a media resource. @param options An instance of NSDictionary that contains keys for specifying options for the initialization of the AVURLAsset. See AVURLAssetPreferPreciseDurationAndTimingKey and AVURLAssetReferenceRestrictionsKey above. */ + (instancetype)URLAssetWithURL:(NSURL *)URL options:(nullable NSDictionary<NSString *, id> *)options;AVURLAssetPreferPreciseDurationAndTimingKey.这个key对应的value是一个布尔值, 用来表明资源是否需要为时长的精确展示,以及随机时间内容的读取进行提前准备。 除了这个苹果官方介绍的功能外,他还可以设置请求头,这个算是隐藏功能了,因为苹果并没有明说这个功能,我是费了很大劲找到的。 NSMutableDictionary * headers = [NSMutableDictionary dictionary]; [headers setObject:@"yourHeader"forKey:@"User-Agent"]; self.urlAsset = [AVURLAsset URLAssetWithURL:self.videoURL options:@{@"AVURLAssetHTTPHeaderFieldsKey" : headers}]; // 初始化playerItem self.playerItem = [AVPlayerItem playerItemWithAsset:self.urlAsset];补充:后来得知这个参数是非公开的API,但是经多人测试项目上线不受影响。 播放相关通知 1、声音类: //声音被打断的通知(电话打来) AVAudioSessionInterruptionNotification //耳机插入和拔出的通知 AVAudioSessionRouteChangeNotification根据userInfo判断具体状态 2、播放类 //播放完成 AVPlayerItemDidPlayToEndTimeNotification //播放失败 AVPlayerItemFailedToPlayToEndTimeNotification //异常中断 AVPlayerItemPlaybackStalledNotification对于播放完成的通知我们可以这么写: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerMovieFinish:) name:AVPlayerItemDidPlayToEndTimeNotification object:[self.player currentItem]];3、系统状态 //进入后台 UIApplicationWillResignActiveNotification //返回前台 UIApplicationDidBecomeActiveNotification提示:所有通知和KVO的使用我们都要记得在不用时remove掉。 小结 视频播放相关的知识比较多,细节的方面需要一点一点去扣。暂且写这么多吧,以后有需要会及时补充。 参考: ZFPlayer AVPlayer那些坑 如果还有什么不理解的可以简书私信问我,或者查看我写的Demo,欢迎star- ( ゜- ゜)つロ乾杯~