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:
parent
8bf40e7355
commit
52878b2aca
@ -37,8 +37,10 @@ import androidx.media3.extractor.ChunkIndex;
|
|||||||
import androidx.media3.extractor.Extractor;
|
import androidx.media3.extractor.Extractor;
|
||||||
import androidx.media3.extractor.mkv.MatroskaExtractor;
|
import androidx.media3.extractor.mkv.MatroskaExtractor;
|
||||||
import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
|
import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/** Utility methods for DASH streams. */
|
/** Utility methods for DASH streams. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -52,33 +54,47 @@ public final class DashUtil {
|
|||||||
* @param requestUri The {@link RangedUri} of the data to request.
|
* @param requestUri The {@link RangedUri} of the data to request.
|
||||||
* @param flags Flags to be set on the returned {@link DataSpec}. See {@link
|
* @param flags Flags to be set on the returned {@link DataSpec}. See {@link
|
||||||
* DataSpec.Builder#setFlags(int)}.
|
* DataSpec.Builder#setFlags(int)}.
|
||||||
|
* @param httpRequestHeaders The {@link DataSpec#httpRequestHeaders}.
|
||||||
* @return The {@link DataSpec}.
|
* @return The {@link DataSpec}.
|
||||||
*/
|
*/
|
||||||
public static DataSpec buildDataSpec(
|
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()
|
return new DataSpec.Builder()
|
||||||
.setUri(requestUri.resolveUri(baseUrl))
|
.setUri(requestUri.resolveUri(baseUrl))
|
||||||
.setPosition(requestUri.start)
|
.setPosition(requestUri.start)
|
||||||
.setLength(requestUri.length)
|
.setLength(requestUri.length)
|
||||||
.setKey(resolveCacheKey(representation, requestUri))
|
.setKey(resolveCacheKey(representation, requestUri))
|
||||||
.setFlags(flags)
|
.setFlags(flags)
|
||||||
|
.setHttpRequestHeaders(httpRequestHeaders)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a {@link DataSpec} for a given {@link RangedUri} belonging to {@link Representation}.
|
* @deprecated Use {@link #buildDataSpec(Representation, String, RangedUri, int, Map)} instead.
|
||||||
*
|
|
||||||
* <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
|
||||||
|
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(
|
public static DataSpec buildDataSpec(
|
||||||
Representation representation, RangedUri requestUri, int flags) {
|
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,
|
||||||
representation.baseUrls.get(baseUrlIndex).url,
|
representation.baseUrls.get(baseUrlIndex).url,
|
||||||
requestUri,
|
requestUri,
|
||||||
/* flags= */ 0);
|
/* flags= */ 0,
|
||||||
|
/* httpRequestHeaders= */ ImmutableMap.of());
|
||||||
InitializationChunk initializationChunk =
|
InitializationChunk initializationChunk =
|
||||||
new InitializationChunk(
|
new InitializationChunk(
|
||||||
dataSource,
|
dataSource,
|
||||||
|
@ -370,6 +370,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
trackSelection.updateSelectedTrack(
|
trackSelection.updateSelectedTrack(
|
||||||
playbackPositionUs, bufferedDurationUs, availableLiveDurationUs, queue, chunkIterators);
|
playbackPositionUs, bufferedDurationUs, availableLiveDurationUs, queue, chunkIterators);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
CmcdLog cmcdLog =
|
||||||
|
cmcdConfiguration == null
|
||||||
|
? null
|
||||||
|
: CmcdLog.createInstance(
|
||||||
|
cmcdConfiguration, trackSelection, playbackPositionUs, loadPositionUs);
|
||||||
|
|
||||||
RepresentationHolder representationHolder =
|
RepresentationHolder representationHolder =
|
||||||
updateSelectedBaseUrl(trackSelection.getSelectedIndex());
|
updateSelectedBaseUrl(trackSelection.getSelectedIndex());
|
||||||
if (representationHolder.chunkExtractor != null) {
|
if (representationHolder.chunkExtractor != null) {
|
||||||
@ -392,7 +399,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
trackSelection.getSelectionReason(),
|
trackSelection.getSelectionReason(),
|
||||||
trackSelection.getSelectionData(),
|
trackSelection.getSelectionData(),
|
||||||
pendingInitializationUri,
|
pendingInitializationUri,
|
||||||
pendingIndexUri);
|
pendingIndexUri,
|
||||||
|
cmcdLog);
|
||||||
return;
|
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;
|
long seekTimeUs = queue.isEmpty() ? loadPositionUs : C.TIME_UNSET;
|
||||||
out.chunk =
|
out.chunk =
|
||||||
newMediaChunk(
|
newMediaChunk(
|
||||||
@ -639,7 +640,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
@C.SelectionReason int trackSelectionReason,
|
@C.SelectionReason int trackSelectionReason,
|
||||||
@Nullable Object trackSelectionData,
|
@Nullable Object trackSelectionData,
|
||||||
@Nullable RangedUri initializationUri,
|
@Nullable RangedUri initializationUri,
|
||||||
@Nullable RangedUri indexUri) {
|
@Nullable RangedUri indexUri,
|
||||||
|
@Nullable CmcdLog cmcdLog) {
|
||||||
Representation representation = representationHolder.representation;
|
Representation representation = representationHolder.representation;
|
||||||
@Nullable RangedUri requestUri;
|
@Nullable RangedUri requestUri;
|
||||||
if (initializationUri != null) {
|
if (initializationUri != null) {
|
||||||
@ -653,9 +655,15 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
} else {
|
} else {
|
||||||
requestUri = indexUri;
|
requestUri = indexUri;
|
||||||
}
|
}
|
||||||
|
ImmutableMap<@CmcdConfiguration.HeaderKey String, String> httpRequestHeaders =
|
||||||
|
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
|
||||||
DataSpec dataSpec =
|
DataSpec dataSpec =
|
||||||
DashUtil.buildDataSpec(
|
DashUtil.buildDataSpec(
|
||||||
representation, representationHolder.selectedBaseUrl.url, requestUri, /* flags= */ 0);
|
representation,
|
||||||
|
representationHolder.selectedBaseUrl.url,
|
||||||
|
requestUri,
|
||||||
|
/* flags= */ 0,
|
||||||
|
httpRequestHeaders);
|
||||||
return new InitializationChunk(
|
return new InitializationChunk(
|
||||||
dataSource,
|
dataSource,
|
||||||
dataSpec,
|
dataSpec,
|
||||||
@ -691,8 +699,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||||
DataSpec dataSpec =
|
DataSpec dataSpec =
|
||||||
DashUtil.buildDataSpec(
|
DashUtil.buildDataSpec(
|
||||||
representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
|
representation,
|
||||||
dataSpec = dataSpec.buildUpon().setHttpRequestHeaders(httpRequestHeaders).build();
|
representationHolder.selectedBaseUrl.url,
|
||||||
|
segmentUri,
|
||||||
|
flags,
|
||||||
|
httpRequestHeaders);
|
||||||
return new SingleSampleMediaChunk(
|
return new SingleSampleMediaChunk(
|
||||||
dataSource,
|
dataSource,
|
||||||
dataSpec,
|
dataSpec,
|
||||||
@ -731,8 +742,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||||
DataSpec dataSpec =
|
DataSpec dataSpec =
|
||||||
DashUtil.buildDataSpec(
|
DashUtil.buildDataSpec(
|
||||||
representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
|
representation,
|
||||||
dataSpec = dataSpec.buildUpon().setHttpRequestHeaders(httpRequestHeaders).build();
|
representationHolder.selectedBaseUrl.url,
|
||||||
|
segmentUri,
|
||||||
|
flags,
|
||||||
|
httpRequestHeaders);
|
||||||
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
|
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
|
||||||
return new ContainerMediaChunk(
|
return new ContainerMediaChunk(
|
||||||
dataSource,
|
dataSource,
|
||||||
@ -803,7 +817,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
representationHolder.representation,
|
representationHolder.representation,
|
||||||
representationHolder.selectedBaseUrl.url,
|
representationHolder.selectedBaseUrl.url,
|
||||||
segmentUri,
|
segmentUri,
|
||||||
flags);
|
flags,
|
||||||
|
/* httpRequestHeaders= */ ImmutableMap.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,6 +40,7 @@ import androidx.media3.exoplayer.offline.DownloadException;
|
|||||||
import androidx.media3.exoplayer.offline.SegmentDownloader;
|
import androidx.media3.exoplayer.offline.SegmentDownloader;
|
||||||
import androidx.media3.exoplayer.upstream.ParsingLoadable.Parser;
|
import androidx.media3.exoplayer.upstream.ParsingLoadable.Parser;
|
||||||
import androidx.media3.extractor.ChunkIndex;
|
import androidx.media3.extractor.ChunkIndex;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -227,7 +228,13 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
|
|||||||
|
|
||||||
private Segment createSegment(
|
private Segment createSegment(
|
||||||
Representation representation, String baseUrl, long startTimeUs, RangedUri rangedUri) {
|
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);
|
return new Segment(startTimeUs, dataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
|||||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||||
import androidx.media3.exoplayer.upstream.CmcdLog;
|
import androidx.media3.exoplayer.upstream.CmcdLog;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -478,17 +479,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
seenExpectedPlaylistError = false;
|
seenExpectedPlaylistError = false;
|
||||||
expectedPlaylistUrl = null;
|
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.
|
// 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 = maybeCreateEncryptionChunkFor(initSegmentKeyUri, selectedTrackIndex);
|
out.chunk = maybeCreateEncryptionChunkFor(initSegmentKeyUri, selectedTrackIndex, cmcdLog);
|
||||||
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 = maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex);
|
out.chunk = maybeCreateEncryptionChunkFor(mediaSegmentKeyUri, selectedTrackIndex, cmcdLog);
|
||||||
if (out.chunk != null) {
|
if (out.chunk != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -504,13 +512,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
CmcdLog cmcdLog =
|
|
||||||
cmcdConfiguration == null
|
|
||||||
? null
|
|
||||||
: CmcdLog.createInstance(
|
|
||||||
cmcdConfiguration, trackSelection, playbackPositionUs, loadPositionUs);
|
|
||||||
|
|
||||||
out.chunk =
|
out.chunk =
|
||||||
HlsMediaChunk.createInstance(
|
HlsMediaChunk.createInstance(
|
||||||
extractorFactory,
|
extractorFactory,
|
||||||
@ -838,7 +839,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Chunk maybeCreateEncryptionChunkFor(@Nullable Uri keyUri, int selectedTrackIndex) {
|
private Chunk maybeCreateEncryptionChunkFor(
|
||||||
|
@Nullable Uri keyUri, int selectedTrackIndex, @Nullable CmcdLog cmcdLog) {
|
||||||
if (keyUri == null) {
|
if (keyUri == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -851,8 +853,14 @@ 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 =
|
||||||
|
cmcdLog == null ? ImmutableMap.of() : cmcdLog.getHttpRequestHeaders();
|
||||||
DataSpec dataSpec =
|
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(
|
return new EncryptionKeyChunk(
|
||||||
encryptionDataSource,
|
encryptionDataSource,
|
||||||
dataSpec,
|
dataSpec,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user