web-dev-qa-db-fra.com

Comment télécharger une vidéo pendant sa lecture, en utilisant ExoPlayer?

Contexte

Je travaille sur une application qui peut lire de courtes vidéos.

Je veux éviter d'accéder à Internet à chaque fois que l'utilisateur les lit, pour le rendre plus rapide et réduire l'utilisation des données.

Le problème

Actuellement, je n'ai trouvé que la lecture ou le téléchargement (c'est juste un fichier pour que je puisse le télécharger comme n'importe quel autre fichier).

Voici le code de lecture d'un fichier vidéo à partir d'une URL (exemple disponible ici ):

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'
...

manifeste

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

Ce que j'ai essayé

J'ai essayé de lire sur les documents, et j'ai obtenu ces liens (en demandant à ce sujet ici ):

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

Malheureusement, actuellement, la seule solution que je peux trouver est de télécharger le fichier sur un autre thread, ce qui entraînera pour l'appareil 2 connexions, utilisant ainsi deux fois la bande passante.

Questions

  1. Comment puis-je utiliser ExoPlayer pour lire un fichier vidéo, tout en le téléchargeant sur un chemin de fichier?
  2. Existe-t-il un moyen d'activer un mécanisme de mise en cache (qui utilise le disque) sur ExoPlayer dans le même but?

Remarque: pour être clair. Je ne veux pas télécharger le fichier et ensuite seulement le lire.


EDIT: J'ai trouvé un moyen d'obtenir et d'utiliser le fichier à partir du cache de l'API (écrit à ce sujet ici ), mais il semble que cela est considéré comme dangereux (écrit ici ).

Donc, étant donné le mécanisme de cache simple pris en charge par l'API d'ExoPlayer, mes questions actuelles sont les suivantes:

  1. Si un fichier a été mis en cache, comment puis-je l'utiliser en toute sécurité?
  2. Si un fichier a été partiellement mis en cache (ce qui signifie que nous en avons téléchargé une partie), comment puis-je continuer à le préparer (sans réellement le lire ou attendre la fin de la lecture) jusqu'à ce que je puisse l'utiliser (de manière sûre bien sûr )?

J'ai créé un dépôt Github pour cela ici . Vous pouvez l'essayer.

6
android developer

vous pouvez utiliser SimpleCache d'exoplayer avec LeastRecentlyUsedCacheEvictor pour mettre en cache pendant la diffusion. Le code ressemblerait à quelque chose.

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