Как сделать подобный механизм центра обрезки на PlayerView ExoPlayer, но не в центре?

Вопрос задан: 1 год назад Последняя активность: 7 месяцев назад
up 18 down

Фон

Мы записываем видео лица пользователя, и обычно лицо находится в верхней половине видео.

Позже мы хотим посмотреть видео, но соотношение сторон PlayerView может отличаться от видео, поэтому требуется некоторое масштабирование и обрезка.

Эта проблема

Единственный способ найти PlayerView так что он будет отображаться во всем имеющемся пространстве, но сохраняя пропорции (что, конечно, приведет к кадрированию), используя app:resize_mode="zoom" , Вот пример того, как это работает с центральной культурой: http://s000.tinyupload.com/?file_id=00574047057406286563 , Чем больше просмотров, которые показывают контент, имеют одинаковое соотношение сторон, тем меньше требуется обрезка.

Но это только для центра, то есть он берет точку 0,5x0,5 видео и масштабирует кадры с этой точки. Это вызывает много случаев потери важного содержания видео.

Например, если у нас есть видео, которое было снято в портретной ориентации, и у нас есть квадратный PlayerView и мы хотим показать верхнюю область, это та часть, которая будет видна:

Как сделать подобный механизм центра обрезки на PlayerView ExoPlayer, но не в центре?

Конечно, если сам контент является квадратным, а представления также квадратными, он должен показывать весь контент без обрезки.

Что я пробовал

Я пробовал поиск через Интернет, StackOverflow (здесь) и на Github, но я не мог найти, как это сделать. Единственная подсказка, которую я нашел, касается AspectRatioFrameLayout и AspectRatioTextureView, но я не нашел, как использовать их для этой задачи, если это вообще возможно.

Мне сказали (вот) что я должен использовать нормальный TextureView и предоставить его непосредственно SimpleExoPlayer с помощью SimpleExoPlayer.setVideoTextureView. И установить специальное преобразование к нему, используя TextureView.setTransform.

После много попыток, что лучше всего использовать (и глядя на видео-репозиторий , Хранилище SuperImageView , а также JCropImageView репозиторий которые имеют примеры масштаба/обрезки ImageView и видео), я опубликовал рабочий образец, который, кажется, показывает видео правильно, но я все еще не уверен в этом, так как я также использую ImageView, который показан сверху прежде чем он начнет играть (чтобы иметь более хороший переход вместо черного контента).

Вот текущий код:

class MainActivity : AppCompatActivity() {
    private val imageResId = R.drawable.test
    private val videoResId = R.raw.test
    private val percentageY = 0.2f
    private var player: SimpleExoPlayer? = null


    override fun onCreate(savedInstanceState: Bundle?) {
        window.setBackgroundDrawable(ColorDrawable(0xff000000.toInt()))
        super.onCreate(savedInstanceState)
        if (cache == null) {
            cache = SimpleCache(File(cacheDir, "media"), LeastRecentlyUsedCacheEvictor(MAX_PREVIEW_CACHE_SIZE_IN_BYTES))
        }
        setContentView(R.layout.activity_main)
//        imageView.visibility = View.INVISIBLE
        imageView.setImageResource(imageResId)
        imageView.doOnPreDraw {
            imageView.imageMatrix = prepareMatrixForImageView(imageView, imageView.drawable.intrinsicWidth.toFloat(), imageView.drawable.intrinsicHeight.toFloat())
//            imageView.imageMatrix = prepareMatrix(imageView, imageView.drawable.intrinsicWidth.toFloat(), imageView.drawable.intrinsicHeight.toFloat())
//            imageView.visibility = View.VISIBLE
        }
    }

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

