From 52878b2aca599d7aee3d87d76d602e81b1946d00 Mon Sep 17 00:00:00 2001 From: rohks Date: Thu, 15 Jun 2023 16:48:55 +0100 Subject: [PATCH] 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 --- .../media3/exoplayer/dash/DashUtil.java | 41 ++++++++++++----- .../dash/DefaultDashChunkSource.java | 45 ++++++++++++------- .../dash/offline/DashDownloader.java | 9 +++- .../media3/exoplayer/hls/HlsChunkSource.java | 30 ++++++++----- 4 files changed, 86 insertions(+), 39 deletions(-) diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashUtil.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashUtil.java index 91465ef5ad..f4bb6509cd 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashUtil.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashUtil.java @@ -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 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}. - * - *

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, diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java index 1d3e221059..2b573b4495 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java @@ -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 diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java index be7ead9a1c..d7aa36033c 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java @@ -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 { 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); } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index 66d052a0fd..6ea2000839 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -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,