Предварительный просмотр панели поиска Exoplayer

Я пытаюсь добавить предварительный просмотр на мою панель поиска в моем exoplayer, как на YouTube или Plex (см. Изображение ниже)

введите здесь описание изображения

Я нашел эту библиотеку, но она еще не обновлена.

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

Я нашел _2 _ при просмотре документа exoplayer. Я предполагаю, что буду использовать эти 3 слушателя, чтобы получить позицию скраба и отобразить соответствующее изображение.


person Biscuit    schedule 19.05.2020    source источник


Ответы (1)


ОБНОВЛЕНИЕ: библиотека обновлена ​​по состоянию на май 2020 года, поэтому вы можете использовать ее напрямую.

Я оставлю код ниже для тех, кто не хочет пользоваться библиотекой.


После поиска и адаптации его к своим потребностям я нашел способ, посмотрев, как обстоят дела у previewSeekBar, и закончил используя то же самое, вот оно:

Мой спрайт состоит из 10 столбцов и 6 строк, каждый квадрат соответствует 1 секунде.

GlideTransformation

private const val MAX_LINES = 6
private const val MAX_COLUMNS = 10
private const val THUMBNAILS_EACH = 1000 // milliseconds
private const val ONE_MINUTE = 60000 // one minute in millisecond

class GlideThumbnailTransformation(position: Long) : BitmapTransformation() {

    private val x: Int
    private val y: Int

    init {
        // Remainder of position on one minute because we just need to know which square of the current miniature
        val square = position.rem(ONE_MINUTE).toInt() / THUMBNAILS_EACH
        y = square / MAX_COLUMNS
        x = square % MAX_COLUMNS
    }

    override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
        val width = toTransform.width / MAX_COLUMNS
        val height = toTransform.height / MAX_LINES
        return Bitmap.createBitmap(toTransform, x * width, y * height, width, height)
    }

    override fun updateDiskCacheKey(messageDigest: MessageDigest) {
        val data: ByteArray = ByteBuffer.allocate(8).putInt(x).putInt(y).array()
        messageDigest.update(data)
    }

    override fun hashCode(): Int {
        return (x.toString() + y.toString()).hashCode()
    }

    override fun equals(other: Any?): Boolean {
        if (other !is GlideThumbnailTransformation) {
            return false
        }
        return other.x == x && other.y == y
    }
}

Действия


val thumbnailUrl = "https://bitdash-a.akamaihd.net/content/MI201109210084_1/thumbnails/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.jpg"
exo_progress.addListener(object : TimeBar.OnScrubListener {
    override fun onScrubMove(timeBar: TimeBar, position: Long) {
        previewFrameLayout.visibility = View.VISIBLE
        val targetX = updatePreviewX(position.toInt(), exoPlayer.duration.toInt())
        previewFrameLayout.x = targetX.toFloat()
        GlideApp.with(scrubbingPreview)
            .load(thumbnailUrl)
            .override(Target.SIZE_ORIGINAL,Target.SIZE_ORIGINAL)
            .transform(GlideThumbnailTransformation(position))
            .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
            .into(scrubbingPreview)
    }

    override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {
        previewFrameLayout.visibility = View.INVISIBLE
    }

    override fun onScrubStart(timeBar: TimeBar, position: Long) {}
})

private fun updatePreviewX(progress: Int, max: Int): Int {
    if (max == 0) { return 0 }

    val parent = previewFrameLayout.parent as ViewGroup
    val layoutParams = previewFrameLayout.layoutParams as MarginLayoutParams
    val offset = progress.toFloat() / max
    val minimumX: Int = previewFrameLayout.left
    val maximumX = (parent.width - parent.paddingRight - layoutParams.rightMargin)

// We remove the padding of the scrubbing, if you have a custom size juste use dimen to calculate this
    val previewPaddingRadius: Int = dpToPx(resources.displayMetrics, DefaultTimeBar.DEFAULT_SCRUBBER_DRAGGED_SIZE_DP).div(2)
    val previewLeftX = (exo_progress as View).left.toFloat()
    val previewRightX = (exo_progress as View).right.toFloat()
    val previewSeekBarStartX: Float = previewLeftX + previewPaddingRadius
    val previewSeekBarEndX: Float = previewRightX - previewPaddingRadius
    val currentX = (previewSeekBarStartX + (previewSeekBarEndX - previewSeekBarStartX) * offset)
    val startX: Float = currentX - previewFrameLayout.width / 2f
    val endX: Float = startX + previewFrameLayout.width

    // Clamp the moves
    return if (startX >= minimumX && endX <= maximumX) {
        startX.toInt()
    } else if (startX < minimumX) {
        minimumX
    } else {
        maximumX - previewFrameLayout.width
    }
}

