Make DrmConfiguration Bundleable

PiperOrigin-RevId: 533721679
(cherry picked from commit 5008417c8cd12680a3945945e00399cca05915c5)
This commit is contained in:
jbibik 2023-05-20 18:57:22 +01:00 committed by Tofunmi Adigun-Hameed
parent d40f37158a
commit 992c9aa470
4 changed files with 238 additions and 2 deletions

View File

@ -23,6 +23,7 @@ import android.os.Bundle;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.BundleableUtil;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
@ -598,7 +599,7 @@ public final class MediaItem implements Bundleable {
}
/** DRM configuration for a media item. */
public static final class DrmConfiguration {
public static final class DrmConfiguration implements Bundleable {
/** Builder for {@link DrmConfiguration}. */
public static final class Builder {
@ -773,7 +774,6 @@ public final class MediaItem implements Bundleable {
}
public DrmConfiguration build() {
return new DrmConfiguration(this);
}
}
@ -888,6 +888,83 @@ public final class MediaItem implements Bundleable {
result = 31 * result + Arrays.hashCode(keySetId);
return result;
}
// Bundleable implementation
private static final String FIELD_SCHEME = Util.intToStringMaxRadix(0);
private static final String FIELD_LICENSE_URI = Util.intToStringMaxRadix(1);
private static final String FIELD_LICENSE_REQUEST_HEADERS = Util.intToStringMaxRadix(2);
private static final String FIELD_MULTI_SESSION = Util.intToStringMaxRadix(3);
private static final String FIELD_PLAY_CLEAR_CONTENT_WITHOUT_KEY = Util.intToStringMaxRadix(4);
private static final String FIELD_FORCE_DEFAULT_LICENSE_URI = Util.intToStringMaxRadix(5);
private static final String FIELD_FORCED_SESSION_TRACK_TYPES = Util.intToStringMaxRadix(6);
private static final String FIELD_KEY_SET_ID = Util.intToStringMaxRadix(7);
/** Object that can restore {@link DrmConfiguration} from a {@link Bundle}. */
@UnstableApi
public static final Creator<DrmConfiguration> CREATOR = DrmConfiguration::fromBundle;
@UnstableApi
private static DrmConfiguration fromBundle(Bundle bundle) {
UUID scheme = UUID.fromString(checkNotNull(bundle.getString(FIELD_SCHEME)));
@Nullable Uri licenseUri = bundle.getParcelable(FIELD_LICENSE_URI);
Bundle licenseMapAsBundle =
BundleableUtil.getBundleWithDefault(bundle, FIELD_LICENSE_REQUEST_HEADERS, Bundle.EMPTY);
ImmutableMap<String, String> licenseRequestHeaders =
BundleableUtil.bundleToStringImmutableMap(licenseMapAsBundle);
boolean multiSession = bundle.getBoolean(FIELD_MULTI_SESSION, false);
boolean playClearContentWithoutKey =
bundle.getBoolean(FIELD_PLAY_CLEAR_CONTENT_WITHOUT_KEY, false);
boolean forceDefaultLicenseUri = bundle.getBoolean(FIELD_FORCE_DEFAULT_LICENSE_URI, false);
ArrayList<@C.TrackType Integer> forcedSessionTrackTypesArray =
BundleableUtil.getIntegerArrayListWithDefault(
bundle, FIELD_FORCED_SESSION_TRACK_TYPES, new ArrayList<>());
ImmutableList<@C.TrackType Integer> forcedSessionTrackTypes =
ImmutableList.copyOf(forcedSessionTrackTypesArray);
@Nullable byte[] keySetId = bundle.getByteArray(FIELD_KEY_SET_ID);
Builder builder = new Builder(scheme);
return builder
.setLicenseUri(licenseUri)
.setLicenseRequestHeaders(licenseRequestHeaders)
.setMultiSession(multiSession)
.setForceDefaultLicenseUri(forceDefaultLicenseUri)
.setPlayClearContentWithoutKey(playClearContentWithoutKey)
.setForcedSessionTrackTypes(forcedSessionTrackTypes)
.setKeySetId(keySetId)
.build();
}
@UnstableApi
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putString(FIELD_SCHEME, scheme.toString());
if (licenseUri != null) {
bundle.putParcelable(FIELD_LICENSE_URI, licenseUri);
}
if (!licenseRequestHeaders.isEmpty()) {
bundle.putBundle(
FIELD_LICENSE_REQUEST_HEADERS, BundleableUtil.stringMapToBundle(licenseRequestHeaders));
}
if (multiSession) {
bundle.putBoolean(FIELD_MULTI_SESSION, multiSession);
}
if (playClearContentWithoutKey) {
bundle.putBoolean(FIELD_PLAY_CLEAR_CONTENT_WITHOUT_KEY, playClearContentWithoutKey);
}
if (forceDefaultLicenseUri) {
bundle.putBoolean(FIELD_FORCE_DEFAULT_LICENSE_URI, forceDefaultLicenseUri);
}
if (!forcedSessionTrackTypes.isEmpty()) {
bundle.putIntegerArrayList(
FIELD_FORCED_SESSION_TRACK_TYPES, new ArrayList<>(forcedSessionTrackTypes));
}
if (keySetId != null) {
bundle.putByteArray(FIELD_KEY_SET_ID, keySetId);
}
return bundle;
}
}
/** Configuration for playing back linear ads with a media item. */

View File

