Add field object type (ot)

Added this CMCD-Object field to Common Media Client Data (CMCD) logging.

PiperOrigin-RevId: 554843305
(cherry picked from commit 3ec462d1cf8c26c717236a4bb4a7bf9ab1fdc7cd)
This commit is contained in:
rohks 2023-08-08 16:09:07 +00:00 committed by Tianyi Feng
parent 7e58fde18e
commit 57c73d51da
10 changed files with 190 additions and 41 deletions

View File

@ -14,7 +14,8 @@
Client Data (CMCD) logging. Client Data (CMCD) logging.
* Add additional fields to Common Media Client Data (CMCD) logging: * Add additional fields to Common Media Client Data (CMCD) logging:
streaming format (sf), stream type (st), version (v), top birate (tb), streaming format (sf), stream type (st), version (v), top birate (tb),
object duration (d) and measured throughput (mtp). object duration (d), measured throughput (mtp) and object type(ot)
([#8699](https://github.com/google/ExoPlayer/issues/8699)).
* Audio: * Audio:
* Fix a bug where `Player.getState()` never transitioned to `STATE_ENDED` * Fix a bug where `Player.getState()` never transitioned to `STATE_ENDED`
when playing very short files when playing very short files

View File

@ -66,7 +66,8 @@ public final class CmcdConfiguration {
KEY_VERSION, KEY_VERSION,
KEY_TOP_BITRATE, KEY_TOP_BITRATE,
KEY_OBJECT_DURATION, KEY_OBJECT_DURATION,
KEY_MEASURED_THROUGHPUT KEY_MEASURED_THROUGHPUT,
KEY_OBJECT_TYPE
}) })
@Documented @Documented
@Target(TYPE_USE) @Target(TYPE_USE)
@ -90,6 +91,7 @@ public final class CmcdConfiguration {
public static final String KEY_TOP_BITRATE = "tb"; public static final String KEY_TOP_BITRATE = "tb";
public static final String KEY_OBJECT_DURATION = "d"; public static final String KEY_OBJECT_DURATION = "d";
public static final String KEY_MEASURED_THROUGHPUT = "mtp"; public static final String KEY_MEASURED_THROUGHPUT = "mtp";
public static final String KEY_OBJECT_TYPE = "ot";
/** /**
* Factory for {@link CmcdConfiguration} instances. * Factory for {@link CmcdConfiguration} instances.
@ -289,4 +291,12 @@ public final class CmcdConfiguration {
public boolean isMeasuredThroughputLoggingAllowed() { public boolean isMeasuredThroughputLoggingAllowed() {
return requestConfig.isKeyAllowed(KEY_MEASURED_THROUGHPUT); return requestConfig.isKeyAllowed(KEY_MEASURED_THROUGHPUT);
} }
/**
* Returns whether logging object type is allowed based on the {@linkplain RequestConfig request
* configuration}.
*/
public boolean isObjectTypeLoggingAllowed() {
return requestConfig.isKeyAllowed(KEY_OBJECT_TYPE);
}
} }

View File

