首页 文章

如何在播放时下载视频,使用ExoPlayer?

提问于
浏览
0

背景

我正在开发一款可以播放一些短视频的应用 .

我希望每次用户播放时都避免访问Internet,以使其更快并降低数据使用量 .

问题

目前我只发现了如何播放或下载(它只是一个文件,所以我可以像任何其他文件一样下载它) .

以下是从URL播放视频文件的代码(示例可用 here ):

gradle

...
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.exoplayer:exoplayer-core:2.8.4'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.8.4'
...

manifest

<manifest package="com.example.user.myapplication" xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <application
        android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"
        tools:ignore="AllowBackup,GoogleAppIndexingWarning">
        <activity
            android:name=".MainActivity" android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

activity_main.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" tools:context=".MainActivity">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/playerView" android:layout_width="match_parent" android:layout_height="match_parent"
        app:resize_mode="zoom"/>
</FrameLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private var player: SimpleExoPlayer? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()
        playVideo()
    }

    private fun playVideo() {
        player = ExoPlayerFactory.newSimpleInstance(this@MainActivity, DefaultTrackSelector())
        playerView.player = player
        player!!.addVideoListener(object : VideoListener {
            override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
            }

            override fun onRenderedFirstFrame() {
                Log.d("appLog", "onRenderedFirstFrame")
            }
        })
        player!!.addListener(object : PlayerEventListener() {
            override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
                super.onPlayerStateChanged(playWhenReady, playbackState)
                when (playbackState) {
                    Player.STATE_READY -> Log.d("appLog", "STATE_READY")
                    Player.STATE_BUFFERING -> Log.d("appLog", "STATE_BUFFERING")
                    Player.STATE_IDLE -> Log.d("appLog", "STATE_IDLE")
                    Player.STATE_ENDED -> Log.d("appLog", "STATE_ENDED")
                }
            }
        })
        player!!.volume = 0f
        player!!.playWhenReady = true
        player!!.repeatMode = Player.REPEAT_MODE_ALL
        player!!.playVideoFromUrl(this@MainActivity, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv")
    }

    override fun onStop() {
        super.onStop()
        playerView.player = null
        player!!.release()
        player = null
    }


    abstract class PlayerEventListener : Player.EventListener {
        override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {}
        override fun onSeekProcessed() {}
        override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {}
        override fun onPlayerError(error: ExoPlaybackException?) {}
        override fun onLoadingChanged(isLoading: Boolean) {}
        override fun onPositionDiscontinuity(reason: Int) {}
        override fun onRepeatModeChanged(repeatMode: Int) {}
        override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}
        override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {}
        override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {}
    }

    companion object {
        @JvmStatic
        fun getUserAgent(context: Context): String {
            val packageManager = context.packageManager
            val info = packageManager.getPackageInfo(context.packageName, 0)
            val appName = info.applicationInfo.loadLabel(packageManager).toString()
            return Util.getUserAgent(context, appName)
        }
    }

    fun SimpleExoPlayer.playVideoFromUri(context: Context, uri: Uri) {
        val dataSourceFactory = DefaultDataSourceFactory(context, MainActivity.getUserAgent(context))
        val mediaSource = ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
        prepare(mediaSource)
    }


    fun SimpleExoPlayer.playVideoFromUrl(context: Context, url: String) = playVideoFromUri(context, Uri.parse(url))

    fun SimpleExoPlayer.playVideoFile(context: Context, file: File) = playVideoFromUri(context, Uri.fromFile(file))
}

我尝试过的

我试过阅读文档,并获得这些链接(通过询问它here):

https://medium.com/google-exoplayer/downloading-streams-6d259eec7f95 https://medium.com/google-exoplayer/downloading-adaptive-streams-37191f9776e

令人遗憾的是,目前我能想出的唯一解决方案是在另一个线程上下载文件,这将导致设备与其 Build 2个连接,从而使用两倍的带宽 .

问题

  • 如何使用ExoPlayer播放视频文件,同时将其下载到某个文件路径?

  • 有没有办法在ExoPlayer上启用缓存机制(使用磁盘)以实现完全相同的目的?

注意:说清楚 . 我不想下载该文件,然后才播放它 .

3 回答

  • 0

    是的,不久前,我认为问题也是如此 . 可能需要在客户端和服务器之间 Build 连接 .

    我在github网站上找到了一个项目 . 它可能会解决你的问题 .

    如下所示:AndroidVideoCache

  • 0

    您可以使用Exoplayer的SimpleCache和LeastRecentlyUsedCacheEvictor在流式传输时进行缓存 . 代码看起来像 .

    temporaryCache = new SimpleCache(new File(context.getExternalCacheDir(), "player"), new LeastRecentlyUsedCacheEvictor(bytesToCache));
    cacheSourceFactory = new CacheDataSourceFactory(temporaryCache, buildDataSourceFactory(), CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
    
  • 0

    好的,我得到了如何使用缓存,但是知道是否有一种方法可以在更流畅的方式下传输时下载文件 .

    这是代码(样本可用here):

    fun SimpleExoPlayer.playVideoFromUriWithCache(context: Context, uri: Uri,cache: Cache) {
        val cacheDataSourceFactory = CacheDataSourceFactory(cache, DefaultHttpDataSourceFactory(getUserAgent(context)))
        val mediaSource = ExtractorMediaSource.Factory(cacheDataSourceFactory).createMediaSource(uri)
        prepare(mediaSource)
    }
    

    用法:

    具有缓存的最大大小 . 示例是10MB:

    const val MAX_PREVIEW_CACHE_SIZE_IN_BYTES = 10L * 1024L * 1024L
    

    在代码中的某个地方,只初始化一次:

    cache = SimpleCache(File(context.cacheDir, "media"), LeastRecentlyUsedCacheEvictor(MAX_PREVIEW_CACHE_SIZE_IN_BYTES))
    

    当需要玩uri时:

    player!!.playVideoFromUriWithCache(context, videoUri, cache)
    

相关问题