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:
rohks 2023-08-01 16:48:19 +00:00 committed by Tianyi Feng
parent 6633b05c0a
commit 11648e6c8e
8 changed files with 136 additions and 128 deletions

View File

@ -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;
}

View File

@ -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(

View File

@ -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,

View File

@ -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)

View File

@ -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))

View File

@ -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",

View File

@ -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.

View File

@ -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",