Exoplayer开发者指南

开发者指南

内容

播放视频和音乐是Android设备上的热门活动。 Android框架提供MediaPlayer作为使用最少代码播放媒体的快速解决方案。 它还提供低级媒体API,如MediaCodec , AudioTrackMediaDrm ,可用于构建自定义媒体播放器解决方案。

ExoPlayer是一个基于Android低级媒体API构建的开源应用级媒体播放器。 本指南介绍了ExoPlayer库及其用法。 它指的是ExoPlayer 主要演示应用程序中的代码,以提供具体示例。 该指南涉及使用ExoPlayer的优缺点。 它展示了如何使用ExoPlayer播放DASH,SmoothStreaming和HLS自适应流,以及MP4,M4A,FMP4,WebM,MKV,MP3,Ogg,WAV,MPEG-TS,MPEG-PS,FLV和ADTS等格式( AAC)。 它还讨论了ExoPlayer事件,消息,自定义和DRM支持。

优点和缺点

与Android内置的MediaPlayer相比,ExoPlayer具有许多优势:

  • 支持基于HTTP的动态自适应流传输(DASH)和SmoothStreaming,MediaPlayer均不支持这两种流。 还支持许多其他格式。 有关详细信息,请参阅支持的格式
  • 支持高级HLS功能,例如正确处理#EXT-X-DISCONTINUITY标签。
  • 能够无缝合并,连接和循环媒体。
  • 能够随应用程序更新播放器。 由于ExoPlayer是您在应用程序apk中包含的库,因此您可以控制使用的版本,并且可以轻松地将更新版本更新为常规应用程序更新的一部分。
  • 不同的设备特定问题和不同设备和Android版本的行为变化较小。
  • 支持Android 4.4(API级别19)及更高版本的Widevine通用加密。
  • 能够根据您的使用情况自定义和扩展播放器。 ExoPlayer专门针对这一点而设计,允许使用自定义实现替换许多组件。
  • 能够使用官方扩展快速集成许多其他库。 例如, IMA扩展程序可让您使用互动式媒体广告SDK轻松通过内容获利。

值得注意的是,还有一些缺点:

  • 对于某些设备上的仅音频播放,ExoPlayer可能比MediaPlayer消耗更多的电池。 有关详细信息,请参阅电池消耗页

图书馆概述

ExoPlayer库的核心是ExoPlayer接口。 ExoPlayer公开了传统的高级媒体播放器功能,例如缓冲媒体,播放,暂停和搜索的功能。 实现旨在对正在播放的媒体类型,存储方式和位置以及如何呈现的媒体类型做出一些假设(并因此施加很少的限制)。ExoPlayer实现不是直接实现媒体的加载和呈现,而是将此工作委托给在创建播放器或准备播放时注入的组件。 所有ExoPlayer实现的通用组件是:

  • MediaSource ,用于定义要播放的媒体,加载媒体,以及从中读取加载的媒体。 在播放开始时通过ExoPlayer.prepare注入MediaSource 。
  • Renderer渲染媒体的各个组件。 创建播放器时会注入Renderer 。
  • TrackSelector ,用于选择MediaSource提供的曲目,供每个可用的Renderer 。 创建播放器时会注入TrackSelector 。
  • 一个LoadControl ,用于控制MediaSource何时缓冲更多媒体,以及缓冲多少媒体。 创建播放器时会注入LoadControl

该库为常见用例提供了这些组件的默认实现,如下面更详细地描述的。 ExoPlayer可以使用这些组件,但如果需要非标准行为,也可以使用自定义实现构建。 例如,可以注入自定义LoadControl来更改播放器的缓冲策略,或者可以注入自定义Renderer以使用Android本身不支持的视频编解码器。

注入实现播放器功能的组件的概念贯穿整个库。 上面列出的组件的默认实现将工作委托给进一步注入的组件。 这允许使用自定义实现单独替换许多子组件。 例如,默认的MediaSource实现需要通过其构造函数注入一个或多个DataSource工厂。 通过提供自定义工厂,可以从非标准源或通过不同的网络堆栈加载数据。

