Simplify and accurately compute chunk duration
Refactored `CmcdLog` to `CmcdHeadersFactory` for improved representation of its purpose and updated implementations. #minor-change PiperOrigin-RevId: 552831995
This commit is contained in:
parent
6633b05c0a
commit
11648e6c8e
@ -35,15 +35,15 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Represents the data for CMCD (Common Media Client Data) in adaptive streaming formats DASH, HLS,
|
||||
* and SmoothStreaming.
|
||||
* This class serves as a factory for generating Common Media Client Data (CMCD) HTTP request
|
||||
* headers in adaptive streaming formats, DASH, HLS, and SmoothStreaming.
|
||||
*
|
||||
* <p>It holds various attributes related to the playback of media content according to the
|
||||
* specifications outlined in the CMCD standard document <a
|
||||
* <p>It encapsulates the necessary attributes and information relevant to media content playback,
|
||||
* following the guidelines specified in the CMCD standard document <a
|
||||
* href="https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf">CTA-5004</a>.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class CmcdLog {
|
||||
public final class CmcdHeadersFactory {
|
||||
|
||||
/** Indicates the streaming format used for media content. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@ -74,34 +74,62 @@ public final class CmcdLog {
|
||||
/** Represents the Live Streaming stream type. */
|
||||
public static final String STREAM_TYPE_LIVE = "l";
|
||||
|
||||
private final CmcdConfiguration cmcdConfiguration;
|
||||
private final ExoTrackSelection trackSelection;
|
||||
private final long bufferedDurationUs;
|
||||
private final @StreamingFormat String streamingFormat;
|
||||
private final boolean isLive;
|
||||
private long chunkDurationUs;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param cmcdConfiguration The {@link CmcdConfiguration} for this chunk source.
|
||||
* @param trackSelection The {@linkplain ExoTrackSelection track selection}.
|
||||
* @param bufferedDurationUs The duration of media currently buffered from the current playback
|
||||
* position, in microseconds.
|
||||
* @param chunkDurationUs The duration of current media chunk being requested, in microseconds. If
|
||||
* the duration is not known, it can be set to {@link C#TIME_UNSET}.
|
||||
* @param streamingFormat The streaming format of the media content. Must be one of the allowed
|
||||
* streaming formats specified by the {@link StreamingFormat} annotation.
|
||||
* @param isLive {@code true} if the media content is being streamed live, {@code false}
|
||||
* otherwise.
|
||||
* @throws IllegalArgumentException If {@code bufferedDurationUs} is negative.
|
||||
*/
|
||||
public static CmcdLog createInstance(
|
||||
public CmcdHeadersFactory(
|
||||
CmcdConfiguration cmcdConfiguration,
|
||||
ExoTrackSelection trackSelection,
|
||||
long bufferedDurationUs,
|
||||
long chunkDurationUs,
|
||||
@StreamingFormat String streamingFormat,
|
||||
boolean isLive) {
|
||||
checkArgument(bufferedDurationUs >= 0);
|
||||
this.cmcdConfiguration = cmcdConfiguration;
|
||||
this.trackSelection = trackSelection;
|
||||
this.bufferedDurationUs = bufferedDurationUs;
|
||||
this.streamingFormat = streamingFormat;
|
||||
this.isLive = isLive;
|
||||
this.chunkDurationUs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration of current media chunk being requested, in microseconds. The default value is
|
||||
* {@link C#TIME_UNSET}.
|
||||
*
|
||||
* @throws IllegalArgumentException If {@code chunkDurationUs} is negative.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public CmcdHeadersFactory setChunkDurationUs(long chunkDurationUs) {
|
||||
checkArgument(chunkDurationUs >= 0);
|
||||
this.chunkDurationUs = chunkDurationUs;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Creates and returns a new {@link ImmutableMap} containing the CMCD HTTP request headers. */
|
||||
public ImmutableMap<@CmcdConfiguration.HeaderKey String, String> createHttpRequestHeaders() {
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> customData =
|
||||
cmcdConfiguration.requestConfig.getCustomData();
|
||||
int bitrateKbps = trackSelection.getSelectedFormat().bitrate / 1000;
|
||||
int bitrateKbps = Util.ceilDivide(trackSelection.getSelectedFormat().bitrate, 1000);
|
||||
|
||||
CmcdLog.CmcdObject.Builder cmcdObject =
|
||||
new CmcdLog.CmcdObject.Builder()
|
||||
.setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_OBJECT));
|
||||
CmcdObject.Builder cmcdObject =
|
||||
new CmcdObject.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_OBJECT));
|
||||
if (cmcdConfiguration.isBitrateLoggingAllowed()) {
|
||||
cmcdObject.setBitrateKbps(bitrateKbps);
|
||||
}
|
||||
@ -111,26 +139,25 @@ public final class CmcdLog {
|
||||
for (int i = 0; i < trackGroup.length; i++) {
|
||||
topBitrate = max(topBitrate, trackGroup.getFormat(i).bitrate);
|
||||
}
|
||||
cmcdObject.setTopBitrateKbps(topBitrate / 1000);
|
||||
cmcdObject.setTopBitrateKbps(Util.ceilDivide(topBitrate, 1000));
|
||||
}
|
||||
if (cmcdConfiguration.isObjectDurationLoggingAllowed() && chunkDurationUs != C.TIME_UNSET) {
|
||||
cmcdObject.setObjectDurationMs(chunkDurationUs / 1000);
|
||||
}
|
||||
|
||||
CmcdLog.CmcdRequest.Builder cmcdRequest =
|
||||
new CmcdLog.CmcdRequest.Builder()
|
||||
.setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_REQUEST));
|
||||
CmcdRequest.Builder cmcdRequest =
|
||||
new CmcdRequest.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_REQUEST));
|
||||
if (cmcdConfiguration.isBufferLengthLoggingAllowed()) {
|
||||
cmcdRequest.setBufferLengthMs(bufferedDurationUs / 1000);
|
||||
}
|
||||
if (cmcdConfiguration.isMeasuredThroughputLoggingAllowed()
|
||||
&& trackSelection.getLatestBitrateEstimate() != Long.MIN_VALUE) {
|
||||
cmcdRequest.setMeasuredThroughputInKbps(trackSelection.getLatestBitrateEstimate() / 1000);
|
||||
cmcdRequest.setMeasuredThroughputInKbps(
|
||||
Util.ceilDivide(trackSelection.getLatestBitrateEstimate(), 1000));
|
||||
}
|
||||
|
||||
CmcdLog.CmcdSession.Builder cmcdSession =
|
||||
new CmcdLog.CmcdSession.Builder()
|
||||
.setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_SESSION));
|
||||
CmcdSession.Builder cmcdSession =
|
||||
new CmcdSession.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_SESSION));
|
||||
if (cmcdConfiguration.isContentIdLoggingAllowed()) {
|
||||
cmcdSession.setContentId(cmcdConfiguration.contentId);
|
||||
}
|
||||
@ -144,40 +171,18 @@ public final class CmcdLog {
|
||||
cmcdSession.setStreamType(isLive ? STREAM_TYPE_LIVE : STREAM_TYPE_VOD);
|
||||
}
|
||||
|
||||
CmcdLog.CmcdStatus.Builder cmcdStatus =
|
||||
new CmcdLog.CmcdStatus.Builder()
|
||||
.setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_STATUS));
|
||||
CmcdStatus.Builder cmcdStatus =
|
||||
new CmcdStatus.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_STATUS));
|
||||
if (cmcdConfiguration.isMaximumRequestThroughputLoggingAllowed()) {
|
||||
cmcdStatus.setMaximumRequestedThroughputKbps(
|
||||
cmcdConfiguration.requestConfig.getRequestedMaximumThroughputKbps(bitrateKbps));
|
||||
}
|
||||
|
||||
return new CmcdLog(
|
||||
cmcdObject.build(), cmcdRequest.build(), cmcdSession.build(), cmcdStatus.build());
|
||||
}
|
||||
|
||||
private final CmcdObject cmcdObject;
|
||||
private final CmcdRequest cmcdRequest;
|
||||
private final CmcdSession cmcdSession;
|
||||
private final CmcdStatus cmcdStatus;
|
||||
|
||||
private CmcdLog(
|
||||
CmcdObject cmcdObject,
|
||||
CmcdRequest cmcdRequest,
|
||||
CmcdSession cmcdSession,
|
||||
CmcdStatus cmcdStatus) {
|
||||
this.cmcdObject = cmcdObject;
|
||||
this.cmcdRequest = cmcdRequest;
|
||||
this.cmcdSession = cmcdSession;
|
||||
this.cmcdStatus = cmcdStatus;
|
||||
}
|
||||
|
||||
public ImmutableMap<@CmcdConfiguration.HeaderKey String, String> getHttpRequestHeaders() {
|
||||
ImmutableMap.Builder<String, String> httpRequestHeaders = ImmutableMap.builder();
|
||||
this.cmcdObject.populateHttpRequestHeaders(httpRequestHeaders);
|
||||
this.cmcdRequest.populateHttpRequestHeaders(httpRequestHeaders);
|
||||
this.cmcdSession.populateHttpRequestHeaders(httpRequestHeaders);
|
||||
this.cmcdStatus.populateHttpRequestHeaders(httpRequestHeaders);
|
||||
cmcdObject.build().populateHttpRequestHeaders(httpRequestHeaders);
|
||||
cmcdRequest.build().populateHttpRequestHeaders(httpRequestHeaders);
|
||||
cmcdSession.build().populateHttpRequestHeaders(httpRequestHeaders);
|
||||
cmcdStatus.build().populateHttpRequestHeaders(httpRequestHeaders);
|
||||
return httpRequestHeaders.buildOrThrow();
|
||||
}
|
||||
|
||||
@ -492,7 +497,7 @@ public final class CmcdLog {
|
||||
|
||||
/** Sets the {@link CmcdSession#customData}. The default value is {@code null}. */
|
||||
@CanIgnoreReturnValue
|
||||
public CmcdSession.Builder setCustomData(@Nullable String customData) {
|
||||
public Builder setCustomData(@Nullable String customData) {
|
||||
this.customData = customData;
|
||||
return this;
|
||||
}
|
||||
@ -635,7 +640,7 @@ public final class CmcdLog {
|
||||
|
||||
/** Sets the {@link CmcdStatus#customData}. The default value is {@code null}. */
|
||||
@CanIgnoreReturnValue
|
||||
public CmcdStatus.Builder setCustomData(@Nullable String customData) {
|
||||
public Builder setCustomData(@Nullable String customData) {
|
||||
this.customData = customData;
|
||||
return this;
|
||||
}
|
@ -28,9 +28,9 @@ import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Tests for {@link CmcdLog}. */
|
||||
/** Tests for {@link CmcdHeadersFactory}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CmcdLogTest {
|
||||
public class CmcdHeadersFactoryTest {
|
||||
|
||||
@Test
|
||||
public void createInstance_populatesCmcdHeaders() {
|
||||
@ -62,17 +62,16 @@ public class CmcdLogTest {
|
||||
when(trackSelection.getTrackGroup())
|
||||
.thenReturn(new TrackGroup(format, new Format.Builder().setPeakBitrate(1_000_000).build()));
|
||||
when(trackSelection.getLatestBitrateEstimate()).thenReturn(500_000L);
|
||||
CmcdLog cmcdLog =
|
||||
CmcdLog.createInstance(
|
||||
cmcdConfiguration,
|
||||
trackSelection,
|
||||
/* bufferedDurationUs= */ 1_760_000,
|
||||
/* chunkDurationUs= */ 3_000_000,
|
||||
CmcdLog.STREAMING_FORMAT_DASH,
|
||||
true);
|
||||
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> requestHeaders =
|
||||
cmcdLog.getHttpRequestHeaders();
|
||||
new CmcdHeadersFactory(
|
||||
cmcdConfiguration,
|
||||
trackSelection,
|
||||
/* bufferedDurationUs= */ 1_760_000,
|
||||
/* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_DASH,
|
||||
/* isLive= */ true)
|
||||
.setChunkDurationUs(3_000_000)
|
||||
.createHttpRequestHeaders();
|
||||
|
||||
assertThat(requestHeaders)
|
||||
.containsExactly(
|
@ -51,7 +51,7 @@ import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
||||
import androidx.media3.exoplayer.source.chunk.SingleSampleMediaChunk;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.exoplayer.upstream.CmcdLog;
|
||||
import androidx.media3.exoplayer.upstream.CmcdHeadersFactory;
|
||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||
import androidx.media3.exoplayer.upstream.LoaderErrorThrower;
|
||||
import androidx.media3.extractor.ChunkIndex;
|
||||
@ -371,24 +371,17 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
playbackPositionUs, bufferedDurationUs, availableLiveDurationUs, queue, chunkIterators);
|
||||
|
||||
int selectedTrackIndex = trackSelection.getSelectedIndex();
|
||||
long chunkDurationUs = C.TIME_UNSET;
|
||||
if (selectedTrackIndex < chunkIterators.length && chunkIterators[selectedTrackIndex].next()) {
|
||||
chunkDurationUs =
|
||||
chunkIterators[selectedTrackIndex].getChunkEndTimeUs()
|
||||
- chunkIterators[selectedTrackIndex].getChunkStartTimeUs();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
CmcdLog cmcdLog =
|
||||
CmcdHeadersFactory cmcdHeadersFactory =
|
||||
cmcdConfiguration == null
|
||||
? null
|
||||
: CmcdLog.createInstance(
|
||||
: new CmcdHeadersFactory(
|
||||
cmcdConfiguration,
|
||||
trackSelection,
|
||||
bufferedDurationUs,
|
||||
chunkDurationUs,
|
||||
CmcdLog.STREAMING_FORMAT_DASH,
|
||||
manifest.dynamic);
|
||||
|
||||
/* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_DASH,
|
||||
/* isLive= */ manifest.dynamic);
|
||||
RepresentationHolder representationHolder = updateSelectedBaseUrl(selectedTrackIndex);
|
||||
if (representationHolder.chunkExtractor != null) {
|
||||
Representation selectedRepresentation = representationHolder.representation;
|
||||
@ -411,7 +404,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
trackSelection.getSelectionData(),
|
||||
pendingInitializationUri,
|
||||
pendingIndexUri,
|
||||
cmcdLog);
|
||||
cmcdHeadersFactory);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -477,7 +470,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
maxSegmentCount,
|
||||
seekTimeUs,
|
||||
nowPeriodTimeUs,
|
||||
cmcdLog);
|
||||
cmcdHeadersFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -652,7 +645,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
@Nullable Object trackSelectionData,
|
||||
@Nullable RangedUri initializationUri,
|
||||
@Nullable RangedUri indexUri,
|
||||
@Nullable CmcdLog cmcdLog) {
|
||||
@Nullable CmcdHeadersFactory cmcdHeadersFactory) {
|
||||
Representation representation = representationHolder.representation;
|
||||
@Nullable RangedUri requestUri;
|
||||
if (initializationUri != null) {
|
||||
@ -667,7 +660,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
requestUri = indexUri;
|
||||
}
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
|
||||
cmcdHeadersFactory == null
|
||||
? ImmutableMap.of()
|
||||
: cmcdHeadersFactory.createHttpRequestHeaders();
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representation,
|
||||
@ -695,12 +690,10 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
int maxSegmentCount,
|
||||
long seekTimeUs,
|
||||
long nowPeriodTimeUs,
|
||||
@Nullable CmcdLog cmcdLog) {
|
||||
@Nullable CmcdHeadersFactory cmcdHeadersFactory) {
|
||||
Representation representation = representationHolder.representation;
|
||||
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
|
||||
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
|
||||
if (representationHolder.chunkExtractor == null) {
|
||||
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
|
||||
int flags =
|
||||
@ -708,6 +701,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
firstSegmentNum, nowPeriodTimeUs)
|
||||
? 0
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||
cmcdHeadersFactory == null
|
||||
? ImmutableMap.of()
|
||||
: cmcdHeadersFactory
|
||||
.setChunkDurationUs(endTimeUs - startTimeUs)
|
||||
.createHttpRequestHeaders();
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representation,
|
||||
@ -751,6 +750,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
representationHolder.isSegmentAvailableAtFullNetworkSpeed(segmentNum, nowPeriodTimeUs)
|
||||
? 0
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||
cmcdHeadersFactory == null
|
||||
? ImmutableMap.of()
|
||||
: cmcdHeadersFactory
|
||||
.setChunkDurationUs(endTimeUs - startTimeUs)
|
||||
.createHttpRequestHeaders();
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representation,
|
||||
|
@ -48,7 +48,7 @@ import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
||||
import androidx.media3.exoplayer.trackselection.BaseTrackSelection;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.exoplayer.upstream.CmcdLog;
|
||||
import androidx.media3.exoplayer.upstream.CmcdHeadersFactory;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
@ -481,36 +481,30 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
seenExpectedPlaylistError = false;
|
||||
expectedPlaylistUrl = null;
|
||||
|
||||
long chunkDurationUs = C.TIME_UNSET;
|
||||
if (selectedTrackIndex < mediaChunkIterators.length
|
||||
&& mediaChunkIterators[selectedTrackIndex].next()) {
|
||||
chunkDurationUs =
|
||||
mediaChunkIterators[selectedTrackIndex].getChunkEndTimeUs()
|
||||
- mediaChunkIterators[selectedTrackIndex].getChunkStartTimeUs();
|
||||
}
|
||||
@Nullable
|
||||
CmcdLog cmcdLog =
|
||||
CmcdHeadersFactory cmcdHeadersFactory =
|
||||
cmcdConfiguration == null
|
||||
? null
|
||||
: CmcdLog.createInstance(
|
||||
: new CmcdHeadersFactory(
|
||||
cmcdConfiguration,
|
||||
trackSelection,
|
||||
bufferedDurationUs,
|
||||
chunkDurationUs,
|
||||
CmcdLog.STREAMING_FORMAT_HLS,
|
||||
!playlist.hasEndTag);
|
||||
/* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_HLS,
|
||||
/* isLive= */ !playlist.hasEndTag);
|
||||
|
||||
// Check if the media segment or its initialization segment are fully encrypted.
|
||||
@Nullable
|
||||
Uri initSegmentKeyUri =
|
||||
getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase.initializationSegment);
|
||||
out.chunk = maybeCreateEncryptionChunkFor(initSegmentKeyUri, selectedTrackIndex, cmcdLog);
|
||||
out.chunk =
|
||||
maybeCreateEncryptionChunkFor(initSegmentKeyUri, selectedTrackIndex, cmcdHeadersFactory);
|
||||
if (out.chunk != null) {
|
||||
return;
|
||||
}
|
||||
@Nullable
|
||||
Uri mediaSegmentKeyUri = getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase);
|
||||
out.chunk = maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex, cmcdLog);
|
||||
out.chunk =
|
||||
maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex, cmcdHeadersFactory);
|
||||
if (out.chunk != null) {
|
||||
return;
|
||||
}
|
||||
@ -546,7 +540,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
/* initSegmentKey= */ keyCache.get(initSegmentKeyUri),
|
||||
shouldSpliceIn,
|
||||
playerId,
|
||||
cmcdLog);
|
||||
cmcdHeadersFactory);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -854,7 +848,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
@Nullable
|
||||
private Chunk maybeCreateEncryptionChunkFor(
|
||||
@Nullable Uri keyUri, int selectedTrackIndex, @Nullable CmcdLog cmcdLog) {
|
||||
@Nullable Uri keyUri,
|
||||
int selectedTrackIndex,
|
||||
@Nullable CmcdHeadersFactory cmcdHeadersFactory) {
|
||||
if (keyUri == null) {
|
||||
return null;
|
||||
}
|
||||
@ -868,7 +864,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
return null;
|
||||
}
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
|
||||
cmcdHeadersFactory == null
|
||||
? ImmutableMap.of()
|
||||
: cmcdHeadersFactory.createHttpRequestHeaders();
|
||||
DataSpec dataSpec =
|
||||
new DataSpec.Builder()
|
||||
.setUri(keyUri)
|
||||
|
@ -34,7 +34,7 @@ import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist;
|
||||
import androidx.media3.exoplayer.source.chunk.MediaChunk;
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.exoplayer.upstream.CmcdLog;
|
||||
import androidx.media3.exoplayer.upstream.CmcdHeadersFactory;
|
||||
import androidx.media3.extractor.DefaultExtractorInput;
|
||||
import androidx.media3.extractor.ExtractorInput;
|
||||
import androidx.media3.extractor.metadata.id3.Id3Decoder;
|
||||
@ -82,6 +82,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
* @param initSegmentKey The initialization segment decryption key, if fully encrypted. Null
|
||||
* otherwise.
|
||||
* @param shouldSpliceIn Whether samples for this chunk should be spliced into existing samples.
|
||||
* @param cmcdHeadersFactory The {@link CmcdHeadersFactory} for generating CMCD request headers.
|
||||
*/
|
||||
public static HlsMediaChunk createInstance(
|
||||
HlsExtractorFactory extractorFactory,
|
||||
@ -102,11 +103,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
@Nullable byte[] initSegmentKey,
|
||||
boolean shouldSpliceIn,
|
||||
PlayerId playerId,
|
||||
@Nullable CmcdLog cmcdLog) {
|
||||
@Nullable CmcdHeadersFactory cmcdHeadersFactory) {
|
||||
// Media segment.
|
||||
HlsMediaPlaylist.SegmentBase mediaSegment = segmentBaseHolder.segmentBase;
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
|
||||
cmcdHeadersFactory == null
|
||||
? ImmutableMap.of()
|
||||
: cmcdHeadersFactory
|
||||
.setChunkDurationUs(mediaSegment.durationUs)
|
||||
.createHttpRequestHeaders();
|
||||
DataSpec dataSpec =
|
||||
new DataSpec.Builder()
|
||||
.setUri(UriUtil.resolveToUri(mediaPlaylist.baseUri, mediaSegment.url))
|
||||
|
@ -210,7 +210,7 @@ public class HlsChunkSourceTest {
|
||||
assertThat(output.chunk.dataSpec.httpRequestHeaders)
|
||||
.containsExactly(
|
||||
"CMCD-Object",
|
||||
"br=800,tb=800",
|
||||
"br=800,tb=800,d=4000",
|
||||
"CMCD-Request",
|
||||
"bl=0",
|
||||
"CMCD-Session",
|
||||
@ -256,7 +256,7 @@ public class HlsChunkSourceTest {
|
||||
assertThat(output.chunk.dataSpec.httpRequestHeaders)
|
||||
.containsExactly(
|
||||
"CMCD-Object",
|
||||
"br=800,tb=800",
|
||||
"br=800,tb=800,d=4000",
|
||||
"CMCD-Request",
|
||||
"bl=0",
|
||||
"CMCD-Session",
|
||||
@ -303,7 +303,7 @@ public class HlsChunkSourceTest {
|
||||
assertThat(output.chunk.dataSpec.httpRequestHeaders)
|
||||
.containsExactly(
|
||||
"CMCD-Object",
|
||||
"br=800,tb=800,key1=value1",
|
||||
"br=800,tb=800,d=4000,key1=value1",
|
||||
"CMCD-Request",
|
||||
"bl=0,key2=\"stringValue\"",
|
||||
"CMCD-Session",
|
||||
|
@ -40,7 +40,7 @@ import androidx.media3.exoplayer.source.chunk.MediaChunk;
|
||||
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.exoplayer.upstream.CmcdLog;
|
||||
import androidx.media3.exoplayer.upstream.CmcdHeadersFactory;
|
||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy.FallbackSelection;
|
||||
import androidx.media3.exoplayer.upstream.LoaderErrorThrower;
|
||||
@ -280,23 +280,17 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
||||
int manifestTrackIndex = trackSelection.getIndexInTrackGroup(trackSelectionIndex);
|
||||
Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex);
|
||||
|
||||
long chunkDurationUs = C.TIME_UNSET;
|
||||
if (trackSelectionIndex < chunkIterators.length && chunkIterators[trackSelectionIndex].next()) {
|
||||
chunkDurationUs =
|
||||
chunkIterators[trackSelectionIndex].getChunkEndTimeUs()
|
||||
- chunkIterators[trackSelectionIndex].getChunkStartTimeUs();
|
||||
}
|
||||
@Nullable
|
||||
CmcdLog cmcdLog =
|
||||
CmcdHeadersFactory cmcdHeadersFactory =
|
||||
cmcdConfiguration == null
|
||||
? null
|
||||
: CmcdLog.createInstance(
|
||||
cmcdConfiguration,
|
||||
trackSelection,
|
||||
bufferedDurationUs,
|
||||
chunkDurationUs,
|
||||
CmcdLog.STREAMING_FORMAT_SS,
|
||||
manifest.isLive);
|
||||
: new CmcdHeadersFactory(
|
||||
cmcdConfiguration,
|
||||
trackSelection,
|
||||
bufferedDurationUs,
|
||||
/* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_SS,
|
||||
/* isLive= */ manifest.isLive)
|
||||
.setChunkDurationUs(chunkEndTimeUs - chunkStartTimeUs);
|
||||
|
||||
out.chunk =
|
||||
newMediaChunk(
|
||||
@ -310,7 +304,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
||||
trackSelection.getSelectionReason(),
|
||||
trackSelection.getSelectionData(),
|
||||
chunkExtractor,
|
||||
cmcdLog);
|
||||
cmcdHeadersFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -355,9 +349,11 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
||||
@C.SelectionReason int trackSelectionReason,
|
||||
@Nullable Object trackSelectionData,
|
||||
ChunkExtractor chunkExtractor,
|
||||
@Nullable CmcdLog cmcdLog) {
|
||||
@Nullable CmcdHeadersFactory cmcdHeadersFactory) {
|
||||
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
|
||||
cmcdHeadersFactory == null
|
||||
? ImmutableMap.of()
|
||||
: cmcdHeadersFactory.createHttpRequestHeaders();
|
||||
DataSpec dataSpec =
|
||||
new DataSpec.Builder().setUri(uri).setHttpRequestHeaders(httpRequestHeaders).build();
|
||||
// In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk.
|
||||
|
@ -64,7 +64,7 @@ public class DefaultSsChunkSourceTest {
|
||||
assertThat(output.chunk.dataSpec.httpRequestHeaders)
|
||||
.containsExactly(
|
||||
"CMCD-Object",
|
||||
"br=307,tb=1536,d=1968",
|
||||
"br=308,tb=1536,d=1968",
|
||||
"CMCD-Request",
|
||||
"bl=0,mtp=1000",
|
||||
"CMCD-Session",
|
||||
@ -109,7 +109,7 @@ public class DefaultSsChunkSourceTest {
|
||||
assertThat(output.chunk.dataSpec.httpRequestHeaders)
|
||||
.containsExactly(
|
||||
"CMCD-Object",
|
||||
"br=307,tb=1536,d=1968",
|
||||
"br=308,tb=1536,d=1968",
|
||||
"CMCD-Request",
|
||||
"bl=0,mtp=1000",
|
||||
"CMCD-Session",
|
||||
@ -155,7 +155,7 @@ public class DefaultSsChunkSourceTest {
|
||||
assertThat(output.chunk.dataSpec.httpRequestHeaders)
|
||||
.containsExactly(
|
||||
"CMCD-Object",
|
||||
"br=307,tb=1536,d=1968,key1=value1",
|
||||
"br=308,tb=1536,d=1968,key1=value1",
|
||||
"CMCD-Request",
|
||||
"bl=0,mtp=1000,key2=\"stringValue\"",
|
||||
"CMCD-Session",
|
||||
|
Loading…
x
Reference in New Issue
Block a user