    private fun prepareMatrix(view: View, contentWidth: Float, contentHeight: Float): Matrix {
        var scaleX = 1.0f
        var scaleY = 1.0f
        val viewWidth = view.measuredWidth.toFloat()
        val viewHeight = view.measuredHeight.toFloat()
        Log.d("AppLog", "viewWidth $viewWidth viewHeight $viewHeight contentWidth:$contentWidth contentHeight:$contentHeight")
        if (contentWidth > viewWidth && contentHeight > viewHeight) {
            scaleX = contentWidth / viewWidth
            scaleY = contentHeight / viewHeight
        } else if (contentWidth < viewWidth && contentHeight < viewHeight) {
            scaleY = viewWidth / contentWidth
            scaleX = viewHeight / contentHeight
        } else if (viewWidth > contentWidth)
            scaleY = viewWidth / contentWidth / (viewHeight / contentHeight)
        else if (viewHeight > contentHeight)
            scaleX = viewHeight / contentHeight / (viewWidth / contentWidth)
        val matrix = Matrix()
        val pivotPercentageX = 0.5f
        val pivotPercentageY = percentageY

        matrix.setScale(scaleX, scaleY, viewWidth * pivotPercentageX, viewHeight * pivotPercentageY)
        return matrix
    }

    private fun prepareMatrixForVideo(view: View, contentWidth: Float, contentHeight: Float): Matrix {
        val msWidth = view.measuredWidth
        val msHeight = view.measuredHeight
        val matrix = Matrix()
        matrix.setScale(1f, (contentHeight / contentWidth) * (msWidth.toFloat() / msHeight), msWidth / 2f, percentageY * msHeight) /*,msWidth/2f,msHeight/2f*/
        return matrix
    }

    private fun prepareMatrixForImageView(view: View, contentWidth: Float, contentHeight: Float): Matrix {
        val dw = contentWidth
        val dh = contentHeight
        val msWidth = view.measuredWidth
        val msHeight = view.measuredHeight
//        Log.d("AppLog", "viewWidth $msWidth viewHeight $msHeight contentWidth:$contentWidth contentHeight:$contentHeight")
        val scalew = msWidth.toFloat() / dw
        val theoryh = (dh * scalew).toInt()
        val scaleh = msHeight.toFloat() / dh
        val theoryw = (dw * scaleh).toInt()
        val scale: Float
        var dx = 0
        var dy = 0
        if (scalew > scaleh) { // fit width
            scale = scalew
//            dy = ((msHeight - theoryh) * 0.0f + 0.5f).toInt() // + 0.5f for rounding
        } else {
            scale = scaleh
            dx = ((msWidth - theoryw) * 0.5f + 0.5f).toInt() // + 0.5f for rounding
        }
        dy = ((msHeight - theoryh) * percentageY + 0.5f).toInt() // + 0.5f for rounding
        val matrix = Matrix()
//        Log.d("AppLog", "scale:$scale dx:$dx dy:$dy")
        matrix.setScale(scale, scale)
        matrix.postTranslate(dx.toFloat(), dy.toFloat())
        return matrix
    }

    private fun playVideo() {
        player = ExoPlayerFactory.newSimpleInstance([email protected], DefaultTrackSelector())
        player!!.setVideoTextureView(textureView)
        player!!.addVideoListener(object : VideoListener {
            override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
                super.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio)
                Log.d("AppLog", "onVideoSizeChanged: $width $height")
                val videoWidth = if (unappliedRotationDegrees % 180 == 0) width else height
                val videoHeight = if (unappliedRotationDegrees % 180 == 0) height else width
                val matrix = prepareMatrixForVideo(textureView, videoWidth.toFloat(), videoHeight.toFloat())
                textureView.setTransform(matrix)
            }

            override fun onRenderedFirstFrame() {
                Log.d("AppLog", "onRenderedFirstFrame")
                player!!.removeVideoListener(this)
//                imageView.animate().alpha(0f).setDuration(5000).start()
                imageView.visibility = View.INVISIBLE
            }
        })
        player!!.volume = 0f
        player!!.repeatMode = Player.REPEAT_MODE_ALL
        player!!.playRawVideo(this, videoResId)
        player!!.playWhenReady = true
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/240/big_buck_bunny_240p_20mb.mkv", cache!!)
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv", cache!!)
        //        player!!.playVideoFromUrl([email protected], "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv")
    }

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

    companion object {
        const val MAX_PREVIEW_CACHE_SIZE_IN_BYTES = 20L * 1024L * 1024L
        var cache: com.google.android.exoplayer2.upstream.cache.Cache? = null

        @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.playRawVideo(context: Context, @RawRes rawVideoRes: Int) {
        val dataSpec = DataSpec(RawResourceDataSource.buildRawResourceUri(rawVideoRes))
        val rawResourceDataSource = RawResourceDataSource(context)
        rawResourceDataSource.open(dataSpec)
        val factory: DataSource.Factory = DataSource.Factory { rawResourceDataSource }
        prepare(LoopingMediaSource(ExtractorMediaSource.Factory(factory).createMediaSource(rawResourceDataSource.uri)))
    }

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

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

    fun SimpleExoPlayer.playVideoFromUri(context: Context, uri: Uri, cache: Cache? = null) {
        val factory = if (cache != null)
            CacheDataSourceFactory(cache, DefaultHttpDataSourceFactory(getUserAgent(context)))
        else
            DefaultDataSourceFactory(context, MainActivity.getUserAgent(context))
        val mediaSource = ExtractorMediaSource.Factory(factory).createMediaSource(uri)
        prepare(mediaSource)
    }
}

