Skip to content

Commit a413d1d

Browse files
committed
feat(sabr): create chunked sample streams
1 parent c573bd3 commit a413d1d

File tree

1 file changed

+107
-33
lines changed

1 file changed

+107
-33
lines changed

app/src/main/java/com/github/libretube/player/SABRMediaPeriod.kt

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,29 @@ import android.net.Uri
44
import android.util.Log
55
import androidx.annotation.OptIn
66
import androidx.media3.common.C
7-
import androidx.media3.common.C.TrackType
87
import androidx.media3.common.Format
98
import androidx.media3.common.MimeTypes
10-
import androidx.media3.common.StreamKey
119
import androidx.media3.common.TrackGroup
12-
import androidx.media3.common.util.ParsableByteArray
1310
import androidx.media3.common.util.UnstableApi
14-
import androidx.media3.datasource.DataSource
15-
import androidx.media3.decoder.DecoderInputBuffer
16-
import androidx.media3.exoplayer.FormatHolder
1711
import androidx.media3.exoplayer.LoadingInfo
1812
import androidx.media3.exoplayer.SeekParameters
13+
import androidx.media3.exoplayer.drm.DrmSessionEventListener
14+
import androidx.media3.exoplayer.drm.DrmSessionManager
1915
import androidx.media3.exoplayer.source.CompositeSequenceableLoaderFactory
2016
import androidx.media3.exoplayer.source.DefaultCompositeSequenceableLoaderFactory
2117
import 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
2419
import androidx.media3.exoplayer.source.SampleStream
2520
import androidx.media3.exoplayer.source.SequenceableLoader
2621
import androidx.media3.exoplayer.source.TrackGroupArray
22+
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream
2723
import androidx.media3.exoplayer.trackselection.ExoTrackSelection
2824
import androidx.media3.exoplayer.upstream.Allocator
25+
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy
26+
import androidx.media3.exoplayer.upstream.LoaderErrorThrower
2927
import 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
3130
import kotlin.io.encoding.Base64
3231
import 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

Comments
 (0)