Так что я еще не смог получить все типы шкал, о которых вы просили, но я смог довольно легко настроить посадку и центральную обрезку, используя exo player. Полный код можно увидеть по адресу https://github.com/yperess/StackOverflow/tree/50091878, и я буду обновлять его по мере поступления. В конце концов, я также заполнил MainActivity, чтобы позволить вам выбрать тип масштабирования в качестве настроек (я сделаю это с помощью простой PreferenceActivity) и прочту значение общих настроек на стороне службы.
Общая идея заключается в том, что в глубине MediaCodec уже реализованы и fit-xy, и center-crop, которые на самом деле являются единственными двумя режимами, которые вам понадобятся, если у вас есть доступ к иерархии представлений. Это так, потому что по центру, подгонке сверху, подгонке под ногами все будет просто подгонкой, если поверхность имеет гравитацию и масштабируется в соответствии с размером видео * минимальное масштабирование. Чтобы заставить их работать, я считаю, что нам необходимо создать контекст OpenGL и предоставить SurfaceTexture. Эта SurfaceTexture может быть обернута заглушкой Surface, которую можно передать exo player. Как только видео загружено, мы можем установить их размер, так как мы их создали. У нас также есть обратный вызов на SurfaceTexture, чтобы сообщить нам, когда кадр готов. На этом этапе мы должны быть в состоянии изменить кадр (возможно, просто используя масштабирование матрицы и преобразование).
Ключевыми компонентами здесь являются создание exo player:
private fun initExoMediaPlayer(): SimpleExoPlayer {
val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory(bandwidthMeter)
val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
val player = ExoPlayerFactory.newSimpleInstance(this@MovieLiveWallpaperService,
trackSelector)
player.playWhenReady = true
player.repeatMode = Player.REPEAT_MODE_ONE
player.volume = 0f
if (mode == Mode.CENTER_CROP) {
player.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
} else {
player.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
}
if (mode == Mode.FIT_CENTER) {
player.addVideoListener(this)
}
return player
}
Затем загрузка видео:
override fun onSurfaceCreated(holder: SurfaceHolder) {
super.onSurfaceCreated(holder)
if (mode == Mode.FIT_CENTER) {
// We need to somehow wrap the surface or set some scale factor on exo player here.
// Most likely this will require creating a SurfaceTexture and attaching it to an
// OpenGL context. Then for each frame, writing it to the original surface but with
// an offset
exoMediaPlayer.setVideoSurface(holder.surface)
} else {
exoMediaPlayer.setVideoSurfaceHolder(holder)
}
val videoUri = RawResourceDataSource.buildRawResourceUri(R.raw.small)
val dataSourceFactory = DataSource.Factory { RawResourceDataSource(context) }
val mediaSourceFactory = ExtractorMediaSource.Factory(dataSourceFactory)
exoMediaPlayer.prepare(mediaSourceFactory.createMediaSource(videoUri))
}
UPDATE:
Получил работу, завтра мне нужно будет ее почистить, прежде чем публиковать код, но вот предварительный просмотр ...
То, что я в итоге сделал, в основном взяло GLSurfaceView и разорвало его на части. Если вы посмотрите на источник, единственное, чего не хватает, что делает невозможным его использование в обоях, это тот факт, что он запускает GLThread только при присоединении к окну. Поэтому, если вы реплицируете тот же код, но разрешаете запускать GLThread вручную, вы можете продолжить. После этого вам просто нужно следить за тем, насколько велик ваш экран по сравнению с видео после масштабирования до минимального масштаба, который будет соответствовать, и сдвигать квад, на котором вы рисуете.
Известные проблемы с кодом:
1. Есть небольшая ошибка с GLThread, которую я не смог выловить. Похоже, есть простая проблема с синхронизацией, когда, когда поток останавливается, я получаю вызов signallAll()
, который на самом деле ничего не ждет.
2. Я не удосужился динамически изменить режим в рендере. Это не должно быть слишком сложно. Добавьте прослушиватель предпочтений при создании Engine, а затем обновите средство визуализации при изменении scale_type
.
UPDATE:
Все проблемы были решены. signallAll()
бросал, потому что я пропустил проверку, чтобы увидеть, что у нас действительно есть замок. Я также добавил прослушиватель для динамического обновления типа масштаба, так что теперь все типы масштабирования используют GlEngine.
НАСЛАЖДАЙТЕСЬ!