入门

对于简单的用例, ExoPlayer入门包括实现以下步骤:

  1. 将ExoPlayer添加为项目的依赖项。
  2. 创建SimpleExoPlayer实例。
  3. 将播放器连接到视图(用于视频输出和用户输入)。
  4. 使用MediaSource准备播放器。
  5. 完成后释放播放器。

以下更详细地概述了这些步骤。 有关完整示例,请参阅主演示应用程序中的PlayerActivity 。

添加ExoPlayer作为依赖项

入门的第一步是确保您的项目根目录中的build.gradle文件中包含JCenter和Google存储库。

 repositories { jcenter () google () } 

接下来,在app模块的build.gradle文件中添加依赖项。 以下内容将为完整的ExoPlayer库添加依赖项:

 implementation 'com.google.android.exoplayer:exoplayer:2.X.X' 

其中2.XX是您的首选版本。 或者,您只能依赖实际需要的库模块。 例如,以下内容将添加对Core,DASH和UI库模块的依赖性,这对于播放DASH内容的应用程序可能是必需的:

 implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X' implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X' implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X' 

下面列出了可用的库模块。 向完整的ExoPlayer库添加依赖项等同于单独添加所有库模块的依赖项。

  • exoplayer-core :核心功能(必需)。
  • exoplayer-dash :支持DASH内容。
  • exoplayer-hls :支持HLS内容。
  • exoplayer-smoothstreaming :支持exoplayer-smoothstreaming内容。
  • exoplayer-ui :用于ExoPlayer的UI组件和资源。

除了库模块,ExoPlayer还有多个扩展模块,这些模块依赖于外部库来提供附加功能。 这些超出了本指南的范围。 浏览扩展目录及其各自的README以获取详细信息。

创建播放器

您可以使用ExoPlayerFactory创建ExoPlayer实例。 工厂提供了一系列方法来创建具有不同级别自定义的ExoPlayer实例。对于绝大多数用例,应使用ExoPlayerFactory.newSimpleInstance方法之一。 这些方法返回SimpleExoPlayer ,它扩展了ExoPlayer以添加额外的高级播放器功能。 下面的代码是创建SimpleExoPlayer的示例。

 // 1. Create a default TrackSelector Handler mainHandler = new Handler (); BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter (); TrackSelection . Factory videoTrackSelectionFactory = new AdaptiveTrackSelection . Factory ( bandwidthMeter ); DefaultTrackSelector trackSelector = new DefaultTrackSelector ( videoTrackSelectionFactory ); // 2. Create the player SimpleExoPlayer player = ExoPlayerFactory . newSimpleInstance ( context , trackSelector ); 

必须从单个应用程序线程访问ExoPlayer实例。 如果该线程具有{@link Looper},则必须是创建该播放器的线程,否则该应用程序的主线程。

将播放器附加到视图

ExoPlayer库提供了一个PlayerView ,它封装了一个PlayerControlView和一个呈现视频的Surface 。 PlayerView可以包含在应用程序的布局xml中。 将播放器绑定到视图非常简单:

 // Bind the player to the view. playerView . setPlayer ( player ); 

如果您需要对播放器控件和渲染视频的Surface进行细粒度控制,则可以分别使用SimpleExoPlayersetVideoSurfaceView, setVideoTextureView , setVideoSurfaceHoldersetVideoSurface方法直接设置播放器的目标SurfaceView , TextureView , SurfaceHolderSurface 。 您可以将PlayerControlView用作独立组件,或实现您自己的与播放器直接交互的播放控件。 setTextOutputsetId3Output可用于在播放期间接收标题和ID3元数据输出。

准备播放器