У меня были различные проблемы при попытке это сделать, пока я не попал в текущую ситуацию, и я обновил этот вопрос несколько раз соответственно. Теперь он работает даже с процентом Y, о котором я говорил, поэтому я могу установить его в 20% от верхней части видео, если я захочу. Тем не менее, я все еще думаю, что есть большая вероятность, что что-то не так, потому что, когда я попытался установить его на 50%, я заметил, что контент может не соответствовать всему представлению.

Я даже посмотрел на исходный код ImageView (вот), чтобы увидеть, как используется центральная культура. Применительно к ImageView он все еще работал как центральная обрезка, но когда я использовал ту же технику на видео, это дало мне очень неправильный результат.

Вопросы

Моя цель здесь состояла в том, чтобы показать и ImageView, и видео, чтобы оно плавно переходило от статического изображения к видео. Все это, хотя оба имеют высший урожай 20% от верха (например). Я опубликовал пример проекта вот попробовать и поделиться людьми с тем, что я нашел.

Итак, теперь мои вопросы связаны с тем, почему это не очень хорошо работает для imageView и/или видео:

  1. Оказывается, ни одно из созданных матриц, которые я пробовал, не работает ни для ImageView, ни для видео. Что именно с этим не так? Как я могу изменить его, чтобы они выглядели одинаково? Например, чтобы масштабировать урожай из лучших 20%?

  2. Я пытался использовать точную матрицу для обоих, но, похоже, каждому это нужно по-разному, хотя оба имеют одинаковый размер и размер контента. Зачем мне нужна другая матрица для каждого?


РЕДАКТИРОВАТЬ: после того, как на этот вопрос был дан ответ, я решил сделать небольшой пример того, как его использовать (доступен репозиторий Github) вот) :

import android.content.Context
import android.graphics.Matrix
import android.graphics.PointF
import android.net.Uri
import android.os.Bundle
import android.view.TextureView
import android.view.View
import androidx.annotation.RawRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.doOnPreDraw
import com.google.android.exoplayer2.ExoPlayerFactory
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.LoopingMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.upstream.*
import com.google.android.exoplayer2.upstream.cache.Cache
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
import com.google.android.exoplayer2.upstream.cache.SimpleCache
import com.google.android.exoplayer2.util.Util
import com.google.android.exoplayer2.video.VideoListener
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File