private fun dpToPx(displayMetrics: DisplayMetrics, dps: Int): Int {
    return (dps * displayMetrics.density).toInt()
}

XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_gravity="bottom"
    android:layoutDirection="ltr"
    android:background="#CC000000"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:paddingTop="4dp"
        android:orientation="horizontal"
        android:id="@+id/controlsLayout"
        app:layout_constraintBottom_toBottomOf="parent">

        <ImageButton android:id="@id/exo_prev"
            style="@style/ExoMediaButton.Previous"/>

        <ImageButton android:id="@id/exo_rew"
            style="@style/ExoMediaButton.Rewind"/>

        <ImageButton android:id="@id/exo_repeat_toggle"
            style="@style/ExoMediaButton"/>

        <ImageButton android:id="@id/exo_play"
            style="@style/ExoMediaButton.Play"/>

        <ImageButton android:id="@id/exo_pause"
            style="@style/ExoMediaButton.Pause"/>

        <ImageButton android:id="@id/exo_ffwd"
            style="@style/ExoMediaButton.FastForward"/>

        <ImageButton android:id="@id/exo_next"
            style="@style/ExoMediaButton.Next"/>

    </LinearLayout>

    <TextView android:id="@id/exo_position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textStyle="bold"
        android:paddingLeft="4dp"
        android:paddingRight="4dp"
        android:includeFontPadding="false"
        android:textColor="#FFBEBEBE"
        app:layout_constraintBottom_toTopOf="@id/controlsLayout"
        app:layout_constraintStart_toStartOf="parent"/>

    <FrameLayout
        android:id="@+id/previewFrameLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:background="@drawable/video_frame"
        android:padding="2dp"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/exo_progress"
        app:layout_constraintDimensionRatio="16:9"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintWidth_default="percent"
        app:layout_constraintWidth_percent="0.25"
        tools:visibility="visible">

        <ImageView
            android:id="@+id/scrubbingPreview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY" />

    </FrameLayout>

    <com.google.android.exoplayer2.ui.DefaultTimeBar
        android:id="@id/exo_progress"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="26dp"
        app:layout_constraintBottom_toBottomOf="@id/exo_position"
        app:layout_constraintEnd_toStartOf="@id/exo_duration"
        app:layout_constraintStart_toEndOf="@+id/exo_position"
        app:layout_constraintTop_toTopOf="@+id/exo_position"/>

    <TextView android:id="@id/exo_duration"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textStyle="bold"
        android:paddingLeft="4dp"
        android:paddingRight="4dp"
        android:includeFontPadding="false"
        android:textColor="#FFBEBEBE"
        app:layout_constraintBaseline_toBaselineOf="@id/exo_position"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

drawable / video_frame

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <stroke
        android:width="2dp"
        android:color="@android:color/white" />

    <solid android:color="@android:color/black" />
</shape>

Возможно, есть некоторые улучшения, поэтому не стесняйтесь комментировать

person Biscuit    schedule 25.05.2020
comment
Вы можете объяснить это [MOSAIQUE_URL], val thumbnailUrl = $ {MOSAIQUE_URL} $ {position.div (60000)}. Jpg - person Tippu Fisal Sheriff; 01.12.2020
comment
это ошибка, удалю из кода, thumbnailUrl выглядит примерно так bitdash-a.akamaihd.net/content/MI201109210084_1/thumbnails/ и благодаря .transform(GlideThumbnailTransformation(position)) я не буду рассчитывать и показывать хороший кадр - person Biscuit; 01.12.2020
comment
Как создавать миниатюрные изображения? - person Tippu Fisal Sheriff; 02.12.2020
comment
Я понятия не имею, как создать такую ​​миниатюру - person Biscuit; 02.12.2020
comment
Как создать ссылку на изображение этого типа (bitdash-a.akamaihd.net/content/MI201109210084_1/thumbnails/…). Потому что на этом изображении у вас есть некоторый набор снимков экрана, связанных с вашим видео. - person Tippu Fisal Sheriff; 02.12.2020