Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ dependencies {
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:extension-leanback:$exoplayer_version"
implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"

// Google Play Services for identity
def playservices_version = "20.1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,33 @@
*/
package com.android.tv.reference.playback

import android.R
import android.net.Uri
import android.os.Bundle
import android.support.v4.media.session.MediaSessionCompat
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.leanback.app.VideoSupportFragment
import androidx.leanback.app.VideoSupportFragmentGlueHost
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.android.tv.reference.R
import com.android.tv.reference.castconnect.CastHelper
import com.android.tv.reference.repository.VideoRepositoryFactory
import com.android.tv.reference.shared.datamodel.Video
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.ForwardingPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.ext.leanback.LeanbackPlayerAdapter
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.ui.SubtitleView
import com.google.android.exoplayer2.upstream.DefaultDataSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import com.google.android.exoplayer2.util.MimeTypes
import com.google.android.gms.cast.tv.CastReceiverContext
import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.Duration


/** Fragment that plays video content with ExoPlayer. */
class PlaybackFragment : VideoSupportFragment() {

Expand Down Expand Up @@ -104,7 +105,9 @@ class PlaybackFragment : VideoSupportFragment() {

override fun onStart() {
super.onStart()
initializePlayer()
lifecycleScope.launch {
initializePlayer()
}
}

override fun onStop() {
Expand All @@ -119,11 +122,43 @@ class PlaybackFragment : VideoSupportFragment() {
CastReceiverContext.getInstance().mediaManager.setSessionCompatToken(null)
}

private fun initializePlayer() {
private suspend fun initializePlayer() {
val videoRepository = VideoRepositoryFactory.getVideoRepository(requireActivity().application)
val subtitles = videoRepository.listSubtitles("m" + video.id).mapIndexed { index, it ->
val builder = MediaItem.SubtitleConfiguration.Builder(
Uri.parse("http://192.168.129.9:3000/api/subtitles/${it.id}")
)
.setMimeType(MimeTypes.TEXT_VTT)
.setLanguage(it.lang)
.setId(it.id.toString())

if (index == 0) {
builder.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
}

builder.build()
}

val mediaItem = MediaItem.Builder()
.setUri(video.videoUri)
.setSubtitleConfigurations(subtitles)
.build()

val dataSourceFactory = DefaultDataSource.Factory(requireContext())
val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).
createMediaSource(MediaItem.fromUri((video.videoUri)))
exoplayer = ExoPlayer.Builder(requireContext()).build().apply {
createMediaSource(mediaItem)

val mTrackSelector = DefaultTrackSelector(requireContext())
// disable subtitles at the start
// mTrackSelector.setParameters(
// mTrackSelector
// .buildUponParameters()
// .setRendererDisabled(2, true)
// )
val mSubtitleIndex = -1
// val mSubtitles = requireActivity().findViewById<SubtitleView>(R.id.leanback_subtitles)

exoplayer = ExoPlayer.Builder(requireContext()).setTrackSelector(mTrackSelector).build().apply {
setMediaSource(mediaSource)
prepare()
addListener(PlayerEventListener())
Expand All @@ -143,6 +178,8 @@ class PlaybackFragment : VideoSupportFragment() {
mediaSession.isActive = true
}

// mSubtitles?.setFractionalTextSize(SubtitleView.DEFAULT_TEXT_SIZE_FRACTION * 18 / 100.0f)

viewModel.onStateChange(VideoPlaybackState.Load(video))
}

Expand All @@ -169,6 +206,7 @@ class PlaybackFragment : VideoSupportFragment() {
).apply {
host = VideoSupportFragmentGlueHost(this@PlaybackFragment)
title = video.name
subtitle = video.year.toString()
// Enable seek manually since PlaybackTransportControlGlue.getSeekProvider() is null,
// so that PlayerAdapter.seekTo(long) will be called during user seeking.
// TODO(gargsahil@): Add a PlaybackSeekDataProvider to support video scrubbing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
package com.android.tv.reference.repository

import android.app.Application
import com.android.tv.reference.shared.datamodel.Subtitles
import com.android.tv.reference.shared.datamodel.Video
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path

/**
* VideoRepository implementation to read video data from a file saved on /res/raw
Expand Down Expand Up @@ -60,8 +62,15 @@ class FileVideoRepository(override val application: Application) : VideoReposito
return getAllVideos().filter { it.seriesUri == seriesUri }
}

override suspend fun listSubtitles(videoId: String): List<Subtitles> {
return service.listSubtitles(videoId)
}

private interface LibraryService {
@GET("library")
suspend fun getLibrary(): List<Video>

@GET("watch/{videoId}/subtitles")
suspend fun listSubtitles(@Path("videoId") videoId: String): List<Subtitles>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.android.tv.reference.repository

import android.app.Application
import com.android.tv.reference.shared.datamodel.Subtitles
import com.android.tv.reference.shared.datamodel.Video
import com.android.tv.reference.shared.datamodel.VideoType

Expand Down Expand Up @@ -46,6 +47,8 @@ interface VideoRepository {
*/
fun getAllVideosFromSeries(seriesUri: String): List<Video>

suspend fun listSubtitles(videoId: String): List<Subtitles>

/**
* Get next episode for the same TV Series.
* - if given episode is not last episode of current season, return next episode.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.android.tv.reference.shared.datamodel

import android.os.Parcelable
import com.squareup.moshi.JsonClass
import kotlinx.parcelize.Parcelize

@Parcelize
@JsonClass(generateAdapter = true)
class Subtitles(
val id: Int,
val lang: String
): Parcelable {}
46 changes: 46 additions & 0 deletions app/src/main/res/layout/fragment_playback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">

<com.google.android.exoplayer2.ui.StyledPlayerView android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:show_shuffle_button="true"
app:show_subtitle_button="true"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#88000000"
android:orientation="vertical">

<TextView android:id="@+id/debug_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:textSize="10sp"
tools:ignore="SmallSp"/>

<LinearLayout android:id="@+id/controls_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">

<Button android:id="@+id/select_tracks_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="select track"
android:enabled="false"/>

</LinearLayout>

</LinearLayout>

</FrameLayout>
36 changes: 36 additions & 0 deletions app/src/main/res/layout/lb_playback_fragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copied from android source so that we can add the subtitle view
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/playback_fragment_root"
android:layout_width="match_parent"
android:transitionGroup="false"
android:layout_height="match_parent">

<FrameLayout
android:id="@+id/playback_fragment_background"
android:transitionGroup="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<FrameLayout
android:id="@+id/playback_controls_dock"
android:transitionGroup="true"
android:layout_height="match_parent"
android:layout_width="match_parent"/>

<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">

<com.google.android.exoplayer2.ui.SubtitleView
android:id="@+id/leanback_subtitles"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>

</FrameLayout>