// https://stackoverflow.com/questions/54216273/how-to-have-similar-mechanism-of-center-crop-on-exoplayers-playerview-but-not
class MainActivity : AppCompatActivity() {
    companion object {
        private val FOCAL_POINT = PointF(0.5f, 0.2f)
        private const val IMAGE_RES_ID = R.drawable.test
        private const val VIDEO_RES_ID = R.raw.test
        private var cache: Cache? = null
        private const val MAX_PREVIEW_CACHE_SIZE_IN_BYTES = 20L * 1024L * 1024L

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

    private var player: SimpleExoPlayer? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (cache == null)
            cache = SimpleCache(File(cacheDir, "media"), LeastRecentlyUsedCacheEvictor(MAX_PREVIEW_CACHE_SIZE_IN_BYTES))
        //        imageView.visibility = View.INVISIBLE
        imageView.setImageResource(IMAGE_RES_ID)
    }

    private fun prepareMatrix(view: View, mediaWidth: Float, mediaHeight: Float, focalPoint: PointF): Matrix? {
        if (view.visibility == View.GONE)
            return null
        val viewHeight = (view.height - view.paddingTop - view.paddingBottom).toFloat()
        val viewWidth = (view.width - view.paddingStart - view.paddingEnd).toFloat()
        if (viewWidth <= 0 || viewHeight <= 0)
            return null
        val matrix = Matrix()
        if (view is TextureView)
        // Restore true media size for further manipulation.
            matrix.setScale(mediaWidth / viewWidth, mediaHeight / viewHeight)
        val scaleFactorY = viewHeight / mediaHeight
        val scaleFactor: Float
        var px = 0f
        var py = 0f
        if (mediaWidth * scaleFactorY >= viewWidth) {
            // Fit height
            scaleFactor = scaleFactorY
            px = -(mediaWidth * scaleFactor - viewWidth) * focalPoint.x / (1 - scaleFactor)
        } else {
            // Fit width
            scaleFactor = viewWidth / mediaWidth
            py = -(mediaHeight * scaleFactor - viewHeight) * focalPoint.y / (1 - scaleFactor)
        }
        matrix.postScale(scaleFactor, scaleFactor, px, py)
        return matrix
    }

    private fun playVideo() {
        player = ExoPlayerFactory.newSimpleInstance([email protected], DefaultTrackSelector())
        player!!.setVideoTextureView(textureView)
        player!!.addVideoListener(object : VideoListener {
            override fun onVideoSizeChanged(videoWidth: Int, videoHeight: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
                super.onVideoSizeChanged(videoWidth, videoHeight, unappliedRotationDegrees, pixelWidthHeightRatio)
                textureView.setTransform(prepareMatrix(textureView, videoWidth.toFloat(), videoHeight.toFloat(), FOCAL_POINT))
            }

            override fun onRenderedFirstFrame() {
                //                Log.d("AppLog", "onRenderedFirstFrame")
                player!!.removeVideoListener(this)
                imageView.animate().alpha(0f).setDuration(2000).start()
                //                imageView.visibility = View.INVISIBLE
            }
        })
        player!!.volume = 0f
        player!!.repeatMode = Player.REPEAT_MODE_ALL
        player!!.playRawVideo(this, VIDEO_RES_ID)
        player!!.playWhenReady = true
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/240/big_buck_bunny_240p_20mb.mkv", cache!!)
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv", cache!!)
        //        player!!.playVideoFromUrl([email protected], "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv")
    }

    override fun onStart() {
        super.onStart()
        imageView.doOnPreDraw {
            val imageWidth: Float = imageView.drawable.intrinsicWidth.toFloat()
            val imageHeight: Float = imageView.drawable.intrinsicHeight.toFloat()
            imageView.imageMatrix = prepareMatrix(imageView, imageWidth, imageHeight, FOCAL_POINT)
        }
        playVideo()
    }

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

    override fun onDestroy() {
        super.onDestroy()
        if (!isChangingConfigurations)
            cache?.release()
    }

    fun SimpleExoPlayer.playRawVideo(context: Context, @RawRes rawVideoRes: Int) {
        val dataSpec = DataSpec(RawResourceDataSource.buildRawResourceUri(rawVideoRes))
        val rawResourceDataSource = RawResourceDataSource(context)
        rawResourceDataSource.open(dataSpec)
        val factory: DataSource.Factory = DataSource.Factory { rawResourceDataSource }
        prepare(LoopingMediaSource(ExtractorMediaSource.Factory(factory).createMediaSource(rawResourceDataSource.uri)))
    }

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

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

