ExoPlayer详解——入门(官方文档)

ExoPlayer详解——入门(官方文档)

2023年6月27日发(作者:)

ExoPlayer详解——⼊门(官⽅⽂档)ExoPlayer详解系列⽂章ExoPlayer详解——⼊门(官⽅⽂档)ExoPlayer详解——媒体类型(官⽅⽂档)ExoPlayer详解——⾼级主题(官⽅⽂档)⼀、ExoPlayer,你好简单⽤例⼊门ExoPlayer包括实现以下步骤:将 ExoPlayer 添加为项⽬的依赖项。创建⼀个 SimpleExoPlayer 实例。将播放器连接到视图(⽤于视频输出和⽤户输⼊)。准备 player 的 MediaSource 播放。完成后释放播放器。添加ExoPlayer作为依赖项1、添加存储库确保项⽬根⽬录中的⽂件中包含Google和JCenter存储库。 repositories { google() jcenter() }12342、添加ExoPlayer模块接下来在app⽬录下的的⽂件中添加依赖项。以下内容将为完整的ExoPlayer库添加依赖项: implementation 'yer:exoplayer:2.X.X'12.X.X您的⾸选版本在哪⾥(可以通过查阅发⾏说明找到最新版本)。作为完整库的替代⽅法,您只能依赖于实际需要的库模块。例如,以下内容将添加对Core,DASH和UI库模块的依赖关系,这对于播放DASH内容的应⽤程序可能是必需的: implementation 'yer:exoplayer-core:2.X.X' implementation 'yer:exoplayer-dash:2.X.X' implementation 'yer:exoplayer-ui:2.X.X'123下⾯列出了可⽤的库模块。向完整的ExoPlayer库添加依赖项等同于单独添加所有库模块的依赖项。exoplayer-core:核⼼功能(必需)。exoplayer-dash:⽀持DASH内容。exoplayer-hls:⽀持HLS内容。exoplayer-smoothstreaming:⽀持SmoothStreaming内容。exoplayer-ui:⽤于ExoPlayer的UI组件和资源。除了库模块,ExoPlayer还有多个扩展模块,它们依赖于外部库来提供附加功能。浏览扩展⽬录及其各⾃的README以获取详细信息。3、打开Java 8⽀持如果尚未启⽤,则需要在所有 ⽂件中打开Java 8⽀持,具体取决于ExoPlayer,⽅法是在以下android部分添加以下内容: compileOptions { targetCompatibility N_1_8 }1234、创建播放器您可以使⽤ ExoPlayer创建实例 ExoPlayerFactory。这个⼯⼚实例提供了⼀系列⽅法来创建 ExoPlayer具有不同级别⾃定义的实例。对于绝⼤多数⽤例, pleInstance应该使⽤其中⼀种⽅法。这些⽅法返回 SimpleExoPlayer,扩展 ExoPlayer为添加额外的⾼级播放器功能。下⾯的代码是创建⼀个的例⼦ SimpleExoPlayer。 SimpleExoPlayer player = pleInstance(context);1必须从单个应⽤程序线程访问ExoPlayer实例。对于绝⼤多数情况,这应该是应⽤程序的主线程。使⽤ExoPlayer的UI组件或IMA扩展时,需要使⽤应⽤程序的主线程。必须访问 ExoPlayer 实例的线程可以通过 Looper在创建播放器时传递来明确指定。如果未 Looper指定,则 Looper使⽤创建播放器的线程,或者如果该线程没有 Looper, Looper则使⽤应⽤程序的主线程。在所有情况下, Looper可以使⽤查询必须访问播放器的线程licationLooper。5、将播放器附加到视图ExoPlayer库提供了⼀个 PlayerView封装⼀个 PlayerControlView,⼀个 SubtitleView和⼀个 Surface视频的视图。⼀个 PlayerView可以包含在应⽤程序的布局 xml中。将播放器绑定到视图⾮常简单: // Bind the player to the view. yer(player);12如果您需要精准控制播放器并在 Surface上渲染视频,可以设置 player的⽬标 SurfaceView, TextureView, SurfaceHolder或Surface直接分别使⽤ SimpleExoPlayer的 setVideoSurfaceView, setVideoTextureView, setVideoSurfaceHolder和setVideoSurface的⽅法。您还可以将其 PlayerControlView⽤作独⽴组件,或实现⾃⼰的播放控件,直接与播放器进⾏交互。SimpleExoPlayer的 setTextOutput和 setId3Output⽅法可⽤于在回放期间接收字幕和ID3元数据。6、准备播放器在 ExoPlayer 中,每个媒体都由⼀个 MediaSource代表。要播放媒体⽂件,必须⾸先创建⼀个对应的 MediaSource,然后将此对象传递给 e。 ExoPlayer 库提供 MediaSourceDASH(DashMediaSource),SmoothStreaming(SsMediaSource),HLS(HlsMediaSource)和常规媒体⽂件 (ProgressiveMediaSource)的实现。以下代码显⽰如何准备 MediaSource适合播放MP4⽂件的播放器。 // Produces DataSource instances through which media data is loaded. y dataSourceFactory = new DefaultDataSourceFactory(context, rAgent(context, "yourApplicationName")); // This is the MediaSource representing the media to be played. MediaSource videoSource = new y(dataSourceFactory) .createMediaSource(mp4VideoUri); // Prepare the player with the source. e(videoSource);123456787、控制播放器⼀旦准备好了播放器,就可以通过调⽤播放器上的⽅法来控制播放。例如: setPlayWhenReady开始和暂停播放,各种 seekTo⽅法在媒体内寻找, setRepeatMode控制媒体是否以及如何循环, setShuffleModeEnabled控制播放列表改组,以及 setPlaybackParameters调整播放速度和⾳⾼。如果 player被绑定到 PlayerView或 PlayerControlView,则⽤户与这些组件的交互将导致调⽤播放器上的相应⽅法。8、释放播放器在不再需要 player时释放播放器⾮常重要,这样可以释放有限的资源,例如视频解码器,供其他应⽤程序使⽤。这可以通过调⽤来完成e。⼆、监听 Player 事件状态更改和回放错误等事件将报告给已注册的 istener实例。注册监听器以接收此类事件很容易: // Add a listener to receive events from the player. tener(eventListener);istener具有空的默认⽅法,因此您只需要实现您感兴趣的⽅法。有关⽅法的完整描述以及何时调⽤它们,请参阅Javadoc。两个最重要的是 onPlayerStateChanged和 onPlayerError,下⾯将更详细地描述。(⼀)、onPlayerStateChangedplayer 状态的变化可以通过 onPlayerStateChanged(boolean playWhenReady, int playbackState)在注册中 实施来接收istener。播放器可以处于以下四种播放状态之⼀:_IDLE:这是初始状态,播放器停⽌时的状态以及播放失败时的状态。_BUFFERING: player ⽆法⽴即从当前位置进⾏播放。这主要是因为需要加载更多数据。_READY: player 可以⽴即从当前位置进⾏播放。_ENDED:播放器播放完所有媒体。除了这些状态之外, player 还有⼀个 playWhenReady标志来指⽰⽤户打算播放。如果状态为 _READY和playWhenReady=true,则播放器仅播放。 @Override public void onPlayerStateChanged( boolean playWhenReady, @ int playbackState) { if (playWhenReady && playbackState == _READY) { // Active playback. } else if (playWhenReady) { // Not playing because playback ended, the player is buffering, stopped or // failed. Check playbackState and ybackError for details. } else { // Paused by app. } }1112(⼆)、 onPlayerError可以通过 onPlayerError(ExoPlaybackException error)在注册中 实现来接收导致回放失败的错误 istener。发⽣故障时,将在播放状态转换为之前⽴即调⽤此⽅法 _IDLE。 ExoPlaybackException有⼀个 type字段,以及相应的 getter⽅法,它们返回原因异常,提供有关失败的更多信息。以下⽰例显⽰了如何检测由于HTTP⽹络问题导致播放失败的时间。 @Override public void onPlayerError(ExoPlaybackException error) { if ( == _SOURCE) { IOException cause = rceException(); if (cause instanceof HttpDataSourceException) { // An HTTP error occurred. HttpDataSourceException httpError = (HttpDataSourceException) cause; // This is the request for which the error occurred. DataSpec requestDataSpec = ec; // It's possible to find out more about the error both by casting and by // querying the cause. if (httpError instanceof dResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, // message and headers. } else { // Try calling se() to retrieve the underlying cause, // although note that it may be null. } } } }1718192021(三)、使⽤ EventLogger默认情况下,ExoPlayer 仅记录错误。要将播放器事件记录到控制台, EventLogger可以使⽤该类。它提供的额外⽇志记录有助于了解播放器正在做什么,以及调试播放问题。 EventLogger实现 AnalyticsListener,因此使⽤ SimpleExoPlayer注册实例很容易: lyticsListener(new EventLogger(trackSelector));11、解释⽇志输出观察⽇志的最简单⽅法是使⽤ Android Studio 的 logcat选项卡。您可以通过包名称选择您的应⽤程序作为可调试进程(如果使⽤演⽰应⽤程序),并通过选择“仅显⽰所选应⽤程序”告诉logcat选项卡仅记录该应⽤程序。可以使⽤表达式进⼀步过滤⽇志记录 EventLogger|ExoPlayerImpl,以便仅从⽇志 EventLogger和播放器本⾝进⾏⽇志记录。使⽤Android Studio的logcat选项卡的另⼀种⽅法是使⽤控制台。例如: adb logcat | grep 'EventLogger|ExoPlayerImpl'12、player信息该 ExoPlayerImpl类提供了两个关于播放器版本,运⾏应⽤程序的设备和操作系统以及已加载的 ExoPlayer 模块的重要内容: ExoPlayerImpl: Release 2cd6e65 [ExoPlayerLib/2.9.6] [marlin, Pixel XL, Google, 26] [, ,] ExoPlayerImpl: Init 2e5194c [ExoPlayerLib/2.9.6] [marlin, Pixel XL, Google, 26]123、播放状态播放器状态更改记录在以下⾏中。在此⽰例中,播放永远不会重建(在初始缓冲之后)并且⽤户暂停⼀次: EventLogger: state [0.00, 0.00, window=0, true, BUFFERING] EventLogger: state [0.92, 0.04, window=0, period=0, true, READY] EventLogger: state [11.53, 10.60, window=0, period=0, false, READY] EventLogger: state [14.26, 10.60, window=0, period=0, true, READY] EventLogger: state [131.89, 128.27, window=0, period=0, true, ENDED]12345⽅括号内的元素是:[float]:⾃ player 创建以来的挂钟时间。[float]:当前播放位置。[window=int]:当前窗⼝索引。[period=int]:该窗⼝中的当前时段。[boolean]: playWhenReady标志。[string]:当前播放状态。4、媒体曲⽬当可⽤或选定的曲⽬发⽣变化时,将记录曲⽬信息。这在回放开始时⾄少发⽣⼀次。以下⽰例显⽰了⾃适应流的跟踪⽇志记录: EventLogger: tracksChanged [2.32, 0.00, window=0, period=0, EventLogger: Renderer:0 [ EventLogger: Group:0, adaptive_supported=YES [ EventLogger: [X] Track:0, id=133, mimeType=video/avc, bitrate=261112, codecs=avc1.4d4015, res=426x240,fps=30.0, supported=YES EventLogger: [X] Track:1, id=134, mimeType=video/avc, bitrate=671331, codecs=avc1.4d401e, res=640x360,fps=30.0, supported=YES EventLogger: [X] Track:2, id=135, mimeType=video/avc, bitrate=1204535, codecs=avc1.4d401f, res=854x480,fps=30.0, supported=YES EventLogger: [X] Track:3, id=160, mimeType=video/avc, bitrate=112329, codecs=avc1.4d400c, res=256x144,fps=30.0, supported=YES EventLogger: [X] Track:4, id=136, mimeType=video/avc, bitrate=2400538, codecs=avc1.4d401f, res=1280x720,fps=30.0, supported=YES EventLogger: ] EventLogger: ] EventLogger: Renderer:1 [ EventLogger: Group:0, adaptive_supported=YES_NOT_SEAMLESS [ EventLogger: [ ] Track:0, id=139, mimeType=audio/mp4a-latm, bitrate=48582, codecs=mp4a.40.5, channels=2,sample_rate=22050, supported=YES EventLogger: [X] Track:1, id=140, mimeType=audio/mp4a-latm, bitrate=127868, codecs=mp4a.40.2, channels=2,sample_rate=44100, supported=YES EventLogger: ] EventLogger: ] EventLogger: ]17播放⾃适应流时,播放期间会记录正在播放的格式的更改以及所选曲⽬的属性: EventLogger: downstreamFormatChanged [3.64, 0.00, window=0, period=0, id=134, mimeType=video/avc,bitrate=671331, codecs=avc1.4d401e, res=640x360, fps=30.0] EventLogger: downstreamFormatChanged [3.64, 0.00, window=0, period=0, id=140, mimeType=audio/mp4a-latm,bitrate=127868, codecs=mp4a.40.2, channels=2, sample_rate=44100]123、解码器选择在⼤多数情况下,ExoPlayer使⽤ MediaCodec从底层平台获取的媒体来呈现媒体。在报告任何播放状态之前,您将找到记录,告诉您哪些解码器已初始化。例如: EventLogger: decoderInitialized [0.77, 0.00, window=0, period=0, video, ] EventLogger: decoderInitialized [0.79, 0.00, window=0, period=0, audio, r]12三、媒体来源在ExoPlayer中,每个媒体都由 MediaSource代表。ExoPlayer库提供 MediaSource 了多种流类型的实现:DashMediaSource对于DASH。SsMediaSource对于SmoothStreaming。HlsMediaSource对于HLS。ProgressiveMediaSource对于常规媒体⽂件。可以 PlayerActivity在主演⽰应⽤程序中找到实例化所有四个的⽰例。1、MediaSource组成除了上⾯描述的 MediaSource 的实现⽅式中,ExoPlayer库还提供 ConcatenatingMediaSource , ClippingMediaSource ,LoopingMediaSource 和 MergingMediaSource 。这些 MediaSource 实现通过组合实现更复杂的回放功能。⼀些常见⽤例如下所述。请注意,尽管在视频播放的 context 中描述了以下⼀些⽰例,但它们同样适⽤于仅⾳频播放,并且实际上适⽤于任何⽀持的媒体类型的播放。2、播放播放列表使⽤⽀持播放列表 ConcatenatingMediaSource ,可以连续播放多个 MediaSource 。可以通过在播放期间添加,移除和移动MediaSource 来动态修改播放列表 ConcatenatingMediaSource 。有关详细信息,请参阅播放列表页⾯。3、剪辑视频ClippingMediaSource 可⽤于剪辑 MediaSource 以便只播放部分内容。以下⽰例将视频播放剪辑为以5秒开始并以10秒结束。 MediaSource videoSource = new y(...).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);12345678要仅剪辑源的开头, endPositionUs 可以设置为 _END_OF_SOURCE 。要仅剪切到特定的持续时间,有⼀个构造函数接受⼀个durationUs 参数。剪切视频⽂件的开头时,尽可能将起始位置与关键帧对齐。如果开始位置未与关键帧对齐,则播放器将需要解码并丢弃从先前关键帧到开始位置的数据,然后才能开始播放。这将在播放开始时引⼊短暂的延迟,包括当播放器转换为播放 ClippingMediaSource播放列表的⼀部分或由于循环播放时。4、循环播放视频要⽆限循环播放,⽐起 LoopingMediaSource 最好使⽤ eatMode 。视频可以使⽤ LoopingMediaSource ⽆缝循环固定次数。以下⽰例播放视频两次。 MediaSource source = new y(...).createMediaSource(videoUri); // Plays the video twice. LoopingMediaSource loopingSource = new LoopingMediaSource(source, 2);12345、侧加载字幕⽂件给定视频⽂件和单独的字幕⽂件, MergingMediaSource 可⽤于将它们合并为单个源以进⾏回放。 // Build the video MediaSource. MediaSource videoSource = new y(...).createMediaSource(videoUri); // Build the subtitle MediaSource. Format subtitleFormat = TextSampleFormat( id, // An identifier for the track. May be null. ATION_SUBRIP, // The mime type. Must be set correctly. selectionFlags, // Selection flags for the track. language); // The subtitle language. May be null. MediaSource subtitleSource = new y(...) .createMediaSource(subtitleUri, subtitleFormat, _UNSET); // Plays the video with the sideloaded subtitle. MergingMediaSource mergedSource = new MergingMediaSource(videoSource, subtitleSource);1234567891、先进的结构可以进⼀步组合成复杂的 MediaSource 以⽤于更多不寻常的实例。给定两个视频A和B,以下⽰例显⽰如何可以⼀起使⽤LoopingMediaSource 和 ConcatenatingMediaSource 来播放序列(A,A,B)。 MediaSource firstSource = new y(...).createMediaSource(firstVideoUri); MediaSource secondSource = new y(...).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);123456789以下⽰例是等效的,表明可以有多种⽅法来实现相同的结果。 MediaSource firstSource = new y(...).createMediaSource(firstVideoUri); MediaSource secondSource = new y(...).createMediaSource(secondVideoUri); // Plays the first video twice, then the second video. ConcatenatingMediaSource concatenatedSource = new ConcatenatingMediaSource(firstSource, firstSource, secondSource);1234567四、播放列表使⽤⽀持播放列表的 ConcatenatingMediaSource ,可以连续播放多个 MediaSource 。以下⽰例表⽰由两个视频组成的播放列表。 MediaSource firstSource = new y(...).createMediaSource(firstVideoUri); MediaSource secondSource = new y(...).createMediaSource(secondVideoUri); // Plays the first video, then the second video. ConcatenatingMediaSource concatenatedSource = new ConcatenatingMediaSource(firstSource, secondSource);1234567播放列表中的项⽬之间的转换是 ⽆缝 的。并不要求它们具有相同的格式(例如,播放列表包含H264和VP9视频都很好)。它们甚⾄可以是不同类型的(例如,播放列表包含视频和仅⾳频流都很好)。允许 MediaSource 在播放列表中多次使⽤相同的内容。1、修改播放列表可以通过在 ConcatenatingMediaSource 中添加,删除和移动 MediaSource 来动态修改播放列表。这可以通过调⽤相应的ConcatenatingMediaSource ⽅法在回放之前和回放期间完成。播放器以正确的⽅式⾃动处理播放期间的修改。例如,如果当前正在播放MediaSource ,则不会中断播放,并且将在完成时播放其新的后继播放。如果当前播放 MediaSource 被移除,则播放器将⾃动移动到播放第⼀个剩余的后继者,或者如果不存在这样的后继者则转换到结束状态。2、识别播放列表项为了简化播放列表项的识别, MediaSource 可以在⼯⼚类中使⽤⾃定义标签设置每个项⽬ MediaSource ,这可以是uri,标题或任何其他⾃定义对象。可以使⽤查询当前正在播放的项⽬的标签 rentTag 。 rentTimeline 返回的当前值Timeline还包含所有标记作为 对象的⼀部分 。 public void addItem() { // Add mediaId (e.g. uri) as tag to the MediaSource. MediaSource mediaSource = new y(...) .setTag(mediaId) .createMediaSource(uri); iaSource(mediaSource); }

