Add fields streaming format(sf), stream type(st) and version(v)

Added these CMCD-Session fields to Common Media Client Data (CMCD) logging.

PiperOrigin-RevId: 547435498
(cherry picked from commit 0412a36564)
This commit is contained in:
rohks 2023-07-12 10:25:50 +01:00 committed by Tianyi Feng
parent 7d35f18732
commit 272c844abd
9 changed files with 159 additions and 19 deletions

View file

@ -8,6 +8,8 @@
* ExoPlayer: * ExoPlayer:
* Fix issue in `PlaybackStatsListener` where spurious `PlaybackStats` are * Fix issue in `PlaybackStatsListener` where spurious `PlaybackStats` are
created after the playlist is cleared. created after the playlist is cleared.
* Add fields streaming format (sf), stream type (st) and version (v) to
Common Media Client Data (CMCD) logging.
* Text: * Text:
* CEA-608: Change cue truncation logic to only consider visible text. * CEA-608: Change cue truncation logic to only consider visible text.
Previously indent and tab offset were included when limiting the cue Previously indent and tab offset were included when limiting the cue

View file

@ -60,7 +60,10 @@ public final class CmcdConfiguration {
KEY_BUFFER_LENGTH, KEY_BUFFER_LENGTH,
KEY_CONTENT_ID, KEY_CONTENT_ID,
KEY_SESSION_ID, KEY_SESSION_ID,
KEY_MAXIMUM_REQUESTED_BITRATE KEY_MAXIMUM_REQUESTED_BITRATE,
KEY_STREAMING_FORMAT,
KEY_STREAM_TYPE,
KEY_VERSION
}) })
@Documented @Documented
@Target(TYPE_USE) @Target(TYPE_USE)
@ -78,6 +81,9 @@ public final class CmcdConfiguration {
public static final String KEY_CONTENT_ID = "cid"; public static final String KEY_CONTENT_ID = "cid";
public static final String KEY_SESSION_ID = "sid"; public static final String KEY_SESSION_ID = "sid";
public static final String KEY_MAXIMUM_REQUESTED_BITRATE = "rtp"; public static final String KEY_MAXIMUM_REQUESTED_BITRATE = "rtp";
public static final String KEY_STREAMING_FORMAT = "sf";
public static final String KEY_STREAM_TYPE = "st";
public static final String KEY_VERSION = "v";
/** /**
* Factory for {@link CmcdConfiguration} instances. * Factory for {@link CmcdConfiguration} instances.
@ -199,7 +205,7 @@ public final class CmcdConfiguration {
} }
/** /**
* Whether logging bitrate is allowed based on the {@linkplain RequestConfig request * Returns whether logging bitrate is allowed based on the {@linkplain RequestConfig request
* configuration}. * configuration}.
*/ */
public boolean isBitrateLoggingAllowed() { public boolean isBitrateLoggingAllowed() {
@ -207,7 +213,7 @@ public final class CmcdConfiguration {
} }
/** /**
* Whether logging buffer length is allowed based on the {@linkplain RequestConfig request * Returns whether logging buffer length is allowed based on the {@linkplain RequestConfig request
* configuration}. * configuration}.
*/ */
public boolean isBufferLengthLoggingAllowed() { public boolean isBufferLengthLoggingAllowed() {
@ -215,7 +221,7 @@ public final class CmcdConfiguration {
} }
/** /**
* Whether logging content ID is allowed based on the {@linkplain RequestConfig request * Returns whether logging content ID is allowed based on the {@linkplain RequestConfig request
* configuration}. * configuration}.
*/ */
public boolean isContentIdLoggingAllowed() { public boolean isContentIdLoggingAllowed() {
@ -223,7 +229,7 @@ public final class CmcdConfiguration {
} }
/** /**
* Whether logging session ID is allowed based on the {@linkplain RequestConfig request * Returns whether logging session ID is allowed based on the {@linkplain RequestConfig request
* configuration}. * configuration}.
*/ */
public boolean isSessionIdLoggingAllowed() { public boolean isSessionIdLoggingAllowed() {
@ -231,10 +237,26 @@ public final class CmcdConfiguration {
} }
/** /**
* Whether logging maximum requested throughput is allowed based on the {@linkplain RequestConfig * Returns whether logging maximum requested throughput is allowed based on the {@linkplain
* request configuration}. * RequestConfig request configuration}.
*/ */
public boolean isMaximumRequestThroughputLoggingAllowed() { public boolean isMaximumRequestThroughputLoggingAllowed() {
return requestConfig.isKeyAllowed(KEY_MAXIMUM_REQUESTED_BITRATE); return requestConfig.isKeyAllowed(KEY_MAXIMUM_REQUESTED_BITRATE);
} }
/**
* Returns whether logging streaming format is allowed based on the {@linkplain RequestConfig
* request configuration}.
*/
public boolean isStreamingFormatLoggingAllowed() {
return requestConfig.isKeyAllowed(KEY_STREAMING_FORMAT);
}
/**
* Returns whether logging stream type is allowed based on the {@linkplain RequestConfig request
* configuration}.
*/
public boolean isStreamTypeLoggingAllowed() {
return requestConfig.isKeyAllowed(KEY_STREAM_TYPE);
}
} }

View file

@ -16,15 +16,21 @@
package androidx.media3.exoplayer.upstream; package androidx.media3.exoplayer.upstream;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringDef;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection; import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
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, * Represents the data for CMCD (Common Media Client Data) in adaptive streaming formats DASH, HLS,
@ -37,6 +43,35 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
@UnstableApi @UnstableApi
public final class CmcdLog { public final class CmcdLog {
/** Indicates the streaming format used for media content. */
@Retention(RetentionPolicy.SOURCE)
@StringDef({STREAMING_FORMAT_DASH, STREAMING_FORMAT_HLS, STREAMING_FORMAT_SS})
@Documented
@Target(TYPE_USE)
public @interface StreamingFormat {}
/** Indicates the type of streaming for media content. */
@Retention(RetentionPolicy.SOURCE)
@StringDef({STREAM_TYPE_VOD, STREAM_TYPE_LIVE})
@Documented
@Target(TYPE_USE)
public @interface StreamType {}
/** Represents the Dynamic Adaptive Streaming over HTTP (DASH) format. */
public static final String STREAMING_FORMAT_DASH = "d";
/** Represents the HTTP Live Streaming (HLS) format. */
public static final String STREAMING_FORMAT_HLS = "h";
/** Represents the Smooth Streaming (SS) format. */
public static final String STREAMING_FORMAT_SS = "s";
/** Represents the Video on Demand (VOD) stream type. */
public static final String STREAM_TYPE_VOD = "v";
/** Represents the Live Streaming stream type. */
public static final String STREAM_TYPE_LIVE = "l";
/** /**
* Creates a new instance. * Creates a new instance.
* *
@ -44,11 +79,17 @@ public final class CmcdLog {
* @param trackSelection The {@linkplain ExoTrackSelection track selection}. * @param trackSelection The {@linkplain ExoTrackSelection track selection}.
* @param bufferedDurationUs The duration of media currently buffered from the current playback * @param bufferedDurationUs The duration of media currently buffered from the current playback
* position, in microseconds. * position, in microseconds.
* @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.
*/ */
public static CmcdLog createInstance( public static CmcdLog createInstance(
CmcdConfiguration cmcdConfiguration, CmcdConfiguration cmcdConfiguration,
ExoTrackSelection trackSelection, ExoTrackSelection trackSelection,
long bufferedDurationUs) { long bufferedDurationUs,
@StreamingFormat String streamingFormat,
boolean isLive) {
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> customData = ImmutableMap<@CmcdConfiguration.HeaderKey String, String> customData =
cmcdConfiguration.requestConfig.getCustomData(); cmcdConfiguration.requestConfig.getCustomData();
int bitrateKbps = trackSelection.getSelectedFormat().bitrate / 1000; int bitrateKbps = trackSelection.getSelectedFormat().bitrate / 1000;
@ -76,6 +117,12 @@ public final class CmcdLog {
if (cmcdConfiguration.isSessionIdLoggingAllowed()) { if (cmcdConfiguration.isSessionIdLoggingAllowed()) {
cmcdSession.setSessionId(cmcdConfiguration.sessionId); cmcdSession.setSessionId(cmcdConfiguration.sessionId);
} }
if (cmcdConfiguration.isStreamingFormatLoggingAllowed()) {
cmcdSession.setStreamingFormat(streamingFormat);
}
if (cmcdConfiguration.isStreamTypeLoggingAllowed()) {
cmcdSession.setStreamType(isLive ? STREAM_TYPE_LIVE : STREAM_TYPE_VOD);
}
CmcdLog.CmcdStatus.Builder cmcdStatus = CmcdLog.CmcdStatus.Builder cmcdStatus =
new CmcdLog.CmcdStatus.Builder() new CmcdLog.CmcdStatus.Builder()
@ -288,6 +335,8 @@ public final class CmcdLog {
public static final class Builder { public static final class Builder {
@Nullable private String contentId; @Nullable private String contentId;
@Nullable private String sessionId; @Nullable private String sessionId;
@Nullable private String streamingFormat;
@Nullable private String streamType;
@Nullable private String customData; @Nullable private String customData;
/** /**
@ -312,6 +361,20 @@ public final class CmcdLog {
return this; return this;
} }
/** Sets the {@link CmcdSession#streamingFormat}. The default value is {@code null}. */
@CanIgnoreReturnValue
public Builder setStreamingFormat(@Nullable @StreamingFormat String streamingFormat) {
this.streamingFormat = streamingFormat;
return this;
}
/** Sets the {@link CmcdSession#streamType}. The default value is {@code null}. */
@CanIgnoreReturnValue
public Builder setStreamType(@Nullable @StreamType String streamType) {
this.streamType = streamType;
return this;
}
/** Sets the {@link CmcdSession#customData}. The default value is {@code null}. */ /** Sets the {@link CmcdSession#customData}. The default value is {@code null}. */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public CmcdSession.Builder setCustomData(@Nullable String customData) { public CmcdSession.Builder setCustomData(@Nullable String customData) {
@ -324,6 +387,13 @@ public final class CmcdLog {
} }
} }
/**
* The version of this specification used for interpreting the defined key names and values. If
* this key is omitted, the client and server MUST interpret the values as being defined by
* version 1. Client SHOULD omit this field if the version is 1.
*/
public static final int VERSION = 1;
/** /**
* A GUID identifying the current content, or {@code null} if unset. * A GUID identifying the current content, or {@code null} if unset.
* *
@ -338,6 +408,19 @@ public final class CmcdLog {
* Maximum length is 64 characters. * Maximum length is 64 characters.
*/ */
@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
* (HLS), s = Smooth Streaming and o = other. If the streaming format being requested is
* unknown, then this key MUST NOT be used.
*/
@Nullable public final String streamingFormat;
/**
* Type of stream. v = all segments are available e.g., VOD and l = segments become available
* over time e.g., LIVE.
*/
@Nullable public final String streamType;
/** /**
* Custom data where the values of the keys are expected to be invariant over the life of the * Custom data where the values of the keys are expected to be invariant over the life of the
* session, or {@code null} if unset. * session, or {@code null} if unset.
@ -350,6 +433,8 @@ public final class CmcdLog {
private CmcdSession(Builder builder) { private CmcdSession(Builder builder) {
this.contentId = builder.contentId; this.contentId = builder.contentId;
this.sessionId = builder.sessionId; this.sessionId = builder.sessionId;
this.streamingFormat = builder.streamingFormat;
this.streamType = builder.streamType;
this.customData = builder.customData; this.customData = builder.customData;
} }
@ -370,6 +455,18 @@ public final class CmcdLog {
headerValue.append( headerValue.append(
Util.formatInvariant("%s=\"%s\",", CmcdConfiguration.KEY_SESSION_ID, sessionId)); Util.formatInvariant("%s=\"%s\",", CmcdConfiguration.KEY_SESSION_ID, sessionId));
} }
if (!TextUtils.isEmpty(this.streamingFormat)) {
headerValue.append(
Util.formatInvariant(
"%s=%s,", CmcdConfiguration.KEY_STREAMING_FORMAT, streamingFormat));
}
if (!TextUtils.isEmpty(this.streamType)) {
headerValue.append(
Util.formatInvariant("%s=%s,", CmcdConfiguration.KEY_STREAM_TYPE, streamType));
}
if (VERSION != 1) {
headerValue.append(Util.formatInvariant("%s=%d,", CmcdConfiguration.KEY_VERSION, VERSION));
}
if (!TextUtils.isEmpty(customData)) { if (!TextUtils.isEmpty(customData)) {
headerValue.append(Util.formatInvariant("%s,", customData)); headerValue.append(Util.formatInvariant("%s,", customData));
} }

View file

@ -60,7 +60,11 @@ public class CmcdLogTest {
.thenReturn(new Format.Builder().setPeakBitrate(840_000).build()); .thenReturn(new Format.Builder().setPeakBitrate(840_000).build());
CmcdLog cmcdLog = CmcdLog cmcdLog =
CmcdLog.createInstance( CmcdLog.createInstance(
cmcdConfiguration, trackSelection, /* bufferedDurationUs= */ 1_760_000); cmcdConfiguration,
trackSelection,
/* bufferedDurationUs= */ 1_760_000,
CmcdLog.STREAMING_FORMAT_DASH,
true);
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> requestHeaders = ImmutableMap<@CmcdConfiguration.HeaderKey String, String> requestHeaders =
cmcdLog.getHttpRequestHeaders(); cmcdLog.getHttpRequestHeaders();
@ -72,7 +76,7 @@ public class CmcdLogTest {
"CMCD-Request", "CMCD-Request",
"bl=1800,key2=\"stringValue\"", "bl=1800,key2=\"stringValue\"",
"CMCD-Session", "CMCD-Session",
"cid=\"mediaId\",sid=\"sessionId\"", "cid=\"mediaId\",sid=\"sessionId\",sf=d,st=l",
"CMCD-Status", "CMCD-Status",
"rtp=1700"); "rtp=1700");
} }

View file

@ -374,7 +374,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
CmcdLog cmcdLog = CmcdLog cmcdLog =
cmcdConfiguration == null cmcdConfiguration == null
? null ? null
: CmcdLog.createInstance(cmcdConfiguration, trackSelection, bufferedDurationUs); : CmcdLog.createInstance(
cmcdConfiguration,
trackSelection,
bufferedDurationUs,
CmcdLog.STREAMING_FORMAT_DASH,
manifest.dynamic);
RepresentationHolder representationHolder = RepresentationHolder representationHolder =
updateSelectedBaseUrl(trackSelection.getSelectedIndex()); updateSelectedBaseUrl(trackSelection.getSelectedIndex());

View file

@ -318,7 +318,7 @@ public class DefaultDashChunkSourceTest {
"CMCD-Request", "CMCD-Request",
"bl=0", "bl=0",
"CMCD-Session", "CMCD-Session",
"cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\""); "cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\",sf=d,st=v");
} }
@Test @Test
@ -363,7 +363,7 @@ public class DefaultDashChunkSourceTest {
"CMCD-Request", "CMCD-Request",
"bl=0", "bl=0",
"CMCD-Session", "CMCD-Session",
"cid=\"mediaIdcontentIdSuffix\"", "cid=\"mediaIdcontentIdSuffix\",sf=d,st=v",
"CMCD-Status", "CMCD-Status",
"rtp=3500"); "rtp=3500");
} }
@ -409,7 +409,7 @@ public class DefaultDashChunkSourceTest {
"CMCD-Request", "CMCD-Request",
"bl=0,key2=\"stringValue\"", "bl=0,key2=\"stringValue\"",
"CMCD-Session", "CMCD-Session",
"cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\",key3=1", "cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\",sf=d,st=v,key3=1",
"CMCD-Status", "CMCD-Status",
"key4=5.0"); "key4=5.0");
} }