    fun SimpleExoPlayer.playVideoFromUri(context: Context, uri: Uri, cache: Cache? = null) {
        val factory = if (cache != null)
            CacheDataSourceFactory(cache, DefaultHttpDataSourceFactory(getUserAgent(context)))
        else
            DefaultDataSourceFactory(context, MainActivity.getUserAgent(context))
        val mediaSource = ExtractorMediaSource.Factory(factory).createMediaSource(uri)
        prepare(mediaSource)
    }
}

Вот решение для одного ImageView, если это необходимо:

class ScaleCropImageView(context: Context, attrs: AttributeSet?) : AppCompatImageView(context, attrs) {
    var focalPoint = PointF(0.5f, 0.5f)
        set(value) {
            field = value
            updateMatrix()
        }
    private val viewWidth: Float
        get() = (width - paddingLeft - paddingRight).toFloat()

    private val viewHeight: Float
        get() = (height - paddingTop - paddingBottom).toFloat()

    init {
        scaleType = ScaleType.MATRIX
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        updateMatrix()
    }

    override fun setImageDrawable(drawable: Drawable?) {
        super.setImageDrawable(drawable)
        updateMatrix()
    }

    @Suppress("MemberVisibilityCanBePrivate")
    fun updateMatrix() {
        if (scaleType != ImageView.ScaleType.MATRIX)
            return
        val dr = drawable ?: return
        imageMatrix = prepareMatrix(
                viewWidth, viewHeight,
                dr.intrinsicWidth.toFloat(), dr.intrinsicHeight.toFloat(), focalPoint, Matrix()
        )
    }

    private fun prepareMatrix(
            viewWidth: Float, viewHeight: Float, mediaWidth: Float, mediaHeight: Float,
            focalPoint: PointF, matrix: Matrix
    ): Matrix? {
        if (viewWidth <= 0 || viewHeight <= 0)
            return null
        var scaleFactor = viewHeight / mediaHeight
        if (mediaWidth * scaleFactor >= viewWidth) {
            // Fit height
            matrix.postScale(scaleFactor, scaleFactor, -(mediaWidth * scaleFactor - viewWidth) * focalPoint.x / (1 - scaleFactor), 0f)
        } else {
            // Fit width
            scaleFactor = viewWidth / mediaWidth
            matrix.postScale(scaleFactor, scaleFactor, 0f, -(mediaHeight * scaleFactor - viewHeight) * focalPoint.y / (1 - scaleFactor))
        }
        return matrix
    }
}

1 ответ

Возможно, для Вашего проекта будут необходимы бесплатные векторные карты. На нашем сайте представлены карты для всех стран.

Реклама

up 8 down accepted

Вопрос в том, как манипулировать изображением, как ImageView.ScaleType.CENTER_CROP но сместить фокус из центра в другое место, которое находится на 20% от верхней части изображения. Во-первых, давайте посмотрим, что CENTER_CROP делает:

Из документация:

CENTER_CROP

Равномерно масштабируйте изображение (сохраняйте пропорции изображения), чтобы оба размера (ширина и высота) изображения были равны или превышали соответствующий размер вида (за вычетом отступов). Затем изображение центрируется на виде. Из XML используйте этот синтаксис: android:scaleType="centerCrop".

Другими словами, масштабируйте изображение без искажений так, чтобы ширина или высота изображения (или ширина и высота) вписывались в вид, чтобы вид был полностью заполнен изображением (без пропусков).

Еще один способ думать об этом заключается в том, что центр изображения «прикреплен» к центру изображения. Затем изображение масштабируется в соответствии с указанными выше критериями.

В следующем видео белые линии отмечают центр изображения; красные линии отмечают центр зрения. Тип шкалы CENTER_CROP. Обратите внимание, как центральные точки изображения и вида совпадают. При изменении размера представления эти две точки продолжают перекрываться и всегда отображаются в центре представления независимо от размера представления.