@Override public void onPositionDiscontinuity(@tinuityReason int reason) { // Load metadata for mediaId and update the UI. CustomMetadata metadata = (rentTag()); t(le()); }1234567891、检测播放何时转换到另⼀个项⽬当前播放项⽬更改时,可以调⽤三种类型的事件:tionDiscontinuity 与 reason = TINUITY_REASON_PERIOD_TRANSITION。当播放⾃动从⼀个项⽬转换到下⼀个项⽬时会发⽣这种情况tionDiscontinuity 与 reason = TINUITY_REASON_SEEK。当当前播放项⽬作为搜索操作的⼀部分⽽改变时,例如在呼叫时,会发⽣这种情况 。lineChanged 与 reason = NE_CHANGE_REASON_DYNAMIC 。当播放列表发⽣更改时会发⽣这种情况,例如,如果添加,移动或删除项⽬。在所有情况下,当您的应⽤程序代码收到事件时,您可以查询播放器以确定播放列表中正在播放的项⽬。这可以使⽤诸如rentWindowIndex 和 rentTag 之类的⽅法来完成。如果您只想检测播放列表项⽬更改,则必须与最后⼀个已知的窗⼝索引或标记进⾏⽐较,因为上述事件可能由于其他原因⽽被触发。五、TrackSelector曲⽬选择确定播放器播放哪些可⽤媒体曲⽬。轨道选择是TrackSelector的责任,可以在ExoPlayer构建时提供其实例。 DefaultTrackSelector trackSelector = new DefaultTrackSelector(); SimpleExoPlayer player = pleInstance(context, trackSelector);123DefaultTrackSelector TrackSelector适⽤于⼤多数⽤例的灵活性。使⽤DefaultTrackSelector 时,可以通过修改它来控制它选择的轨道Parameters 。这可以在播放之前或播放期间完成。例如,以下代码告诉选择器将视频轨道选择限制为SD,并选择德语⾳轨(如果有): ameters( trackSelector .buildUponParameters() .setMaxVideoSizeSd() .setPreferredAudioLanguage("deu"));12345这是基于约束的轨道选择的⽰例,其中在不知道实际可⽤的轨道的情况下指定约束。可以使⽤指定许多不同类型的约束 Parameters 。Parameters 也可⽤于从可⽤的轨道中选择特定轨道。有关更多详细信息DefaultTrackSelector,请参阅Parameters和ParametersBuilder⽂档。六、UI组件应⽤程序播放媒体需要⽤户界⾯组件来显⽰媒体和控制播放。ExoPlayer库包含⼀个包含许多UI组件的UI模块。要依赖UI模块添加依赖项,如下所⽰,2.X.X您的⾸选版本在哪⾥(可以通过查阅发⾏说明找到最新版本)。implementation 'yer:exoplayer-ui:2.X.X'1最重要的组件是 PlayerControlView 和 PlayerView 。PlayerControlView是⽤于控制播放的视图。它显⽰标准播放控件,包括播放/暂停按钮,快进和快退按钮以及搜索栏。PlayerView是播放的⾼级视图。它在播放期间显⽰视频,字幕和专辑封⾯,以及使⽤PlayerControlView播放控件 。两个视图都有⼀个 setPlayer 附加和分离(通过传递 null)播放器实例的⽅法。1、PlayerViewPlayerView 可⽤于视频和⾳频播放。它在视频播放的情况下呈现视频和字幕,并且可以在⾳频⽂件中显⽰作为元数据包括的艺术作品。您可以在布局⽂件中像其他UI组件中⼀样,加载PlayerView : 123456上⾯的代码⽚段说明了PlayerView提供了⼏个属性。这些属性可⽤于⾃定义视图的⾏为及其外观。这些属性中的⼤多数都具有相应的setter⽅法,可⽤于在运⾏时⾃定义视图。该 PlayerView的Javadoc⽂档中详细介绍这些属性和setter⽅法。⼀旦PlayerView在布局⽂件中声明,就可以onCreate在活动的⽅法中查找它: @Override protected void onCreate(Bundle savedInstanceState) { te(savedInstanceState); // ... playerView = findViewById(_view); }123456当player初始化时,可以通过调⽤setPlayer以下内容将其附加到视图 : // Instantiate the player. player = pleInstance(context); // Attach player to the view. yer(player); // Prepare the player with the dash media source. e(createMediaSource());1234562、选择Surface类型该surface_type属性PlayerView可让您设置⽤于视频播放的Surface类型。除了值spherical_view(这是球形视频播放⼀个特殊的值)时,允许值是surface_view, texture_view和none。如果视图仅⽤于⾳频播放,则应使⽤none以避免必须创建Surface,因为这样做可能耗费资源。如果视图是⽤于常规视频播放那么 surface_view 或 texture_view 应该使⽤。对于视频播放,相⽐TextureView,SurfaceView有许多好处:显着降低了许多设备的功耗。更准确的帧定时,使视频播放更流畅。播放受DRM保护的内容时⽀持安全输出。因此,相⽐较于TextureView,SurfaceView应尽可能优先考虑。 TextureView只有在SurfaceView不符合您需求的情况下才能使⽤。⼀个⽰例是在Android N之前需要平滑动画或滚动视频表⾯,如下所述。对于这种情况,最好 TextureView 只在SDK_INT⼩于24(AndroidN)时使⽤, 否则,使⽤SurfaceView。SurfaceView在Android N之前,渲染未与视图动画正确同步。在早期版本SurfaceView中,当放⼊滚动容器或受到动画影响时,这可能会导致不必要的效果 。这些效果包括视图的内容看起来略微落后于它应该显⽰的位置,并且视图在受到动画时变⿊。为了在Android N之前实现流畅的动画或视频滚动,因此必须使⽤TextureView⽽不是SurfaceView。3、PlayerControlView当使⽤ PlayerView 时, PlayerControlView 在内部⽤于提供播放控制。对于特定⽤例 PlayerControlView ,也可以⽤作独⽴组件。它可以像任何其他UI组件⼀样包含在布局⽂件中: 1234⽐起 PlayerView , PlayerControlView 的Javadoc更详细地记录了可⽤的属性和setter⽅法。查找 PlayerControlView 并将播放器附加到视图与使⽤时类似PlayerView: @Override protected void onCreate(Bundle savedInstanceState) { te(savedInstanceState); // ... playerControlView = findViewById(_control_view); }

private void initializePlayer() { // Instantiate the player. player = pleInstance(context); // Attach player to the view. yer(player); // Prepare the player with the dash media source. e(createMediaSource()); }1234567891、覆盖布局⽂件当PlayerView被实例化后,它从布局⽂件 exo_player_扩展 (inflate) 它的布局。PlayerControlView从exo_player_control_中扩展其布局。要⾃定义这些布局,应⽤程序可以在其⾃⼰的res/layout*⽬录中定义具有相同名称的布局⽂件。这些布局⽂件会覆盖ExoPlayer库提供的⽂件。例如,假设我们希望我们的播放控件仅包含位于视图中⼼的播放/暂停按钮。我们可以通过exo_player_control_在应⽤程序的res/layout ⽬录中创建⽂件来实现这⼀点,包含:

171819与标准对照相⽐,视觉外观的变化如下所⽰。5、⾃定义布局⽂件覆盖布局⽂件是改变整个应⽤程序布局的绝佳解决⽅案,但是如果仅在⼀个地⽅需要⾃定义布局呢?要实现这⼀点,⾸先要定义⼀个布局⽂件,就像覆盖其中⼀个默认布局⼀样,但这次给它⼀个不同的⽂件名custom_。其次,使⽤属性指⽰在对视图进⾏扩展(inflate)时应使⽤此布局。例如,在使⽤时 PlayerView,可以使⽤以下controller_layout_id属性指定扩展(inflate)以提供播放控件的布局:

android:id="@+id/player_view" android:layout_width="match_parent" android:layout_height="match_parent" app:controller_layout_id="@layout/custom_controls"/>12345七、下载媒体ExoPlayer提供下载媒体以进⾏离线播放的功能。在⼤多数⽤例中,即使您的应⽤程序处于后台,也可以继续下载。对于这些⽤例,您的应⽤程序应该是⼦类DownloadService,并将命令发送到服务以添加,删除和控制下载。下图显⽰了涉及的主要类。DownloadService:包装DownloadManager并转发命令。该服务允许DownloadManager即使应⽤程序在后台运⾏也能继续运⾏。DownloadManager:管理多个下载,根据⽹络连接等要求加载(和存储)来⾃(和) DownloadIndex的状态,启动和停⽌下载。要下载内容,管理员通常会读取从HttpDataSource下载的数据 ,并将其写⼊Cache。DownloadIndex:保持下载状态。1、创建DownloadService要创建⼀个DownloadService,您需要将其⼦类化并实现其抽象⽅法:getDownloadManager():返回DownloadManager要使⽤的。getScheduler():返回⼀个可选项Scheduler,可以在满⾜挂起下载进度所需的需求时重新启动服务。ExoPlayer提供两种实现:PlatformScheduler,它使⽤JobScheduler。JobDispatcherScheduler,它使⽤ Firebase JobDispatcher。getForegroundNotification():返回服务在前台运⾏时要显⽰的通知。您可以使⽤rogressNotification默认样式创建通知。最后,您需要在⽂件中定义服务: 12345678见DemoDownloadService和在⼀个具体的例⼦中ExoPlayer演⽰应⽤程序。2、创建 DownloadManager下⾯的代码⽚段演⽰了如何实例化⼀个DownloadManager,可以通过返回getDownloadManager()在DownloadService: // Note: This should be a singleton in your app. databaseProvider = new ExoDatabaseProvider(context);

// A download cache should not evict media, so should use a NoopCacheEvictor. downloadCache = new SimpleCache( downloadDirectory, new NoOpCacheEvictor(), databaseProvider);

// Create a factory for reading the data from the network. dataSourceFactory = new DefaultHttpDataSourceFactory(userAgent);

// Create the download manager. downloadManager = new DownloadManager( context, databaseProvider, downloadCache, dataSourceFactory);

// Optionally, setters can be called to configure the download manager. uirements(requirements); ParallelDownloads(3);2请参阅DemoApplication演⽰应⽤程序中的具体⽰例。演⽰应⽤程序中的⽰例还从旧 ActionFile 实例导⼊下载状态。只有ActionFile在ExoPlayer 2.10.0之前使⽤您的应⽤程序时才需要这样做。3、添加下载要添加下载,您需要创建⼀个DownloadRequest并将其发送给您 DownloadService。对于⾃适应流,DownloadHelper可以⽤来帮助构建⼀个DownloadRequest,如本页后⾯所述。以下⽰例显⽰了如何为渐进式流创建下载请求: DownloadRequest downloadRequest = new DownloadRequest( contentId, _PROGRESSIVE, contentUri, /* streamKeys= */ ist(), /* customCacheKey= */ null, appData);1234567contentId是内容的唯⼀标识符,appData是应⽤程序希望与下载关联的任何数据。在简单的情况下, contentUri通常可以使⽤contentId,但应⽤程序可以⾃由使⽤任何最适合其⽤例的ID⽅案。创建后,可以将请求发送DownloadService到添加下载: dDownload( context, , downloadRequest, /* foreground= */ false)12345MyDownloadServiceapp的DownloadService⼦类在哪⾥, foreground参数控制是否在前台启动服务。如果你的应⽤程序已经在前台,那么foreground通常应该将参数设置为false,因为DownloadService如果它确定它有⼯作要做,它将把⾃⼰置于前台。4、删除下载可以通过发送删除命令来删除下载DownloadService,其中contentId标识要删除的下载: moveDownload( context, , contentId, /* foreground= */ false)12345您还可以删除所有下载的数据 moveAllDownloads。5、启动和停⽌下载只有满⾜四个条件时才会下载:下载没有停⽌原因(见下⽂)。下载不会暂停。满⾜下载进度的要求。要求可以指定对允许的⽹络类型的约束,以及设备是空闲还是连接到充电器。不超过最⼤并⾏下载数。所有这些条件都可以通过发送命令来控制 DownloadService。(1)、设置和清除下载停⽌原因可以设置停⽌⼀个或所有下载的原因: // Set the stop reason for a single download. tStopReason( context, , contentId, stopReason, /* foreground= */ false);

// Clear the stop reason for a single download. tStopReason( context, , contentId, _REASON_NONE, /* foreground= */ false);1234567891stopReason可以是任何⾮零值(_REASON_NONE = 0是⼀个特殊值,意味着下载没有停⽌)。有多种停⽌下载原因的应⽤可以使⽤不同的值来跟踪每次下载停⽌的原因。设置和清除所有下载的停⽌原因与设置和清除单个下载的停⽌原因的⽅式相同,但contentId应设置为null。当下载具有⾮零停⽌原因时,它将处于该 _STOPPED状态。(2)、暂停和恢复所有下载所有下载都可以暂停并恢复如下: // Pause all downloads. useDownloads( context, , /* foreground= */ false);

// Resume all downloads. sumeDownloads( context, , /* foreground= */ false);11下载暂停时,它将处于该_QUEUED状态。(3)、设置下载要求的进度Requirements可⽤于指定下载继续进⾏必须满⾜的约束。可以通过uirements()在创建时调⽤来设置要求DownloadManager,如上例所⽰。它们也可以通过向以下命令发送命令来动态更改DownloadService: // Set the download requirements. tRequirements( context, , requirements, /* foreground= */ false);123456如果由于未满⾜要求⽽⽆法继续下载,则它将处于该_QUEUED状态。您可以使⽤查询不满⾜的要求MetRequirements()。(4)、设置最⼤并⾏下载数可以通过调⽤设置最⼤并⾏下载数 ParallelDownloads()。这通常在创建时完成DownloadManager,如上例所⽰。如果由于最⼤并⾏下载数量已在进⾏⽽⽆法继续下载,则它将处于该_QUEUED状态。6、查询下载DownloadManager的DownloadIndex可以查询所有下载,包括那些已完成或失败的状态。可以通过调⽤nloadIndex()来获得该DownloadIndex 。然后可以通过调⽤nloads()获得迭代所有下载的游标 。或者,可以通过调⽤nload()查询单个下载的状态。DownloadManager还提供rentDownloads(),仅返回当前状态(即未完成或失败)的下载。此⽅法对于更新显⽰当前下载的进度和状态的通知和其他UI组件⾮常有⽤。7、听取下载您可以添加⼀个侦听器,DownloadManager以便在当前下载更改状态时收到通知:下载进度更新不会触发调⽤er。要更新显⽰下载进度的UI组件,您应该定期查询DownloadManager所需的更新速率。DownloadService 包含此⽰例,定期更新服务前台通知。8、播放下载的内容播放下载的内容类似于播放在线内容,除了从下载Cache⽽不是通过⽹络读取数据。请勿直接从下载⽬录中尝试读取⽂件,这⼀点很重要。⽽是使⽤如下所述的ExoPlayer库类。要播放下载的内容,请使⽤⽤于下载CacheDataSourceFactory的相同Cache实例创建⼀个。使⽤此⼯⼚类,构建⼀个MediaSource播放。您应该使⽤原始内容contentUri(即下载的内容)构建MediaSource,⽽不是指向下载⽬录或其中任何⽂件的URI。 CacheDataSourceFactory dataSourceFactory = new CacheDataSourceFactory( downloadCache, upstreamDataSourceFactory); ProgressiveMediaSource mediaSource = new ProgressiveMediaSource .Factory(dataSourceFactory) .createMediaSource(contentUri); e(mediaSource);1234569、下载和播放⾃适应流⾃适应流(例如DASH,SmoothStreaming和HLS)通常包含多个媒体轨道。通常有多个轨道包含不同质量的相同内容(例如SD,HD和4K视频轨道)。可能还存在包含不同内容的相同类型的多个轨道(例如,不同语⾔的多个⾳轨)。对于流式播放,可以使⽤轨道选择器来选择播放哪个轨道。类似地,对于下载,DownloadHelper可以使⽤来选择下载哪个轨道。DownloadHelper的典型⽤法如下:使⽤其中⼀种⽅法构建⼀个DownloadHelper。使⽤prepare(ck)并准备帮助程序并等待回调。 DownloadHelper downloadHelper = h( contentUri, dataSourceFactory, new DefaultRenderersFactory(context)); e(myCallback);123456可以选择使⽤检查默认选定的曲⽬ getMappedTrackInfo 和 getTrackSelections ,使⽤clearTrackSelections,replaceTrackSelections和addTrackSelection进⾏调整。通过调⽤getDownloadRequest为所选曲⽬创建⼀个DownloadRequest。如上所述,请求可以传递给DownloadService添加下载。使⽤release()释放helper。您可以通过调⽤MediaSource以下内容创建MediaSource播放 : MediaSource mediaSource = MediaSource(downloadRequest, dataSourceFactory);12创建MediaSource的⼈知道已下载了哪些曲⽬,因此只会在播放期间尝试使⽤这些曲⽬。请参阅 PlayerActivity 演⽰应⽤程序中的具体⽰例。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1687823073a48240.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信