mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Refactor HttpMediaDrmCallback's executePost into a shared utility method within DrmUtil
This commit is contained in:
parent
05cbbffbd7
commit
4ee34cc00e
@ -31,16 +31,26 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
|
import androidx.media3.common.util.Assertions;
|
||||||
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.datasource.DataSource;
|
||||||
|
import androidx.media3.datasource.DataSourceInputStream;
|
||||||
|
import androidx.media3.datasource.DataSpec;
|
||||||
|
import androidx.media3.datasource.HttpDataSource;
|
||||||
|
import androidx.media3.datasource.StatsDataSource;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/** DRM-related utility methods. */
|
/** DRM-related utility methods. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class DrmUtil {
|
public final class DrmUtil {
|
||||||
|
private static final int MAX_MANUAL_REDIRECTS = 5;
|
||||||
|
|
||||||
/** Identifies the operation which caused a DRM-related error. */
|
/** Identifies the operation which caused a DRM-related error. */
|
||||||
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
|
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
|
||||||
@ -131,6 +141,81 @@ public final class DrmUtil {
|
|||||||
&& e.getMessage().contains("Landroid/media/ResourceBusyException;.<init>(");
|
&& e.getMessage().contains("Landroid/media/ResourceBusyException;.<init>(");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a Http Post Request to the supplied {@link StatsDataSource} with retry handling and
|
||||||
|
* returns the entire response in a byte buffer.
|
||||||
|
*
|
||||||
|
* @param dataSource A {@link DataSource} that is usually be HTTP-based
|
||||||
|
* @param url The requesting url
|
||||||
|
* @param httpBody Request Payload
|
||||||
|
* @param requestProperties Http Header Request Properties
|
||||||
|
* @return a byte array that holds the response payload
|
||||||
|
* @throws MediaDrmCallbackException if an exception was encountered during the download
|
||||||
|
*/
|
||||||
|
public static byte[] executePost(
|
||||||
|
DataSource dataSource,
|
||||||
|
String url,
|
||||||
|
@Nullable byte[] httpBody,
|
||||||
|
Map<String, String> requestProperties)
|
||||||
|
throws MediaDrmCallbackException {
|
||||||
|
StatsDataSource statsDataSource = new StatsDataSource(dataSource);
|
||||||
|
int manualRedirectCount = 0;
|
||||||
|
DataSpec dataSpec =
|
||||||
|
new DataSpec.Builder()
|
||||||
|
.setUri(url)
|
||||||
|
.setHttpRequestHeaders(requestProperties)
|
||||||
|
.setHttpMethod(DataSpec.HTTP_METHOD_POST)
|
||||||
|
.setHttpBody(httpBody)
|
||||||
|
.setFlags(DataSpec.FLAG_ALLOW_GZIP)
|
||||||
|
.build();
|
||||||
|
DataSpec originalDataSpec = dataSpec;
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
DataSourceInputStream inputStream = new DataSourceInputStream(statsDataSource, dataSpec);
|
||||||
|
try {
|
||||||
|
return ByteStreams.toByteArray(inputStream);
|
||||||
|
} catch (HttpDataSource.InvalidResponseCodeException e) {
|
||||||
|
@Nullable String redirectUrl = getRedirectUrl(e, manualRedirectCount);
|
||||||
|
if (redirectUrl == null) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
manualRedirectCount++;
|
||||||
|
dataSpec = dataSpec.buildUpon().setUri(redirectUrl).build();
|
||||||
|
} finally {
|
||||||
|
Util.closeQuietly(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MediaDrmCallbackException(
|
||||||
|
originalDataSpec,
|
||||||
|
Assertions.checkNotNull(statsDataSource.getLastOpenedUri()),
|
||||||
|
statsDataSource.getResponseHeaders(),
|
||||||
|
statsDataSource.getBytesRead(),
|
||||||
|
/* cause= */ e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static String getRedirectUrl(
|
||||||
|
HttpDataSource.InvalidResponseCodeException exception, int manualRedirectCount) {
|
||||||
|
// For POST requests, the underlying network stack will not normally follow 307 or 308
|
||||||
|
// redirects automatically. Do so manually here.
|
||||||
|
boolean manuallyRedirect =
|
||||||
|
(exception.responseCode == 307 || exception.responseCode == 308)
|
||||||
|
&& manualRedirectCount < MAX_MANUAL_REDIRECTS;
|
||||||
|
if (!manuallyRedirect) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map<String, List<String>> headerFields = exception.headerFields;
|
||||||
|
if (headerFields != null) {
|
||||||
|
@Nullable List<String> locationHeaders = headerFields.get("Location");
|
||||||
|
if (locationHeaders != null && !locationHeaders.isEmpty()) {
|
||||||
|
return locationHeaders.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
private static final class Api23 {
|
private static final class Api23 {
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.drm;
|
package androidx.media3.exoplayer.drm;
|
||||||
|
|
||||||
|
import static androidx.media3.exoplayer.drm.DrmUtil.executePost;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -23,26 +25,19 @@ import androidx.media3.common.util.Assertions;
|
|||||||
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.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.datasource.DataSourceInputStream;
|
|
||||||
import androidx.media3.datasource.DataSpec;
|
import androidx.media3.datasource.DataSpec;
|
||||||
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException;
|
|
||||||
import androidx.media3.datasource.StatsDataSource;
|
import androidx.media3.datasource.StatsDataSource;
|
||||||
import androidx.media3.exoplayer.drm.ExoMediaDrm.KeyRequest;
|
import androidx.media3.exoplayer.drm.ExoMediaDrm.KeyRequest;
|
||||||
import androidx.media3.exoplayer.drm.ExoMediaDrm.ProvisionRequest;
|
import androidx.media3.exoplayer.drm.ExoMediaDrm.ProvisionRequest;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.io.ByteStreams;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/** A {@link MediaDrmCallback} that makes requests using {@link DataSource} instances. */
|
/** A {@link MediaDrmCallback} that makes requests using {@link DataSource} instances. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
||||||
|
|
||||||
private static final int MAX_MANUAL_REDIRECTS = 5;
|
|
||||||
|
|
||||||
private final DataSource.Factory dataSourceFactory;
|
private final DataSource.Factory dataSourceFactory;
|
||||||
@Nullable private final String defaultLicenseUrl;
|
@Nullable private final String defaultLicenseUrl;
|
||||||
private final boolean forceDefaultLicenseUrl;
|
private final boolean forceDefaultLicenseUrl;
|
||||||
@ -124,7 +119,7 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
|||||||
String url =
|
String url =
|
||||||
request.getDefaultUrl() + "&signedRequest=" + Util.fromUtf8Bytes(request.getData());
|
request.getDefaultUrl() + "&signedRequest=" + Util.fromUtf8Bytes(request.getData());
|
||||||
return executePost(
|
return executePost(
|
||||||
dataSourceFactory,
|
dataSourceFactory.createDataSource(),
|
||||||
url,
|
url,
|
||||||
/* httpBody= */ null,
|
/* httpBody= */ null,
|
||||||
/* requestProperties= */ Collections.emptyMap());
|
/* requestProperties= */ Collections.emptyMap());
|
||||||
@ -159,70 +154,11 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
|||||||
synchronized (keyRequestProperties) {
|
synchronized (keyRequestProperties) {
|
||||||
requestProperties.putAll(keyRequestProperties);
|
requestProperties.putAll(keyRequestProperties);
|
||||||
}
|
}
|
||||||
return executePost(dataSourceFactory, url, request.getData(), requestProperties);
|
return executePost(
|
||||||
}
|
dataSourceFactory.createDataSource(),
|
||||||
|
url,
|
||||||
private static byte[] executePost(
|
request.getData(),
|
||||||
DataSource.Factory dataSourceFactory,
|
requestProperties
|
||||||
String url,
|
);
|
||||||
@Nullable byte[] httpBody,
|
|
||||||
Map<String, String> requestProperties)
|
|
||||||
throws MediaDrmCallbackException {
|
|
||||||
StatsDataSource dataSource = new StatsDataSource(dataSourceFactory.createDataSource());
|
|
||||||
int manualRedirectCount = 0;
|
|
||||||
DataSpec dataSpec =
|
|
||||||
new DataSpec.Builder()
|
|
||||||
.setUri(url)
|
|
||||||
.setHttpRequestHeaders(requestProperties)
|
|
||||||
.setHttpMethod(DataSpec.HTTP_METHOD_POST)
|
|
||||||
.setHttpBody(httpBody)
|
|
||||||
.setFlags(DataSpec.FLAG_ALLOW_GZIP)
|
|
||||||
.build();
|
|
||||||
DataSpec originalDataSpec = dataSpec;
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
DataSourceInputStream inputStream = new DataSourceInputStream(dataSource, dataSpec);
|
|
||||||
try {
|
|
||||||
return ByteStreams.toByteArray(inputStream);
|
|
||||||
} catch (InvalidResponseCodeException e) {
|
|
||||||
@Nullable String redirectUrl = getRedirectUrl(e, manualRedirectCount);
|
|
||||||
if (redirectUrl == null) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
manualRedirectCount++;
|
|
||||||
dataSpec = dataSpec.buildUpon().setUri(redirectUrl).build();
|
|
||||||
} finally {
|
|
||||||
Util.closeQuietly(inputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new MediaDrmCallbackException(
|
|
||||||
originalDataSpec,
|
|
||||||
Assertions.checkNotNull(dataSource.getLastOpenedUri()),
|
|
||||||
dataSource.getResponseHeaders(),
|
|
||||||
dataSource.getBytesRead(),
|
|
||||||
/* cause= */ e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static String getRedirectUrl(
|
|
||||||
InvalidResponseCodeException exception, int manualRedirectCount) {
|
|
||||||
// For POST requests, the underlying network stack will not normally follow 307 or 308
|
|
||||||
// redirects automatically. Do so manually here.
|
|
||||||
boolean manuallyRedirect =
|
|
||||||
(exception.responseCode == 307 || exception.responseCode == 308)
|
|
||||||
&& manualRedirectCount < MAX_MANUAL_REDIRECTS;
|
|
||||||
if (!manuallyRedirect) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Map<String, List<String>> headerFields = exception.headerFields;
|
|
||||||
if (headerFields != null) {
|
|
||||||
@Nullable List<String> locationHeaders = headerFields.get("Location");
|
|
||||||
if (locationHeaders != null && !locationHeaders.isEmpty()) {
|
|
||||||
return locationHeaders.get(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user