mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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:
parent
7e58fde18e
commit
57c73d51da
@ -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
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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",
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
@ -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(
|
||||||
|
@ -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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user