在ExoPlayer中,每个媒体都由MediaSource代表。 要播放介质,必须首先创建相应的MediaSource ,然后将此对象传递给ExoPlayer.prepare 。 ExoPlayer库为DASH( DashMediaSource ), SsMediaSource ( SsMediaSource ),HLS( HlsMediaSource )和常规媒体文件( ExtractorMediaSource )提供MediaSource实现。 本指南后面将详细介绍这些实现。以下代码显示如何使用适合播放MP4文件的MediaSource准备播放器。

 // Measures bandwidth during playback. Can be null if not required. DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter (); // Produces DataSource instances through which media data is loaded. DataSource . Factory dataSourceFactory = new DefaultDataSourceFactory ( context , Util . getUserAgent ( context , "yourApplicationName" ), bandwidthMeter ); // This is the MediaSource representing the media to be played. MediaSource videoSource = new ExtractorMediaSource . Factory ( dataSourceFactory ) . createMediaSource ( mp4VideoUri ); // Prepare the player with the source. player . prepare ( videoSource ); 

控制播放器

一旦准备好了播放器,就可以通过调用播放器上的方法来控制播放。 例如, setPlayWhenReady启动并暂停播放,各种seekTo方法在媒体内搜索, setRepeatMode控制媒体是否以及如何循环, setShuffleModeEnabled控制播放列表改组, setPlaybackParameters调整播放速度和音高。

如果播放器绑定到PlayerViewPlayerControlView则用户与这些组件的交互将导致调用播放器上的相应方法。

听取球员事件

状态更改和回放错误等事件将报告给已注册的Player.EventListener实例。 注册监听器以接收此类事件很容易:

 // Add a listener to receive events from the player. player . addListener ( eventListener ); 

如果您只对事件的子集感兴趣,则扩展Player.DefaultEventListener而不是实现Player.EventListener允许您仅实现您感兴趣的方法。

使用SimpleExoPlayer ,可以在播放器上设置其他侦听器。 addVideoListener方法允许您接收与视频渲染相关的事件,这些事件可能对调整UI很有用(例如,正在渲染视频的Surface的宽高比)。 addAnalyticsListener方法允许您接收详细事件,这可能对分析有用。

释放播放器

在不再需要播放器时释放播放器非常重要,这样可以释放有限的资源,例如视频解码器,供其他应用程序使用。 这可以通过调用ExoPlayer.release来完成。

MediaSource的

在ExoPlayer中,每个媒体都由MediaSource代表。 ExoPlayer库为DASH( DashMediaSource ), SsMediaSource ( SsMediaSource ),HLS( HlsMediaSource )和常规媒体文件( ExtractorMediaSource )提供MediaSource实现。 可以在主演示应用程序的 PlayerActivity中找到如何实例化所有四个示例 。

除了上面描述的MediaSource实现之外,ExoPlayer库还提供了ConcatenatingMediaSource , ClippingMediaSource , LoopingMediaSourceMergingMediaSource 。 这些MediaSource实现通过组合实现更复杂的回放功能。 一些常见用例如下所述。 请注意,尽管在视频播放的上下文中描述了以下示例,但它们同样适用于仅音频播放,并且实际上适用于任何支持的媒体类型的播放。

播放列表

使用ConcatenatingMediaSource支持播放列表,可以顺序播放多个MediaSource 。 以下示例表示由两个视频组成的播放列表。

 MediaSource firstSource = new ExtractorMediaSource . Factory (...). createMediaSource ( firstVideoUri ); MediaSource secondSource = new ExtractorMediaSource . Factory (...). createMediaSource ( secondVideoUri ); // Plays the first video, then the second video. ConcatenatingMediaSource concatenatedSource = new ConcatenatingMediaSource ( firstSource , secondSource ); 

连接源之间的转换是无缝的。 不要求它们具有相同的格式(例如,将包含480p H264的视频文件与包含720p VP9的视频文件连接起来就可以了)。 它们甚至可能是不同类型的(例如,将视频与仅音频流连接起来很好)。 允许在串联中多次使用单个MediaSource 。

可以通过在ConcatenatingMediaSource添加,删除和移动MediaSource来动态修改播放列表。 这可以通过调用相应的ConcatenatingMediaSource方法在回放之前和回放期间完成。 播放器以正确的方式自动处理播放期间的修改。 例如,如果移动当前播放的MediaSource ,则不会中断播放,并且将在完成时播放其新的后继播放器。 如果当前播放的MediaSource被移除,则播放器将自动移动到播放第一个剩余的后继者,或者如果不存在这样的后继者则转换到结束状态。

