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

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

#minor-release

PiperOrigin-RevId: 547435498
This commit is contained in:
rohks 2023-07-12 10:25:50 +01:00 committed by Rohit Singh
parent bbdc64a732
commit 0412a36564
9 changed files with 159 additions and 19 deletions

View File

@ -32,6 +32,8 @@
* Allow `MediaItem` updates for all `MediaSource` classes provided by the
library via `Player.replaceMediaItem(s)`
(([#33](https://github.com/androidx/media/issues/33)),([#9978](https://github.com/google/ExoPlayer/issues/9978))).
* Add fields streaming format (sf), stream type (st) and version (v) to
Common Media Client Data (CMCD) logging.
* Transformer:
* Parse EXIF rotation data for image inputs.
* Remove `TransformationRequest.HdrMode` annotation type and its

View File

@ -60,7 +60,10 @@ public final class CmcdConfiguration {
KEY_BUFFER_LENGTH,
KEY_CONTENT_ID,
KEY_SESSION_ID,
KEY_MAXIMUM_REQUESTED_BITRATE
KEY_MAXIMUM_REQUESTED_BITRATE,
KEY_STREAMING_FORMAT,
KEY_STREAM_TYPE,
KEY_VERSION
})
@Documented
@Target(TYPE_USE)
@ -78,6 +81,9 @@ public final class CmcdConfiguration {
public static final String KEY_CONTENT_ID = "cid";
public static final String KEY_SESSION_ID = "sid";
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.
@ -201,7 +207,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}.
*/
public boolean isBitrateLoggingAllowed() {
@ -209,7 +215,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}.
*/
public boolean isBufferLengthLoggingAllowed() {
@ -217,7 +223,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}.
*/
public boolean isContentIdLoggingAllowed() {
@ -225,7 +231,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}.
*/
public boolean isSessionIdLoggingAllowed() {
@ -233,10 +239,26 @@ public final class CmcdConfiguration {
}
/**
* Whether logging maximum requested throughput is allowed based on the {@linkplain RequestConfig
* request configuration}.
* Returns whether logging maximum requested throughput is allowed based on the {@linkplain
* RequestConfig request configuration}.
*/
public boolean isMaximumRequestThroughputLoggingAllowed() {
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;
import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.annotation.StringDef;
import androidx.media3.common.C;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import com.google.common.collect.ImmutableMap;
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,
@ -37,6 +43,35 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
@UnstableApi
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.
*
@ -44,11 +79,17 @@ public final class CmcdLog {
* @param trackSelection The {@linkplain ExoTrackSelection track selection}.
* @param bufferedDurationUs The duration of media currently buffered from the current playback
* 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(
CmcdConfiguration cmcdConfiguration,
ExoTrackSelection trackSelection,
long bufferedDurationUs) {
long bufferedDurationUs,
@StreamingFormat String streamingFormat,
boolean isLive) {
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> customData =
cmcdConfiguration.requestConfig.getCustomData();
int bitrateKbps = trackSelection.getSelectedFormat().bitrate / 1000;
@ -76,6 +117,12 @@ public final class CmcdLog {
if (cmcdConfiguration.isSessionIdLoggingAllowed()) {
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 =
new CmcdLog.CmcdStatus.Builder()
@ -290,6 +337,8 @@ public final class CmcdLog {
public static final class Builder {
@Nullable private String contentId;
@Nullable private String sessionId;
@Nullable private String streamingFormat;
@Nullable private String streamType;
@Nullable private String customData;
/**
@ -314,6 +363,20 @@ public final class CmcdLog {
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}. */
@CanIgnoreReturnValue
public CmcdSession.Builder setCustomData(@Nullable String customData) {
@ -326,6 +389,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.
*
@ -342,6 +412,19 @@ public final class CmcdLog {
*/
@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
* session, or {@code null} if unset.
@ -354,6 +437,8 @@ public final class CmcdLog {
private CmcdSession(Builder builder) {
this.contentId = builder.contentId;
this.sessionId = builder.sessionId;
this.streamingFormat = builder.streamingFormat;
this.streamType = builder.streamType;
this.customData = builder.customData;
}
@ -374,6 +459,18 @@ public final class CmcdLog {
headerValue.append(
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)) {
headerValue.append(Util.formatInvariant("%s,", customData));
}

View File

@ -60,7 +60,11 @@ public class CmcdLogTest {
.thenReturn(new Format.Builder().setPeakBitrate(840_000).build());
CmcdLog cmcdLog =
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 =
cmcdLog.getHttpRequestHeaders();
@ -72,7 +76,7 @@ public class CmcdLogTest {
"CMCD-Request",
"bl=1800,key2=\"stringValue\"",
"CMCD-Session",
"cid=\"mediaId\",sid=\"sessionId\"",
"cid=\"mediaId\",sid=\"sessionId\",sf=d,st=l",
"CMCD-Status",
"rtp=1700");
}

View File

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

View File

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

View File

@ -485,7 +485,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
CmcdLog cmcdLog =
cmcdConfiguration == 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.
@Nullable

View File

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

View File

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