@ -23,9 +23,12 @@ import android.util.SparseArray;
import androidx.annotation.Nullable;
import androidx.media3.common.Bundleable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Utilities for {@link Bundleable}. */
@UnstableApi
@ -94,6 +97,47 @@ public final class BundleableUtil {
return sparseArray;
}
public static Bundle stringMapToBundle(Map<String, String> bundleableMap) {
Bundle bundle = new Bundle();
for (Map.Entry<String, String> entry : bundleableMap.entrySet()) {
bundle.putString(entry.getKey(), entry.getValue());
}
return bundle;
}
public static HashMap<String, String> bundleToStringHashMap(Bundle bundle) {
HashMap<String, String> map = new HashMap<>();
if (bundle == Bundle.EMPTY) {
return map;
}
for (String key : bundle.keySet()) {
@Nullable String value = bundle.getString(key);
if (value != null) {
map.put(key, value);
}
}
return map;
}
public static ImmutableMap<String, String> bundleToStringImmutableMap(Bundle bundle) {
if (bundle == Bundle.EMPTY) {
return ImmutableMap.of();
}
HashMap<String, String> map = bundleToStringHashMap(bundle);
return ImmutableMap.copyOf(map);
}
public static Bundle getBundleWithDefault(Bundle bundle, String field, Bundle defaultValue) {
@Nullable Bundle result = bundle.getBundle(field);
return result != null ? result : defaultValue;
}
public static ArrayList<Integer> getIntegerArrayListWithDefault(
Bundle bundle, String field, ArrayList<Integer> defaultValue) {
@Nullable ArrayList<Integer> result = bundle.getIntegerArrayList(field);
return result != null ? result : defaultValue;
}
/**
* Sets the application class loader to the given {@link Bundle} if no class loader is present.
*

View File

@ -247,6 +247,25 @@ public class MediaItemTest {
.build());
}
@Test
public void createDrmConfigurationInstance_roundTripViaBundle_yieldsEqualInstance() {
MediaItem.DrmConfiguration drmConfiguration =
new MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
.setLicenseUri(URI_STRING + "/license")
.setLicenseRequestHeaders(ImmutableMap.of("Referer", "http://www.google.com"))
.setMultiSession(true)
.setForceDefaultLicenseUri(true)
.setPlayClearContentWithoutKey(true)
.setForcedSessionTrackTypes(ImmutableList.of(C.TRACK_TYPE_AUDIO))
.setKeySetId(new byte[] {1, 2, 3})
.build();
MediaItem.DrmConfiguration drmConfigurationFromBundle =
MediaItem.DrmConfiguration.CREATOR.fromBundle(drmConfiguration.toBundle());
assertThat(drmConfigurationFromBundle).isEqualTo(drmConfiguration);
}
@Test
public void builderSetCustomCacheKey_setsCustomCacheKey() {
MediaItem mediaItem =

View File

@ -0,0 +1,96 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.common.util;
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit tests for {@link BundleableUtil}. */
@RunWith(AndroidJUnit4.class)
public class BundleableUtilTest {
@Test
public void testStringMapToBundle() {
Map<String, String> originalMap = new HashMap<>();
originalMap.put("firstKey", "firstValue");
originalMap.put("secondKey", "repeatedValue");
originalMap.put("thirdKey", "repeatedValue");
originalMap.put("", "valueOfEmptyKey");
Bundle mapAsBundle = BundleableUtil.stringMapToBundle(originalMap);
Map<String, String> restoredMap = BundleableUtil.bundleToStringHashMap(mapAsBundle);
assertThat(restoredMap).isEqualTo(originalMap);
}
@Test
public void testGetBundleWithDefault() {
Bundle fullInnerBundle = new Bundle();
fullInnerBundle.putString("0", "123");
fullInnerBundle.putBoolean("1", false);
fullInnerBundle.putInt("2", 123);
Bundle defaultBundle = new Bundle();
defaultBundle.putString("0", "I am default");
Bundle outerBundle = new Bundle();
outerBundle.putBundle("0", fullInnerBundle);
outerBundle.putBundle("1", Bundle.EMPTY);
outerBundle.putBundle("2", null);
Bundle restoredInnerBundle =
BundleableUtil.getBundleWithDefault(outerBundle, "0", defaultBundle);
Bundle restoredEmptyBundle =
BundleableUtil.getBundleWithDefault(outerBundle, "1", defaultBundle);
Bundle restoredNullBundle =
BundleableUtil.getBundleWithDefault(outerBundle, "2", defaultBundle);
assertThat(restoredInnerBundle).isEqualTo(fullInnerBundle);
assertThat(restoredEmptyBundle).isEqualTo(Bundle.EMPTY);
assertThat(restoredNullBundle).isEqualTo(defaultBundle);
}
@Test
public void testGetIntegerArrayListWithDefault() {
ArrayList<Integer> normalArray = new ArrayList<>();
normalArray.add(4);
normalArray.add(8);
normalArray.add(16);
ArrayList<Integer> emptyArray = new ArrayList<>();
ArrayList<Integer> defaultIntegerArray = new ArrayList<>();
defaultIntegerArray.add(0);
Bundle bundle = new Bundle();
bundle.putIntegerArrayList("0", normalArray);
bundle.putIntegerArrayList("1", emptyArray);
bundle.putIntegerArrayList("2", null);
ArrayList<Integer> restoredIntegerArray =
BundleableUtil.getIntegerArrayListWithDefault(bundle, "0", defaultIntegerArray);
ArrayList<Integer> restoredEmptyIntegerArray =
BundleableUtil.getIntegerArrayListWithDefault(bundle, "1", defaultIntegerArray);
ArrayList<Integer> restoredNullIntegerArray =
BundleableUtil.getIntegerArrayListWithDefault(bundle, "2", defaultIntegerArray);
assertThat(restoredIntegerArray).isEqualTo(normalArray);
assertThat(restoredEmptyIntegerArray).isEqualTo(emptyArray);
assertThat(restoredNullIntegerArray).isEqualTo(defaultIntegerArray);
}
}