@ -23,6 +23,8 @@ import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringDef; import androidx.annotation.StringDef;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.C.TrackType;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroup;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
@ -45,6 +47,33 @@ import java.lang.annotation.Target;
@UnstableApi @UnstableApi
public final class CmcdHeadersFactory { public final class CmcdHeadersFactory {
/**
* Retrieves the object type value from the given {@link ExoTrackSelection}.
*
* @param trackSelection The {@link ExoTrackSelection} from which to retrieve the object type.
* @return The object type value as a String if {@link TrackType} can be mapped to one of the
* object types specified by {@link ObjectType} annotation, or {@code null}.
* @throws IllegalArgumentException if the provided {@link ExoTrackSelection} is {@code null}.
*/
@Nullable
public static @ObjectType String getObjectType(ExoTrackSelection trackSelection) {
checkArgument(trackSelection != null);
@C.TrackType
int trackType = MimeTypes.getTrackType(trackSelection.getSelectedFormat().sampleMimeType);
if (trackType == C.TRACK_TYPE_UNKNOWN) {
trackType = MimeTypes.getTrackType(trackSelection.getSelectedFormat().containerMimeType);
}
if (trackType == C.TRACK_TYPE_AUDIO) {
return OBJECT_TYPE_AUDIO_ONLY;
} else if (trackType == C.TRACK_TYPE_VIDEO) {
return OBJECT_TYPE_VIDEO_ONLY;
} else {
// Track type cannot be mapped to a known object type.
return null;
}
}
/** Indicates the streaming format used for media content. */ /** Indicates the streaming format used for media content. */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@StringDef({STREAMING_FORMAT_DASH, STREAMING_FORMAT_HLS, STREAMING_FORMAT_SS}) @StringDef({STREAMING_FORMAT_DASH, STREAMING_FORMAT_HLS, STREAMING_FORMAT_SS})
@ -59,6 +88,18 @@ public final class CmcdHeadersFactory {
@Target(TYPE_USE) @Target(TYPE_USE)
public @interface StreamType {} public @interface StreamType {}
/** Indicates the media type of current object being requested. */
@Retention(RetentionPolicy.SOURCE)
@StringDef({
OBJECT_TYPE_INIT_SEGMENT,
OBJECT_TYPE_AUDIO_ONLY,
OBJECT_TYPE_VIDEO_ONLY,
OBJECT_TYPE_MUXED_AUDIO_AND_VIDEO
})
@Documented
@Target(TYPE_USE)
public @interface ObjectType {}
/** Represents the Dynamic Adaptive Streaming over HTTP (DASH) format. */ /** Represents the Dynamic Adaptive Streaming over HTTP (DASH) format. */
public static final String STREAMING_FORMAT_DASH = "d"; public static final String STREAMING_FORMAT_DASH = "d";
@ -74,12 +115,25 @@ public final class CmcdHeadersFactory {
/** Represents the Live Streaming stream type. */ /** Represents the Live Streaming stream type. */
public static final String STREAM_TYPE_LIVE = "l"; public static final String STREAM_TYPE_LIVE = "l";
/** Represents the object type for an initialization segment in a media container. */
public static final String OBJECT_TYPE_INIT_SEGMENT = "i";
/** Represents the object type for audio-only content in a media container. */
public static final String OBJECT_TYPE_AUDIO_ONLY = "a";
/** Represents the object type for video-only content in a media container. */
public static final String OBJECT_TYPE_VIDEO_ONLY = "v";
/** Represents the object type for muxed audio and video content in a media container. */
public static final String OBJECT_TYPE_MUXED_AUDIO_AND_VIDEO = "av";
private final CmcdConfiguration cmcdConfiguration; private final CmcdConfiguration cmcdConfiguration;
private final ExoTrackSelection trackSelection; private final ExoTrackSelection trackSelection;
private final long bufferedDurationUs; private final long bufferedDurationUs;
private final @StreamingFormat String streamingFormat; private final @StreamingFormat String streamingFormat;
private final boolean isLive; private final boolean isLive;
private long chunkDurationUs; private long chunkDurationUs;
private @Nullable @ObjectType String objectType;
/** /**
* Creates an instance. * Creates an instance.
@ -122,6 +176,18 @@ public final class CmcdHeadersFactory {
return this; return this;
} }
/**
* Sets the object type of the current object being requested. Must be one of the allowed object
* types specified by the {@link ObjectType} annotation.
*
* <p>Default is {@code null}.
*/
@CanIgnoreReturnValue
public CmcdHeadersFactory setObjectType(@Nullable @ObjectType String objectType) {
this.objectType = objectType;
return this;
}
/** Creates and returns a new {@link ImmutableMap} containing the CMCD HTTP request headers. */ /** Creates and returns a new {@link ImmutableMap} containing the CMCD HTTP request headers. */
public ImmutableMap<@CmcdConfiguration.HeaderKey String, String> createHttpRequestHeaders() { public ImmutableMap<@CmcdConfiguration.HeaderKey String, String> createHttpRequestHeaders() {
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> customData = ImmutableMap<@CmcdConfiguration.HeaderKey String, String> customData =
@ -130,6 +196,7 @@ public final class CmcdHeadersFactory {
CmcdObject.Builder cmcdObject = CmcdObject.Builder cmcdObject =
new CmcdObject.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_OBJECT)); new CmcdObject.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_OBJECT));
if (!getIsInitSegment()) {
if (cmcdConfiguration.isBitrateLoggingAllowed()) { if (cmcdConfiguration.isBitrateLoggingAllowed()) {
cmcdObject.setBitrateKbps(bitrateKbps); cmcdObject.setBitrateKbps(bitrateKbps);
} }
@ -144,10 +211,15 @@ public final class CmcdHeadersFactory {
if (cmcdConfiguration.isObjectDurationLoggingAllowed() && chunkDurationUs != C.TIME_UNSET) { if (cmcdConfiguration.isObjectDurationLoggingAllowed() && chunkDurationUs != C.TIME_UNSET) {
cmcdObject.setObjectDurationMs(chunkDurationUs / 1000); cmcdObject.setObjectDurationMs(chunkDurationUs / 1000);
} }
}
if (cmcdConfiguration.isObjectTypeLoggingAllowed()) {
cmcdObject.setObjectType(objectType);
}
CmcdRequest.Builder cmcdRequest = CmcdRequest.Builder cmcdRequest =
new CmcdRequest.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_REQUEST)); new CmcdRequest.Builder().setCustomData(customData.get(CmcdConfiguration.KEY_CMCD_REQUEST));
if (cmcdConfiguration.isBufferLengthLoggingAllowed()) { if (!getIsInitSegment() && cmcdConfiguration.isBufferLengthLoggingAllowed()) {
cmcdRequest.setBufferLengthMs(bufferedDurationUs / 1000); cmcdRequest.setBufferLengthMs(bufferedDurationUs / 1000);
} }
if (cmcdConfiguration.isMeasuredThroughputLoggingAllowed() if (cmcdConfiguration.isMeasuredThroughputLoggingAllowed()
@ -186,6 +258,10 @@ public final class CmcdHeadersFactory {
return httpRequestHeaders.buildOrThrow(); return httpRequestHeaders.buildOrThrow();
} }
private boolean getIsInitSegment() {
return objectType != null && objectType.equals(OBJECT_TYPE_INIT_SEGMENT);
}
/** Keys whose values vary with the object being requested. Contains CMCD fields: {@code br}. */ /** Keys whose values vary with the object being requested. Contains CMCD fields: {@code br}. */
private static final class CmcdObject { private static final class CmcdObject {
@ -194,6 +270,7 @@ public final class CmcdHeadersFactory {
private int bitrateKbps; private int bitrateKbps;
private int topBitrateKbps; private int topBitrateKbps;
private long objectDurationMs; private long objectDurationMs;
@Nullable private @ObjectType String objectType;
@Nullable private String customData; @Nullable private String customData;
/** Creates a new instance with default values. */ /** Creates a new instance with default values. */
@ -231,6 +308,13 @@ public final class CmcdHeadersFactory {
return this; return this;
} }
/** Sets the {@link CmcdObject#objectType}. The default value is {@code null}. */
@CanIgnoreReturnValue
public Builder setObjectType(@Nullable @ObjectType String objectType) {
this.objectType = objectType;
return this;
}
/** Sets the {@link CmcdObject#customData}. The default value is {@code null}. */ /** Sets the {@link CmcdObject#customData}. The default value is {@code null}. */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder setCustomData(@Nullable String customData) { public Builder setCustomData(@Nullable String customData) {
@ -267,6 +351,14 @@ public final class CmcdHeadersFactory {
*/ */
public final long objectDurationMs; public final long objectDurationMs;
/**
* The media type of the current object being requested , or {@code null} if unset. Must be one
* of the allowed object types specified by the {@link ObjectType} annotation.
*
* <p>If the object type being requested is unknown, then this key MUST NOT be used.
*/
@Nullable public final @ObjectType String objectType;
/** /**
* Custom data where the values of the keys vary with the object being requested, or {@code * Custom data where the values of the keys vary with the object being requested, or {@code
* null} if unset. * null} if unset.
@ -280,6 +372,7 @@ public final class CmcdHeadersFactory {
this.bitrateKbps = builder.bitrateKbps; this.bitrateKbps = builder.bitrateKbps;
this.topBitrateKbps = builder.topBitrateKbps; this.topBitrateKbps = builder.topBitrateKbps;
this.objectDurationMs = builder.objectDurationMs; this.objectDurationMs = builder.objectDurationMs;
this.objectType = builder.objectType;
this.customData = builder.customData; this.customData = builder.customData;
} }
@ -305,6 +398,10 @@ public final class CmcdHeadersFactory {
Util.formatInvariant( Util.formatInvariant(
"%s=%d,", CmcdConfiguration.KEY_OBJECT_DURATION, objectDurationMs)); "%s=%d,", CmcdConfiguration.KEY_OBJECT_DURATION, objectDurationMs));
} }
if (!TextUtils.isEmpty(objectType)) {
headerValue.append(
Util.formatInvariant("%s=%s,", CmcdConfiguration.KEY_OBJECT_TYPE, objectType));
}
if (!TextUtils.isEmpty(customData)) { if (!TextUtils.isEmpty(customData)) {
headerValue.append(Util.formatInvariant("%s,", customData)); headerValue.append(Util.formatInvariant("%s,", customData));
} }
@ -376,6 +473,10 @@ public final class CmcdHeadersFactory {
* The buffer length in milliseconds associated with the media object being requested, or {@link * The buffer length in milliseconds associated with the media object being requested, or {@link
* C#TIME_UNSET} if unset. * C#TIME_UNSET} if unset.
* *
* <p>This key SHOULD only be sent with an {@link CmcdObject#objectType} of {@link
* #OBJECT_TYPE_AUDIO_ONLY}, {@link #OBJECT_TYPE_VIDEO_ONLY} or {@link
* #OBJECT_TYPE_MUXED_AUDIO_AND_VIDEO}.
*
* <p>This value MUST be rounded to the nearest 100 ms. * <p>This value MUST be rounded to the nearest 100 ms.
*/ */
public final long bufferLengthMs; public final long bufferLengthMs;
@ -527,15 +628,16 @@ public final class CmcdHeadersFactory {
*/ */
@Nullable public final String sessionId; @Nullable public final String sessionId;
/** /**
* The streaming format that defines the current request. d = MPEG DASH, h = HTTP Live Streaming * The streaming format that defines the current request , or {@code null} if unset. Must be one
* (HLS), s = Smooth Streaming and o = other. If the streaming format being requested is * of the allowed streaming formats specified by the {@link StreamingFormat} annotation.
* unknown, then this key MUST NOT be used. *
* <p>If the streaming format being requested is unknown, then this key MUST NOT be used.
*/ */
@Nullable public final @StreamingFormat String streamingFormat; @Nullable public final @StreamingFormat String streamingFormat;
/** /**
* Type of stream. v = all segments are available e.g., VOD and l = segments become available * Type of stream, or {@code null} if unset. Must be one of the allowed stream types specified
* over time e.g., LIVE. * by the {@link StreamType} annotation.
*/ */
@Nullable public final @StreamType String streamType; @Nullable public final @StreamType String streamType;

View File

@ -662,7 +662,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders = ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
cmcdHeadersFactory == null cmcdHeadersFactory == null
? ImmutableMap.of() ? ImmutableMap.of()
: cmcdHeadersFactory.createHttpRequestHeaders(); : cmcdHeadersFactory
.setObjectType(CmcdHeadersFactory.OBJECT_TYPE_INIT_SEGMENT)
.createHttpRequestHeaders();
DataSpec dataSpec = DataSpec dataSpec =
DashUtil.buildDataSpec( DashUtil.buildDataSpec(
representation, representation,
@ -706,6 +708,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
? ImmutableMap.of() ? ImmutableMap.of()
: cmcdHeadersFactory : cmcdHeadersFactory
.setChunkDurationUs(endTimeUs - startTimeUs) .setChunkDurationUs(endTimeUs - startTimeUs)
.setObjectType(CmcdHeadersFactory.getObjectType(trackSelection))
.createHttpRequestHeaders(); .createHttpRequestHeaders();
DataSpec dataSpec = DataSpec dataSpec =
DashUtil.buildDataSpec( DashUtil.buildDataSpec(
@ -755,6 +758,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
? ImmutableMap.of() ? ImmutableMap.of()
: cmcdHeadersFactory : cmcdHeadersFactory
.setChunkDurationUs(endTimeUs - startTimeUs) .setChunkDurationUs(endTimeUs - startTimeUs)
.setObjectType(CmcdHeadersFactory.getObjectType(trackSelection))
.createHttpRequestHeaders(); .createHttpRequestHeaders();
DataSpec dataSpec = DataSpec dataSpec =
DashUtil.buildDataSpec( DashUtil.buildDataSpec(

View File

@ -314,7 +314,7 @@ public class DefaultDashChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=700,tb=1300,d=4000", "br=700,tb=1300,d=4000,ot=v",
"CMCD-Request", "CMCD-Request",
"bl=0,mtp=1000", "bl=0,mtp=1000",
"CMCD-Session", "CMCD-Session",
@ -359,7 +359,7 @@ public class DefaultDashChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=700,tb=1300,d=4000", "br=700,tb=1300,d=4000,ot=v",
"CMCD-Request", "CMCD-Request",
"bl=0,mtp=1000", "bl=0,mtp=1000",
"CMCD-Session", "CMCD-Session",
@ -405,7 +405,7 @@ public class DefaultDashChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=700,tb=1300,d=4000,key1=value1", "br=700,tb=1300,d=4000,ot=v,key1=value1",
"CMCD-Request", "CMCD-Request",
"bl=0,mtp=1000,key2=\"stringValue\"", "bl=0,mtp=1000,key2=\"stringValue\"",
"CMCD-Session", "CMCD-Session",

View File

@ -27,6 +27,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroup;
import androidx.media3.common.util.TimestampAdjuster; import androidx.media3.common.util.TimestampAdjuster;
import androidx.media3.common.util.UriUtil; import androidx.media3.common.util.UriUtil;
@ -488,21 +489,27 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
trackSelection, trackSelection,
bufferedDurationUs, bufferedDurationUs,
/* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_HLS, /* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_HLS,
/* isLive= */ !playlist.hasEndTag); /* isLive= */ !playlist.hasEndTag)
.setObjectType(
getIsMuxedAudioAndVideo()
? CmcdHeadersFactory.OBJECT_TYPE_MUXED_AUDIO_AND_VIDEO
: CmcdHeadersFactory.getObjectType(trackSelection));
// Check if the media segment or its initialization segment are fully encrypted. // Check if the media segment or its initialization segment are fully encrypted.
@Nullable @Nullable
Uri initSegmentKeyUri = Uri initSegmentKeyUri =
getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase.initializationSegment); getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase.initializationSegment);
out.chunk = out.chunk =
maybeCreateEncryptionChunkFor(initSegmentKeyUri, selectedTrackIndex, cmcdHeadersFactory); maybeCreateEncryptionChunkFor(
initSegmentKeyUri, selectedTrackIndex, /* isInitSegment= */ true, cmcdHeadersFactory);
if (out.chunk != null) { if (out.chunk != null) {
return; return;
} }
@Nullable @Nullable
Uri mediaSegmentKeyUri = getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase); Uri mediaSegmentKeyUri = getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase);
out.chunk = out.chunk =
maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex, cmcdHeadersFactory); maybeCreateEncryptionChunkFor(
mediaSegmentKeyUri, selectedTrackIndex, /* isInitSegment= */ false, cmcdHeadersFactory);
if (out.chunk != null) { if (out.chunk != null) {
return; return;
} }
@ -541,6 +548,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
cmcdHeadersFactory); cmcdHeadersFactory);
} }
private boolean getIsMuxedAudioAndVideo() {
Format format = trackGroup.getFormat(trackSelection.getSelectedIndex());
String audioMimeType = MimeTypes.getAudioMediaMimeType(format.codecs);
String videoMimeType = MimeTypes.getVideoMediaMimeType(format.codecs);
return audioMimeType != null && videoMimeType != null;
}
@Nullable @Nullable
private static SegmentBaseHolder getNextSegmentHolder( private static SegmentBaseHolder getNextSegmentHolder(
HlsMediaPlaylist mediaPlaylist, long nextMediaSequence, int nextPartIndex) { HlsMediaPlaylist mediaPlaylist, long nextMediaSequence, int nextPartIndex) {
@ -848,6 +862,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private Chunk maybeCreateEncryptionChunkFor( private Chunk maybeCreateEncryptionChunkFor(
@Nullable Uri keyUri, @Nullable Uri keyUri,
int selectedTrackIndex, int selectedTrackIndex,
boolean isInitSegment,
@Nullable CmcdHeadersFactory cmcdHeadersFactory) { @Nullable CmcdHeadersFactory cmcdHeadersFactory) {
if (keyUri == null) { if (keyUri == null) {
return null; return null;
@ -861,10 +876,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
keyCache.put(keyUri, encryptionKey); keyCache.put(keyUri, encryptionKey);
return null; return null;
} }
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders = ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
cmcdHeadersFactory == null ImmutableMap.of();
? ImmutableMap.of() if (cmcdHeadersFactory != null) {
: cmcdHeadersFactory.createHttpRequestHeaders(); if (isInitSegment) {
cmcdHeadersFactory.setObjectType(CmcdHeadersFactory.OBJECT_TYPE_INIT_SEGMENT);
}
httpRequestHeaders = cmcdHeadersFactory.createHttpRequestHeaders();
}
DataSpec dataSpec = DataSpec dataSpec =
new DataSpec.Builder() new DataSpec.Builder()
.setUri(keyUri) .setUri(keyUri)

View File

@ -141,8 +141,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
? getEncryptionIvArray(Assertions.checkNotNull(initSegment.encryptionIV)) ? getEncryptionIvArray(Assertions.checkNotNull(initSegment.encryptionIV))
: null; : null;
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url); Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> initHttpRequestHeaders =
cmcdHeadersFactory == null
? ImmutableMap.of()
: cmcdHeadersFactory
.setObjectType(CmcdHeadersFactory.OBJECT_TYPE_INIT_SEGMENT)
.createHttpRequestHeaders();
initDataSpec = initDataSpec =
new DataSpec(initSegmentUri, initSegment.byteRangeOffset, initSegment.byteRangeLength); new DataSpec.Builder()
.setUri(initSegmentUri)
.setPosition(initSegment.byteRangeOffset)
.setLength(initSegment.byteRangeLength)
.setHttpRequestHeaders(initHttpRequestHeaders)
.build();
initDataSource = buildDataSource(dataSource, initSegmentKey, initSegmentIv); initDataSource = buildDataSource(dataSource, initSegmentKey, initSegmentIv);
} }

View File

@ -210,7 +210,7 @@ public class HlsChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=800,tb=800,d=4000", "br=800,tb=800,d=4000,ot=v",
"CMCD-Request", "CMCD-Request",
"bl=0", "bl=0",
"CMCD-Session", "CMCD-Session",
@ -256,7 +256,7 @@ public class HlsChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=800,tb=800,d=4000", "br=800,tb=800,d=4000,ot=v",
"CMCD-Request", "CMCD-Request",
"bl=0", "bl=0",
"CMCD-Session", "CMCD-Session",
@ -303,7 +303,7 @@ public class HlsChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=800,tb=800,d=4000,key1=value1", "br=800,tb=800,d=4000,ot=v,key1=value1",
"CMCD-Request", "CMCD-Request",
"bl=0,key2=\"stringValue\"", "bl=0,key2=\"stringValue\"",
"CMCD-Session", "CMCD-Session",

View File

@ -290,7 +290,8 @@ public class DefaultSsChunkSource implements SsChunkSource {
bufferedDurationUs, bufferedDurationUs,
/* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_SS, /* streamingFormat= */ CmcdHeadersFactory.STREAMING_FORMAT_SS,
/* isLive= */ manifest.isLive) /* isLive= */ manifest.isLive)
.setChunkDurationUs(chunkEndTimeUs - chunkStartTimeUs); .setChunkDurationUs(chunkEndTimeUs - chunkStartTimeUs)
.setObjectType(CmcdHeadersFactory.getObjectType(trackSelection));
out.chunk = out.chunk =
newMediaChunk( newMediaChunk(

View File

@ -64,7 +64,7 @@ public class DefaultSsChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=308,tb=1536,d=1968", "br=308,tb=1536,d=1968,ot=v",
"CMCD-Request", "CMCD-Request",
"bl=0,mtp=1000", "bl=0,mtp=1000",
"CMCD-Session", "CMCD-Session",
@ -109,7 +109,7 @@ public class DefaultSsChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=308,tb=1536,d=1968", "br=308,tb=1536,d=1968,ot=v",
"CMCD-Request", "CMCD-Request",
"bl=0,mtp=1000", "bl=0,mtp=1000",
"CMCD-Session", "CMCD-Session",
@ -155,7 +155,7 @@ public class DefaultSsChunkSourceTest {
assertThat(output.chunk.dataSpec.httpRequestHeaders) assertThat(output.chunk.dataSpec.httpRequestHeaders)
.containsExactly( .containsExactly(
"CMCD-Object", "CMCD-Object",
"br=308,tb=1536,d=1968,key1=value1", "br=308,tb=1536,d=1968,ot=v,key1=value1",
"CMCD-Request", "CMCD-Request",
"bl=0,mtp=1000,key2=\"stringValue\"", "bl=0,mtp=1000,key2=\"stringValue\"",
"CMCD-Session", "CMCD-Session",