剪辑视频

ClippingMediaSource可用于剪辑MediaSource以便只播放其中的一部分。 以下示例将视频播放剪辑为以5秒开始并以10秒结束。

 MediaSource videoSource = new ExtractorMediaSource . Factory (...). createMediaSource ( videoUri ); // Clip to start at 5 seconds and end at 10 seconds. ClippingMediaSource clippingSource = new ClippingMediaSource ( videoSource , /* startPositionUs= */ 5_000_000 , /* endPositionUs= */ 10_000_000 ); 

要仅剪辑源的开头,可以将C.TIME_END_OF_SOURCE设置为C.TIME_END_OF_SOURCE 。 要仅剪切到特定的持续时间,有一个构造函数接受durationUs参数。

剪切视频文件的开头时,尽可能将起始位置与关键帧对齐。 如果开始位置未与关键帧对齐,则播放器将需要解码并丢弃从先前关键帧到开始位置的数据,然后才能开始播放。 这将在播放开始时引入短暂的延迟,包括播放器转换为播放ClippingMediaSource作为播放列表的一部分或由于循环播放。

循环播放视频

要无限循环,通常最好使用ExoPlayer.setRepeatMode而不是LoopingMediaSource 。

使用LoopingMediaSource可以将视频无缝循环固定次数。 以下示例播放视频两次。

 MediaSource source = new ExtractorMediaSource . Factory (...). createMediaSource ( videoUri ); // Plays the video twice. LoopingMediaSource loopingSource = new LoopingMediaSource ( source , 2 ); 

侧加载字幕文件

给定视频文件和单独的字幕文件,可以使用MergingMediaSource将它们合并为单个源以进行回放。

 // Build the video MediaSource. MediaSource videoSource = new ExtractorMediaSource . Factory (...). createMediaSource ( videoUri ); // Build the subtitle MediaSource. Format subtitleFormat = Format . createTextSampleFormat ( id , // An identifier for the track. May be null. MimeTypes . APPLICATION_SUBRIP , // The mime type. Must be set correctly. selectionFlags , // Selection flags for the track. language ); // The subtitle language. May be null. MediaSource subtitleSource = new SingleSampleMediaSource . Factory (...) . createMediaSource ( subtitleUri , subtitleFormat , C . TIME_UNSET ); // Plays the video with the sideloaded subtitle. MergingMediaSource mergedSource = new MergingMediaSource ( videoSource , subtitleSource ); 

先进的作文

可以将复合MediaSource进一步组合用于更多不寻常的用例。 给定两个视频A和B,以下示例显示了如何一起使用LoopingMediaSourceConcatenatingMediaSource来播放序列(A,A,B)。

 MediaSource firstSource = new ExtractorMediaSource . Factory (...). createMediaSource ( firstVideoUri ); MediaSource secondSource = new ExtractorMediaSource . Factory (...). createMediaSource ( secondVideoUri ); // Plays the first video twice. LoopingMediaSource firstSourceTwice = new LoopingMediaSource ( firstSource , 2 ); // Plays the first video twice, then the second video. ConcatenatingMediaSource concatenatedSource = new ConcatenatingMediaSource ( firstSourceTwice , secondSource ); 

以下示例是等效的,表明可以有多种方法来实现相同的结果。

 MediaSource firstSource = new ExtractorMediaSource . Builder ( firstVideoUri , ...). build (); MediaSource secondSource = new ExtractorMediaSource . Builder ( secondVideoUri , ...). build (); // Plays the first video twice, then the second video. ConcatenatingMediaSource concatenatedSource = new ConcatenatingMediaSource ( firstSource , firstSource , secondSource ); 

跟踪选择

曲目选择确定播放器的Renderer播放哪些可用的媒体曲目。 轨道选择是TrackSelector的责任,每当构建ExoPlayer时,必须提供其实例。

