@@ -4,30 +4,29 @@ import android.net.Uri
44import android.util.Log
55import androidx.annotation.OptIn
66import androidx.media3.common.C
7- import androidx.media3.common.C.TrackType
87import androidx.media3.common.Format
98import androidx.media3.common.MimeTypes
10- import androidx.media3.common.StreamKey
119import androidx.media3.common.TrackGroup
12- import androidx.media3.common.util.ParsableByteArray
1310import androidx.media3.common.util.UnstableApi
14- import androidx.media3.datasource.DataSource
15- import androidx.media3.decoder.DecoderInputBuffer
16- import androidx.media3.exoplayer.FormatHolder
1711import androidx.media3.exoplayer.LoadingInfo
1812import androidx.media3.exoplayer.SeekParameters
13+ import androidx.media3.exoplayer.drm.DrmSessionEventListener
14+ import androidx.media3.exoplayer.drm.DrmSessionManager
1915import androidx.media3.exoplayer.source.CompositeSequenceableLoaderFactory
2016import androidx.media3.exoplayer.source.DefaultCompositeSequenceableLoaderFactory
2117import androidx.media3.exoplayer.source.MediaPeriod
22- import androidx.media3.exoplayer.source.MediaSource
23- import androidx.media3.exoplayer.source.SampleQueue
18+ import androidx.media3.exoplayer.source.MediaSourceEventListener
2419import androidx.media3.exoplayer.source.SampleStream
2520import androidx.media3.exoplayer.source.SequenceableLoader
2621import androidx.media3.exoplayer.source.TrackGroupArray
22+ import androidx.media3.exoplayer.source.chunk.ChunkSampleStream
2723import androidx.media3.exoplayer.trackselection.ExoTrackSelection
2824import androidx.media3.exoplayer.upstream.Allocator
25+ import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy
26+ import androidx.media3.exoplayer.upstream.LoaderErrorThrower
2927import com.github.libretube.api.obj.Streams
30- import com.github.libretube.api.poToken.PoTokenGenerator
28+ import com.github.libretube.player.manifest.SABRManifest
29+ import okhttp3.internal.immutableListOf
3130import kotlin.io.encoding.Base64
3231import kotlin.io.encoding.ExperimentalEncodingApi
3332
@@ -38,20 +37,30 @@ class SABRMediaPeriod(
3837 private val allocator : Allocator ,
3938 private val videoId : String ,
4039 private val streams : Streams ,
41- ) : MediaPeriod {
40+ private val manifestLoaderErrorThrower : LoaderErrorThrower ,
41+ private val chunkSourceFactory : SABRChunkSource .Factory ,
42+ private val manifest : SABRManifest ,
43+ private val drmSessionManager : DrmSessionManager ,
44+ private val drmEventDispatcher : DrmSessionEventListener .EventDispatcher ,
45+ private val loadErrorHandlingPolicy : LoadErrorHandlingPolicy ,
46+ private val mediaSourceEventDispatcher : MediaSourceEventListener .EventDispatcher ,
47+ ) : MediaPeriod, SequenceableLoader.Callback<ChunkSampleStream<SABRChunkSource>> {
4248 private val TAG = SABRMediaPeriod ::class .simpleName
4349
50+ private var sampleStreams: Array <ChunkSampleStream <SABRChunkSource >> = emptyArray()
4451 private val sabrStream: SABRStream = SABRStream ()
4552 private val tracks: Array <TrackGroup > = buildTracks()
4653 private val compositeSequenceableLoaderFactory: CompositeSequenceableLoaderFactory =
4754 DefaultCompositeSequenceableLoaderFactory ()
48- var compositeSequenceableLoader = compositeSequenceableLoaderFactory.empty()
55+ private var compositeSequenceableLoader = compositeSequenceableLoaderFactory.empty()
56+ private var callback: MediaPeriod .Callback ? = null
4957
5058 @kotlin.OptIn (ExperimentalEncodingApi ::class )
5159 override fun prepare (
5260 callback : MediaPeriod .Callback ,
5361 positionUs : Long
5462 ) {
63+ this .callback = callback
5564 Log .e(TAG , " prepare: ${streams.videoPlaybackUstreamerConfig} " )
5665 sabrStream.prepare(
5766 videoId,
@@ -85,45 +94,82 @@ class SABRMediaPeriod(
8594 // stupid hack because kotlin doesn't allow assigning null in an out array
8695 val streams = streams as Array <SampleStream ?>
8796
88-
89- // deselect old tracks.
97+ val sampleStreamsList: MutableList <ChunkSampleStream <SABRChunkSource >> = mutableListOf ();
9098 for (i in 0 .. < selections.size) {
91- if (streams[i] != null && (selections[i] == null || ! mayRetainStreamFlags[i])) {
92- streams[i] = null
99+ if (streams[i] != null ) {
100+ val stream = streams[i] as ChunkSampleStream <SABRChunkSource >;
101+ if (selections[i] == null || ! mayRetainStreamFlags[i]) {
102+ stream.release();
103+ streams[i] = null ;
104+ } else {
105+ stream.getChunkSource().updateTrackSelection(checkNotNull(selections[i]));
106+ sampleStreamsList.add(stream);
107+ }
108+ }
109+ if (streams[i] == null && selections[i] != null ) {
110+ val stream = buildSampleStream(selections[i], positionUs);
111+ sampleStreamsList.add(stream);
112+ streams[i] = stream;
113+ streamResetFlags[i] = true ;
93114 }
94115 }
95-
96- // TODO: select new tracks
116+ sampleStreams = sampleStreamsList.toTypedArray()
117+ compositeSequenceableLoader = compositeSequenceableLoaderFactory.create(
118+ sampleStreamsList, sampleStreamsList.map { immutableListOf(it.primaryTrackType) })
97119
98120 return positionUs
99121 }
100122
101123
102- override fun discardBuffer (positionUs : Long , toKeyframe : Boolean ) {}
103-
104- override fun readDiscontinuity (): Long = C .TIME_UNSET
124+ override fun discardBuffer (positionUs : Long , toKeyframe : Boolean ) {
125+ for (sampleStream in sampleStreams) {
126+ sampleStream.discardBuffer(positionUs, toKeyframe)
127+ }
128+ }
105129
106- override fun seekToUs (positionUs : Long ): Long = positionUs
130+ override fun reevaluateBuffer (positionUs : Long ) =
131+ compositeSequenceableLoader.reevaluateBuffer(positionUs)
107132
108- override fun getAdjustedSeekPositionUs (
109- positionUs : Long ,
110- seekParameters : SeekParameters
111- ): Long = positionUs
133+ override fun continueLoading (loadingInfo : LoadingInfo ): Boolean =
134+ compositeSequenceableLoader.continueLoading(loadingInfo)
112135
113- override fun getBufferedPositionUs (): Long = compositeSequenceableLoader.bufferedPositionUs
136+ override fun isLoading (): Boolean = compositeSequenceableLoader.isLoading
114137
115138 override fun getNextLoadPositionUs (): Long = compositeSequenceableLoader.nextLoadPositionUs
116139
117- override fun continueLoading (loadingInfo : LoadingInfo ): Boolean =
118- compositeSequenceableLoader.continueLoading(loadingInfo)
140+ override fun readDiscontinuity (): Long = C .TIME_UNSET
119141
120- override fun isLoading (): Boolean = compositeSequenceableLoader.isLoading
142+ override fun getBufferedPositionUs (): Long = compositeSequenceableLoader.bufferedPositionUs
121143
122- override fun reevaluateBuffer (positionUs : Long ) =
123- compositeSequenceableLoader.reevaluateBuffer(positionUs)
144+ override fun seekToUs (positionUs : Long ): Long {
145+ for (sampleStream in sampleStreams) {
146+ sampleStream.seekToUs(positionUs)
147+ }
148+ return positionUs
149+ }
150+
151+ override fun getAdjustedSeekPositionUs (
152+ positionUs : Long ,
153+ seekParameters : SeekParameters
154+ ): Long {
155+ for (sampleStream in sampleStreams) {
156+ if (sampleStream.primaryTrackType == C .TRACK_TYPE_VIDEO ) {
157+ sampleStream.getAdjustedSeekPositionUs(positionUs, seekParameters)
158+ }
159+ }
160+ return positionUs
161+ }
124162
125163 fun release () {
126164 sabrStream.destroy()
165+ for (sampleStream in sampleStreams) {
166+ sampleStream.release()
167+ }
168+ callback = null ;
169+ }
170+
171+ override fun onContinueLoadingRequested (source : ChunkSampleStream <SABRChunkSource >) {
172+ callback!! .onContinueLoadingRequested(this )
127173 }
128174
129175 private fun buildTracks (): Array <TrackGroup > {
@@ -141,7 +187,6 @@ class SABRMediaPeriod(
141187 .build()
142188 }.toTypedArray()
143189 tracks.add(TrackGroup (" audio" , * audioFormats))
144- sampleStreams.put(" audio" , SABRSampleStream (allocator, sabrStream, C .TRACK_TYPE_AUDIO , audioFormats.first()))
145190
146191 val videoFormats = streams.videoStreams.take(1 ).map { stream ->
147192 Format .Builder ()
@@ -155,8 +200,37 @@ class SABRMediaPeriod(
155200 .setHeight(stream.height ? : - 1 ).build()
156201 }.toTypedArray()
157202 tracks.add(TrackGroup (" video" , * videoFormats))
158- sampleStreams.put(" video" , SABRSampleStream (allocator, sabrStream, C .TRACK_TYPE_VIDEO , videoFormats.first()))
159203
160204 return tracks.toTypedArray()
161205 }
206+
207+ private fun buildSampleStream (
208+ selection : ExoTrackSelection ? ,
209+ positionUs : Long
210+ ) : ChunkSampleStream <SABRChunkSource > {
211+ val streamElementIndex = trackGroups.indexOf(selection!! .trackGroup)
212+ val chunkSource =
213+ chunkSourceFactory.createChunkSource(
214+ manifestLoaderErrorThrower,
215+ manifest,
216+ streamElementIndex,
217+ selection,
218+ )!!
219+ return ChunkSampleStream <SABRChunkSource >(
220+ // TODO: use correct tracktype
221+ C .TRACK_TYPE_VIDEO ,
222+ null ,
223+ null ,
224+ chunkSource,
225+ this ,
226+ allocator,
227+ positionUs,
228+ drmSessionManager,
229+ drmEventDispatcher,
230+ loadErrorHandlingPolicy,
231+ mediaSourceEventDispatcher,
232+ false ,
233+ null
234+ )
235+ }
162236}
0 commit comments