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.
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))
}
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.
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:
J'ai créé un dépôt Github pour cela ici . Vous pouvez l'essayer.
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);