DefaultTrackSelector是一个灵活的TrackSelector适用于大多数用例。 使用DefaultTrackSelector ,可以通过修改其Parameters来控制它选择的轨道。 这可以在播放之前或播放期间完成。 例如,以下代码告诉选择器将视频轨道选择限制为SD,并选择德语音轨(如果有):

 trackSelector . setParameters ( trackSelector . buildUponParameters () . setMaxVideoSizeSd () . setPreferredAudioLanguage ( "deu" )); 

这是基于约束的轨道选择的示例,其中在不知道实际可用的轨道的情况下指定约束。 可以使用Parameters指定许多不同类型的约束。 Parameters还可用于从可用的轨道中选择特定轨道。 有关更多详细信息,请参阅DefaultTrackSelector , ParametersParametersBuilder文档。

将消息发送到组件

可以向ExoPlayer组件发送消息。 这些可以使用createMessage创建,然后使用PlayerMessage.send发送。 默认情况下,消息尽快在回放线程上传递,但可以通过设置另一个回调线程(使用PlayerMessage.setHandler )或指定传递回放位置(使用PlayerMessage.setPosition )来自定义。 通过ExoPlayer发送消息可确保按顺序执行操作,同时在播放器上执行任何其他操作。

大多数ExoPlayer的开箱即用渲染器都支持允许在播放期间更改其配置的消息。 例如,音频渲染器接受消息来设置音量,视频渲染器接受消息来设置曲面。 这些消息应在回放线程上传递,以确保线程安全。

定制

与Android的MediaPlayer ,ExoPlayer的主要优势之一是能够自定义和扩展播放器以更好地适应开发人员的使用案例。ExoPlayer库专门为此而设计,定义了许多接口和抽象基类,使应用程序开发人员可以轻松替换库提供的默认实现。 以下是构建自定义组件的一些用例:

  • Renderer - 您可能希望实现自定义Renderer来处理库提供的默认实现不支持的媒体类型。
  • TrackSelector - 实现自定义TrackSelector允许应用程序开发人员更改每个可用Renderer选择供MediaSource公开的轨道使用的方式。
  • LoadControl - 实现自定义LoadControl允许应用程序开发人员更改播放器的缓冲策略。
  • Extractor - 如果需要支持库当前不支持的容器格式,请考虑实现自定义Extractor类,然后可以将其与ExtractorMediaSource一起用于播放该类型的媒体。
  • MediaSource - 如果您希望获得以自定义方式提供给渲染器的媒体示例,或者您希望实现自定义MediaSource合成行为,则实现自定义MediaSource类可能是合适的。
  • DataSource - ExoPlayer的上游包已经包含了许多针对不同用例的DataSource实现。 您可能希望实现自己的DataSource类以另一种方式加载数据,例如通过自定义协议,使用自定义HTTP堆栈或自定义持久缓存。

构建自定义组件时,建议执行以下操作:

  • 如果自定义组件需要将事件报告回应用程序,我们建议您使用与现有ExoPlayer组件相同的模型执行此操作,其中事件侦听器与Handler一起传递给组件的构造函数。
  • 我们建议自定义组件使用与现有ExoPlayer组件相同的模型,以允许应用程序在播放期间重新配置,如向组件发送消息中所述。 为此,您应该实现ExoPlayerComponent并在其handleMessage方法中接收配置更改。 您的应用应通过调用ExoPlayer的sendMessagesblockingSendMessages方法来传递配置更改。

高级主题

数字版权管理

ExoPlayer支持Android 4.4(API级别19)的受数字版权管理(DRM)保护的播放。 有关更多详细信息,请参阅DRM页面 。

电池消耗

有关使用ExoPlayer时电池消耗的信息,请参阅电池消耗页面 。

缩小ExoPlayer库

有关最小化ExoPlayer库大小的建议,请参阅Shrinking ExoPlayer页面 。

猜你喜欢

转载自blog.csdn.net/duoluo9/article/details/81383332