【问题标题】:How to integrate Google Cast v3 with ExoPlayer v2?如何将 Google Cast v3 与 ExoPlayer v2 集成?
【发布时间】:2017-08-24 13:03:17
【问题描述】:

如何将 Google Cast v3 与 ExoPlayer v2 完全集成?该活动将包含一个FrameLayout,其中包含一个com.google.android.exoplayer2.ui.SimpleExoPlayerViewGoogle tutorial 仅涵盖与 VideoView 的集成。

【问题讨论】:

    标签: android google-cast exoplayer


    【解决方案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 扩展实现此目的的详细说明。

    1。您将需要这些依赖项:

    // 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"
    

    2。您将需要 Cast 按钮

    Cast 按钮可以添加到活动的选项菜单中。这是推荐的方法。

    将以下内容添加到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>
    

    然后将以下代码添加到您的 Activity 以启用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
    }
    

    3。为 Cast 上下文声明选项提供程序

    您需要这个,以便获得包含您可以投射到的设备列表的选项对话框。将此添加到您的 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" />
    

    4。你的Activity需要实现ExoPlayer的Cast Extension接口SessionAvailabilityListener

    此界面将允许您监听 Cast 会话可用性的变化。根据 Cast 会话是否可用,您可以将播放定向到本地播放器或远程播放器。

    override fun onCastSessionAvailable() {
        playOnPlayer(castPlayer)
    }
    
    override fun onCastSessionUnavailable() {
        playOnPlayer(exoPlayer)
    }
    

    5。您将需要逻辑来初始化播放器:

    注意我们如何调用castPlayer?.setSessionAvailabilityListener(this),其中this 指的是实现SessionAvailabilityListener 接口的Activity。当 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)
        }
    }
    

    6。您需要在所选播放器上播放的逻辑:

    • 此方法允许您存储播放状态(playbackPositionplayWhenReadywindowIndex
    • 为本地或远程播放器创建正确的媒体类型
    • 选择应该开始播放的播放器

    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)
        }
    }
    

    7。记住状态并清理资源

    每次在后台或前台之间切换应用程序时,您都需要释放或请求资源。每次您将播放器的资源释放回系统时,您都需要保存其状态。

    /**
     * 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
    }
    

    【讨论】:

    • 是否可以更改 DefaultCastOptionsProvider?我尝试将 Cast MiniControllerFragment 添加到应用程序中,当我点击它时,ExpandedControllerActivity 没有按应有的方式打开。
    • 是的,有可能。您可以编写自己的类来扩展OptionsProvider 并覆盖getCastOptions(context: Context?): CastOptionsgetAdditionalSessionProviders(context: Context?): MutableList&lt;SessionProvider&gt;。然后,您将在 AndroidManifest.xml 中使用此类的完全限定名称
    【解决方案2】:

    Google Cast SDK 独立于本地播放器,您可以使用ExoPlayerMediaPlayer (VideoView)

    一旦您的 APP 有活动会话,请将 url 放入 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-02
      • 2011-11-23
      • 1970-01-01
      • 1970-01-01
      • 2015-12-23
      • 1970-01-01
      • 2017-01-07
      • 2018-11-05
      相关资源
      最近更新 更多