View file

@ -483,7 +483,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
CmcdLog cmcdLog = CmcdLog cmcdLog =
cmcdConfiguration == null cmcdConfiguration == null
? null ? null
: CmcdLog.createInstance(cmcdConfiguration, trackSelection, bufferedDurationUs); : CmcdLog.createInstance(
cmcdConfiguration,
trackSelection,
bufferedDurationUs,
CmcdLog.STREAMING_FORMAT_HLS,
!playlist.hasEndTag);
// 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

View file

@ -214,7 +214,7 @@ public class HlsChunkSourceTest {
"CMCD-Request", "CMCD-Request",
"bl=0", "bl=0",
"CMCD-Session", "CMCD-Session",
"cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\""); "cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\",sf=h,st=v");
} }
@Test @Test
@ -260,7 +260,7 @@ public class HlsChunkSourceTest {
"CMCD-Request", "CMCD-Request",
"bl=0", "bl=0",
"CMCD-Session", "CMCD-Session",
"cid=\"mediaIdcontentIdSuffix\"", "cid=\"mediaIdcontentIdSuffix\",sf=h,st=v",
"CMCD-Status", "CMCD-Status",
"rtp=4000"); "rtp=4000");
} }
@ -307,7 +307,7 @@ public class HlsChunkSourceTest {
"CMCD-Request", "CMCD-Request",
"bl=0,key2=\"stringValue\"", "bl=0,key2=\"stringValue\"",
"CMCD-Session", "CMCD-Session",
"cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\",key3=1", "cid=\"mediaId\",sid=\"" + cmcdConfiguration.sessionId + "\",sf=h,st=v,key3=1",
"CMCD-Status", "CMCD-Status",
"key4=5.0"); "key4=5.0");
} }

View file

@ -284,7 +284,12 @@ public class DefaultSsChunkSource implements SsChunkSource {
CmcdLog cmcdLog = CmcdLog cmcdLog =
cmcdConfiguration == null cmcdConfiguration == null
? null ? null
: CmcdLog.createInstance(cmcdConfiguration, trackSelection, bufferedDurationUs); : CmcdLog.createInstance(
cmcdConfiguration,
trackSelection,
bufferedDurationUs,
CmcdLog.STREAMING_FORMAT_SS,
manifest.isLive);
out.chunk = out.chunk =
newMediaChunk( newMediaChunk(