Как сделать подобный механизм центра обрезки на PlayerView ExoPlayer, но не в центре?

Итак, что означает иметь подобное центру поведение в другом месте, например, на 20% сверху? Как и в центре обрезки, мы можем указать, что точка, которая находится в 20% от верхней части изображения, и точка, в которой 20% от верхней части вида будут «закреплены», как точка 50% «закреплена» в центре обрезки. Горизонтальное расположение этой точки остается на уровне 50% изображения и вида. Теперь изображение можно масштабировать для соответствия другим условиям обрезки по центру, которые указывают, что ширина и/или высота изображения будут соответствовать виду без зазоров. (Под размером представления понимается размер представления за вычетом отступов.)

Вот короткое видео об этом 20% поведении урожая. В этом видео белые линии показывают середину изображения, красные линии показывают закрепленную точку на виде, а синяя линия, которая отображается за горизонтальной красной линией, обозначает 20% от верхней части изображения. (Демонстрационный проект включен GitHub.

Как сделать подобный механизм центра обрезки на PlayerView ExoPlayer, но не в центре?

Вот результат, показывающий полное изображение, которое было предоставлено, и видео в квадратной рамке, переходящей из неподвижного изображения. ,

Как сделать подобный механизм центра обрезки на PlayerView ExoPlayer, но не в центре?

MainActivity.kt
prepareMatrix() это метод, который определяет, как масштабировать/обрезать изображение. С видео необходимо проделать дополнительную работу, поскольку создается впечатление, что оно сделано в соответствии с TextureViewкак тип шкалы "FIT_XY", когда он назначен TextureView. Из-за этого масштабирования размер носителя должен быть восстановлен до prepareMatrix() называется для видео

class MainActivity : AppCompatActivity() {
    private val imageResId = R.drawable.test
    private val videoResId = R.raw.test
    private var player: SimpleExoPlayer? = null
    private val mFocalPoint = PointF(0.5f, 0.2f)

    override fun onCreate(savedInstanceState: Bundle?) {
        window.setBackgroundDrawable(ColorDrawable(0xff000000.toInt()))
        super.onCreate(savedInstanceState)
        if (cache == null) {
            cache = SimpleCache(File(cacheDir, "media"), LeastRecentlyUsedCacheEvictor(MAX_PREVIEW_CACHE_SIZE_IN_BYTES))
        }
        setContentView(R.layout.activity_main)
        //        imageView.visibility = View.INVISIBLE
        imageView.setImageResource(imageResId)
        imageView.doOnPreDraw {
            imageView.scaleType = ImageView.ScaleType.MATRIX
            val imageWidth: Float = ContextCompat.getDrawable(this, imageResId)!!.intrinsicWidth.toFloat()
            val imageHeight: Float = ContextCompat.getDrawable(this, imageResId)!!.intrinsicHeight.toFloat()
            imageView.imageMatrix = prepareMatrix(imageView, imageWidth, imageHeight, mFocalPoint, Matrix())
            val b = BitmapFactory.decodeResource(resources, imageResId)
            val d = BitmapDrawable(resources, b.copy(Bitmap.Config.ARGB_8888, true))
            val c = Canvas(d.bitmap)
            val p = Paint()
            p.color = resources.getColor(android.R.color.holo_red_dark)
            p.style = Paint.Style.STROKE
            val strokeWidth = 10
            p.strokeWidth = strokeWidth.toFloat()
            // Horizontal line
            c.drawLine(0f, imageHeight * mFocalPoint.y, imageWidth, imageHeight * mFocalPoint.y, p)
            // Vertical line
            c.drawLine(imageWidth * mFocalPoint.x, 0f, imageWidth * mFocalPoint.x, imageHeight, p)
            // Line in horizontal and vertical center
            p.color = resources.getColor(android.R.color.white)
            c.drawLine(imageWidth / 2, 0f, imageWidth / 2, imageHeight, p)
            c.drawLine(0f, imageHeight / 2, imageWidth, imageHeight / 2, p)

            imageView.setImageBitmap(d.bitmap)
            imageViewFull.setImageBitmap(d.bitmap)
        }
    }

    fun startPlay(view: View) {
        playVideo()
    }

    private fun getViewWidth(view: View): Float {
        return (view.width - view.paddingStart - view.paddingEnd).toFloat()
    }

    private fun getViewHeight(view: View): Float {
        return (view.height - view.paddingTop - view.paddingBottom).toFloat()
    }

    private fun prepareMatrix(targetView: View, mediaWidth: Float, mediaHeight: Float,
                              focalPoint: PointF, matrix: Matrix): Matrix {
        if (targetView.visibility != View.VISIBLE) {
            return matrix
        }
        val viewHeight = getViewHeight(targetView)
        val viewWidth = getViewWidth(targetView)
        val scaleFactorY = viewHeight / mediaHeight
        val scaleFactor: Float
        val px: Float
        val py: Float
        if (mediaWidth * scaleFactorY >= viewWidth) {
            // Fit height
            scaleFactor = scaleFactorY
            px = -(mediaWidth * scaleFactor - viewWidth) * focalPoint.x / (1 - scaleFactor)
            py = 0f
        } else {
            // Fit width
            scaleFactor = viewWidth / mediaWidth
            px = 0f
            py = -(mediaHeight * scaleFactor - viewHeight) * focalPoint.y / (1 - scaleFactor)
        }
        matrix.postScale(scaleFactor, scaleFactor, px, py)
        return matrix
    }

    private fun playVideo() {
        player = ExoPlayerFactory.newSimpleInstance([email protected], DefaultTrackSelector())
        player!!.setVideoTextureView(textureView)
        player!!.addVideoListener(object : VideoListener {
            override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
                super.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio)
                val matrix = Matrix()
                // Restore true media size for further manipulation.
                matrix.setScale(width / getViewWidth(textureView), height / getViewHeight(textureView))
                textureView.setTransform(prepareMatrix(textureView, width.toFloat(), height.toFloat(), mFocalPoint, matrix))
            }

            override fun onRenderedFirstFrame() {
                Log.d("AppLog", "onRenderedFirstFrame")
                player!!.removeVideoListener(this)
                imageView.animate().alpha(0f).setDuration(2000).start()
                imageView.visibility = View.INVISIBLE
            }
        })
        player!!.volume = 0f
        player!!.repeatMode = Player.REPEAT_MODE_ALL
        player!!.playRawVideo(this, videoResId)
        player!!.playWhenReady = true
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/240/big_buck_bunny_240p_20mb.mkv", cache!!)
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv", cache!!)
        //        player!!.playVideoFromUrl([email protected], "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv")
    }

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

    companion object {
        const val MAX_PREVIEW_CACHE_SIZE_IN_BYTES = 20L * 1024L * 1024L
        var cache: com.google.android.exoplayer2.upstream.cache.Cache? = null

        @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.playRawVideo(context: Context, @RawRes rawVideoRes: Int) {
        val dataSpec = DataSpec(RawResourceDataSource.buildRawResourceUri(rawVideoRes))
        val rawResourceDataSource = RawResourceDataSource(context)
        rawResourceDataSource.open(dataSpec)
        val factory: DataSource.Factory = DataSource.Factory { rawResourceDataSource }
        prepare(LoopingMediaSource(ExtractorMediaSource.Factory(factory).createMediaSource(rawResourceDataSource.uri)))
    }

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

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

    fun SimpleExoPlayer.playVideoFromUri(context: Context, uri: Uri, cache: Cache? = null) {
        val factory = if (cache != null)
            CacheDataSourceFactory(cache, DefaultHttpDataSourceFactory(getUserAgent(context)))
        else
            DefaultDataSourceFactory(context, MainActivity.getUserAgent(context))
        val mediaSource = ExtractorMediaSource.Factory(factory).createMediaSource(uri)
        prepare(mediaSource)
    }
}

Ошибка 505

Что-то пошло не так

Попробуйте воспользоваться поиском