如何将Google Cast v3与ExoPlayer v2完全集成?该活动将包含FrameLayout
,其中包含com.google.android.exoplayer2.ui.SimpleExoPlayerView
。 Google tutorial仅涵盖与VideoView
的集成。
答案 0 :(得分:1)
此Gist的Kotlin类中提供了以下代码,该代码应该可以帮助尝试首次设置自己的CastPlayer的人:
https://gist.github.com/stefan-zh/fd52e0ee06088ac4086d2ea3fb7d7f3e
此外,通过Google阅读本教程将对您有帮助:https://codelabs.developers.google.com/codelabs/cast-videos-android/index.html#0
我还使用了本教程来开始学习:https://android.jlelse.eu/sending-media-to-chromecast-has-never-been-easier-c331eeef1e0a
以下是如何使用ExoPlayer及其Cast扩展实现此目的的细分。
// ExoPlayer is an advanced media player for playing media files
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
implementation "com.google.android.exoplayer:extension-cast:$exoplayer_version"
可以在活动的选项菜单中添加“投射”按钮。这是推荐的方法。
将以下内容添加到res/menu/browse.xml
(在我的情况下,菜单文件称为browse.xml
):
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
</menu>
然后将以下代码添加到您的活动中以启用castButton
:
/**
* We need to populate the Cast button across all activities as suggested by Google Cast Guide:
* https://developers.google.com/cast/docs/design_checklist/cast-button#sender-cast-icon-available
*/
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val result = super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.browse, menu)
castButton = CastButtonFactory.setUpMediaRouteButton(applicationContext, menu, R.id.media_route_menu_item)
return result
}
您需要这样做,以便获得包含可投射到的设备列表的选项对话框。将其添加到AndroidManifest.xml
标签中的application
:
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider" />
SessionAvailabilityListener
此界面将允许您监听Cast会话可用性中的更改。根据Cast会话是否可用,您可以将播放定向到本地播放器或远程播放器。
override fun onCastSessionAvailable() {
playOnPlayer(castPlayer)
}
override fun onCastSessionUnavailable() {
playOnPlayer(exoPlayer)
}
请注意我们如何调用castPlayer?.setSessionAvailabilityListener(this)
,其中this
是指您实现SessionAvailabilityListener
接口的活动。 Cast会话可用性更改时,将调用侦听器的方法。
private fun initializePlayers() {
exoPlayer = SimpleExoPlayer.Builder(this).build()
playerView.player = exoPlayer
if (castPlayer == null) {
castPlayer = CastPlayer(castContext)
castPlayer?.setSessionAvailabilityListener(this)
}
// start the playback
if (castPlayer?.isCastSessionAvailable == true) {
playOnPlayer(castPlayer)
} else {
playOnPlayer(exoPlayer)
}
}
playbackPosition
,playWhenReady
或windowIndex
)playOnPlayer()方法:
private fun playOnPlayer(player: Player?) {
if (currentPlayer == player) {
return
}
// save state from the existing player
currentPlayer?.let {
if (it.playbackState != Player.STATE_ENDED) {
it.rememberState()
}
it.stop(true)
}
// set the new player
currentPlayer = player
// set up the playback
// if the current player is the ExoPlayer, play from it
if (currentPlayer == exoPlayer) {
// build the MediaSource from the URI
val uri = Uri.parse(videoClipUrl)
val dataSourceFactory = DefaultDataSourceFactory(this@SampleCastingPlayerActivity, "exoplayer-agent")
val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
// use stored state (if any) to resume (or start) playback
exoPlayer?.playWhenReady = playWhenReady
exoPlayer?.seekTo(currentWindow, playbackPosition)
exoPlayer?.prepare(mediaSource, false, false)
}
// if the current player is the CastPlayer, play from it
if (currentPlayer == castPlayer) {
val metadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
metadata.putString(MediaMetadata.KEY_TITLE, "Title")
metadata.putString(MediaMetadata.KEY_SUBTITLE, "Subtitle")
metadata.addImage(WebImage(Uri.parse("any-image-url")))
val mediaInfo = MediaInfo.Builder(videoClipUrl)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(MimeTypes.VIDEO_MP4)
.setMetadata(metadata)
.build()
val mediaItem = MediaQueueItem.Builder(mediaInfo).build()
castPlayer?.loadItem(mediaItem, playbackPosition)
}
}
每次在后台或前台之间切换应用程序时,都需要释放或请求资源。每次将播放器的资源释放回系统时,都需要保存其状态。
/**
* Remembers the state of the playback of this Player.
*/
private fun Player.rememberState() {
this@SampleCastingPlayerActivity.playWhenReady = playWhenReady
this@SampleCastingPlayerActivity.playbackPosition = currentPosition
this@SampleCastingPlayerActivity.currentWindow = currentWindowIndex
}
/**
* Releases the resources of the local player back to the system.
*/
private fun releaseLocalPlayer() {
exoPlayer?.release()
exoPlayer = null
playerView.player = null
}
/**
* Releases the resources of the remote player back to the system.
*/
private fun releaseRemotePlayer() {
castPlayer?.setSessionAvailabilityListener(null)
castPlayer?.release()
castPlayer = null
}
答案 1 :(得分:0)
Google Cast SDK独立于本地播放器,您可以使用ExoPlayer
或MediaPlayer
(VideoView
)
一旦您的APP有活动会话,请将网址放在MediaInfo
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
movieMetadata.putString(MediaMetadata.KEY_TITLE, "Title")
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, "Sub")
val mediaLoadOptions = MediaInfo.Builder( < URL > )
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(< Content Type of Media>)
.setMetadata(movieMetadata)
.setStreamDuration(<Media Duration >)
.build()
mCastSession.remoteMediaClient.load(buildMediaInfo(url), mediaLoadOptions)
如果您需要流式传输本地媒体,则需要使用NanoHttpd或您选择的其他媒体自行流式传输,并实施Cast Receiver