Add CMCD logging when requesting initialization chunk for DASH and HLS

Additionally, two existing methods to `buildDataSpec` in `DashUtil` have been deprecated, while a new method has been added that allows the inclusion of `httpRequestHeaders`.

Issue: google/ExoPlayer#8699

#minor-release

PiperOrigin-RevId: 540594444
This commit is contained in:
rohks 2023-06-15 16:48:55 +01:00 committed by Marc Baechinger
parent 8bf40e7355
commit 52878b2aca
4 changed files with 86 additions and 39 deletions

View File

@ -37,8 +37,10 @@ import androidx.media3.extractor.ChunkIndex;
import androidx.media3.extractor.Extractor;
import androidx.media3.extractor.mkv.MatroskaExtractor;
import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/** Utility methods for DASH streams. */
@UnstableApi
@ -52,33 +54,47 @@ public final class DashUtil {
* @param requestUri The {@link RangedUri} of the data to request.
* @param flags Flags to be set on the returned {@link DataSpec}. See {@link
* DataSpec.Builder#setFlags(int)}.
* @param httpRequestHeaders The {@link DataSpec#httpRequestHeaders}.
* @return The {@link DataSpec}.
*/
public static DataSpec buildDataSpec(
Representation representation, String baseUrl, RangedUri requestUri, int flags) {
Representation representation,
String baseUrl,
RangedUri requestUri,
int flags,
Map<String, String> httpRequestHeaders) {
return new DataSpec.Builder()
.setUri(requestUri.resolveUri(baseUrl))
.setPosition(requestUri.start)
.setLength(requestUri.length)
.setKey(resolveCacheKey(representation, requestUri))
.setFlags(flags)
.setHttpRequestHeaders(httpRequestHeaders)
.build();
}
/**
* Builds a {@link DataSpec} for a given {@link RangedUri} belonging to {@link Representation}.
*
* <p>Uses the first base URL of the representation to build the data spec.
*
* @param representation The {@link Representation} to which the request belongs.
* @param requestUri The {@link RangedUri} of the data to request.
* @param flags Flags to be set on the returned {@link DataSpec}. See {@link
* DataSpec.Builder#setFlags(int)}.
* @return The {@link DataSpec}.
* @deprecated Use {@link #buildDataSpec(Representation, String, RangedUri, int, Map)} instead.
*/
@Deprecated
public static DataSpec buildDataSpec(
Representation representation, String baseUrl, RangedUri requestUri, int flags) {
return buildDataSpec(
representation, baseUrl, requestUri, flags, /* httpRequestHeaders= */ ImmutableMap.of());
}
/**
* @deprecated Use {@link #buildDataSpec(Representation, String, RangedUri, int, Map)} instead.
*/
@Deprecated
public static DataSpec buildDataSpec(
Representation representation, RangedUri requestUri, int flags) {
return buildDataSpec(representation, representation.baseUrls.get(0).url, requestUri, flags);
return buildDataSpec(
representation,
representation.baseUrls.get(0).url,
requestUri,
flags,
/* httpRequestHeaders= */ ImmutableMap.of());
}
/**
@ -292,7 +308,8 @@ public final class DashUtil {
representation,
representation.baseUrls.get(baseUrlIndex).url,
requestUri,
/* flags= */ 0);
/* flags= */ 0,
/* httpRequestHeaders= */ ImmutableMap.of());
InitializationChunk initializationChunk =
new InitializationChunk(
dataSource,

View File

@ -370,6 +370,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
trackSelection.updateSelectedTrack(
playbackPositionUs, bufferedDurationUs, availableLiveDurationUs, queue, chunkIterators);
@Nullable
CmcdLog cmcdLog =
cmcdConfiguration == null
? null
: CmcdLog.createInstance(
cmcdConfiguration, trackSelection, playbackPositionUs, loadPositionUs);
RepresentationHolder representationHolder =
updateSelectedBaseUrl(trackSelection.getSelectedIndex());
if (representationHolder.chunkExtractor != null) {
@ -392,7 +399,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
trackSelection.getSelectionReason(),
trackSelection.getSelectionData(),
pendingInitializationUri,
pendingIndexUri);
pendingIndexUri,
cmcdLog);
return;
}
}
@ -445,13 +453,6 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
}
@Nullable
CmcdLog cmcdLog =
cmcdConfiguration == null
? null
: CmcdLog.createInstance(
cmcdConfiguration, trackSelection, playbackPositionUs, loadPositionUs);
long seekTimeUs = queue.isEmpty() ? loadPositionUs : C.TIME_UNSET;
out.chunk =
newMediaChunk(
@ -639,7 +640,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
@C.SelectionReason int trackSelectionReason,
@Nullable Object trackSelectionData,
@Nullable RangedUri initializationUri,
@Nullable RangedUri indexUri) {
@Nullable RangedUri indexUri,
@Nullable CmcdLog cmcdLog) {
Representation representation = representationHolder.representation;
@Nullable RangedUri requestUri;
if (initializationUri != null) {
@ -653,9 +655,15 @@ public class DefaultDashChunkSource implements DashChunkSource {
} else {
requestUri = indexUri;
}
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
DataSpec dataSpec =
DashUtil.buildDataSpec(
representation, representationHolder.selectedBaseUrl.url, requestUri, /* flags= */ 0);
representation,
representationHolder.selectedBaseUrl.url,
requestUri,
/* flags= */ 0,
httpRequestHeaders);
return new InitializationChunk(
dataSource,
dataSpec,
@ -691,8 +699,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec =
DashUtil.buildDataSpec(
representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
dataSpec = dataSpec.buildUpon().setHttpRequestHeaders(httpRequestHeaders).build();
representation,
representationHolder.selectedBaseUrl.url,
segmentUri,
flags,
httpRequestHeaders);
return new SingleSampleMediaChunk(
dataSource,
dataSpec,
@ -731,8 +742,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec =
DashUtil.buildDataSpec(
representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
dataSpec = dataSpec.buildUpon().setHttpRequestHeaders(httpRequestHeaders).build();
representation,
representationHolder.selectedBaseUrl.url,
segmentUri,
flags,
httpRequestHeaders);
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
return new ContainerMediaChunk(
dataSource,
@ -803,7 +817,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
representationHolder.representation,
representationHolder.selectedBaseUrl.url,
segmentUri,
flags);
flags,
/* httpRequestHeaders= */ ImmutableMap.of());
}
@Override

View File

@ -40,6 +40,7 @@ import androidx.media3.exoplayer.offline.DownloadException;
import androidx.media3.exoplayer.offline.SegmentDownloader;
import androidx.media3.exoplayer.upstream.ParsingLoadable.Parser;
import androidx.media3.extractor.ChunkIndex;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -227,7 +228,13 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
private Segment createSegment(
Representation representation, String baseUrl, long startTimeUs, RangedUri rangedUri) {
DataSpec dataSpec = DashUtil.buildDataSpec(representation, baseUrl, rangedUri, /* flags= */ 0);
DataSpec dataSpec =
DashUtil.buildDataSpec(
representation,
baseUrl,
rangedUri,
/* flags= */ 0,
/* httpRequestHeaders= */ ImmutableMap.of());
return new Segment(startTimeUs, dataSpec);
}

View File

@ -50,6 +50,7 @@ import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
import androidx.media3.exoplayer.upstream.CmcdLog;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.primitives.Ints;
import java.io.IOException;
@ -478,17 +479,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
seenExpectedPlaylistError = false;
expectedPlaylistUrl = null;
@Nullable
CmcdLog cmcdLog =
cmcdConfiguration == null
? null
: CmcdLog.createInstance(
cmcdConfiguration, trackSelection, playbackPositionUs, loadPositionUs);
// 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);
out.chunk = maybeCreateEncryptionChunkFor(initSegmentKeyUri, selectedTrackIndex, cmcdLog);
if (out.chunk != null) {
return;
}
@Nullable
Uri mediaSegmentKeyUri = getFullEncryptionKeyUri(playlist, segmentBaseHolder.segmentBase);
out.chunk = maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex);
out.chunk = maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex, cmcdLog);
if (out.chunk != null) {
return;
}
@ -504,13 +512,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return;
}
@Nullable
CmcdLog cmcdLog =
cmcdConfiguration == null
? null
: CmcdLog.createInstance(
cmcdConfiguration, trackSelection, playbackPositionUs, loadPositionUs);
out.chunk =
HlsMediaChunk.createInstance(
extractorFactory,
@ -838,7 +839,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
@Nullable
private Chunk maybeCreateEncryptionChunkFor(@Nullable Uri keyUri, int selectedTrackIndex) {
private Chunk maybeCreateEncryptionChunkFor(
@Nullable Uri keyUri, int selectedTrackIndex, @Nullable CmcdLog cmcdLog) {
if (keyUri == null) {
return null;
}
@ -851,8 +853,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
keyCache.put(keyUri, encryptionKey);
return null;
}
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
DataSpec dataSpec =
new DataSpec.Builder().setUri(keyUri).setFlags(DataSpec.FLAG_ALLOW_GZIP).build();
new DataSpec.Builder()
.setUri(keyUri)
.setFlags(DataSpec.FLAG_ALLOW_GZIP)
.setHttpRequestHeaders(httpRequestHeaders)
.build();
return new EncryptionKeyChunk(
encryptionDataSource,
dataSpec,