• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.drm;
17 
18 import android.os.Parcel;
19 import android.os.Parcelable;
20 import android.text.TextUtils;
21 import androidx.annotation.Nullable;
22 import com.google.android.exoplayer2.C;
23 import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
24 import com.google.android.exoplayer2.util.Assertions;
25 import com.google.android.exoplayer2.util.Util;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Comparator;
29 import java.util.List;
30 import java.util.UUID;
31 
32 /**
33  * Initialization data for one or more DRM schemes.
34  */
35 public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
36 
37   /**
38    * Merges {@link DrmInitData} obtained from a media manifest and a media stream.
39    *
40    * <p>The result is generated as follows.
41    *
42    * <ol>
43    *   <li>Include all {@link SchemeData}s from {@code manifestData} where {@link
44    *       SchemeData#hasData()} is true.
45    *   <li>Include all {@link SchemeData}s in {@code mediaData} where {@link SchemeData#hasData()}
46    *       is true and for which we did not include an entry from the manifest targeting the same
47    *       UUID.
48    *   <li>If available, the scheme type from the manifest is used. If not, the scheme type from the
49    *       media is used.
50    * </ol>
51    *
52    * @param manifestData DRM session acquisition data obtained from the manifest.
53    * @param mediaData DRM session acquisition data obtained from the media.
54    * @return A {@link DrmInitData} obtained from merging a media manifest and a media stream.
55    */
createSessionCreationData( @ullable DrmInitData manifestData, @Nullable DrmInitData mediaData)56   public static @Nullable DrmInitData createSessionCreationData(
57       @Nullable DrmInitData manifestData, @Nullable DrmInitData mediaData) {
58     ArrayList<SchemeData> result = new ArrayList<>();
59     String schemeType = null;
60     if (manifestData != null) {
61       schemeType = manifestData.schemeType;
62       for (SchemeData data : manifestData.schemeDatas) {
63         if (data.hasData()) {
64           result.add(data);
65         }
66       }
67     }
68 
69     if (mediaData != null) {
70       if (schemeType == null) {
71         schemeType = mediaData.schemeType;
72       }
73       int manifestDatasCount = result.size();
74       for (SchemeData data : mediaData.schemeDatas) {
75         if (data.hasData() && !containsSchemeDataWithUuid(result, manifestDatasCount, data.uuid)) {
76           result.add(data);
77         }
78       }
79     }
80 
81     return result.isEmpty() ? null : new DrmInitData(schemeType, result);
82   }
83 
84   private final SchemeData[] schemeDatas;
85 
86   // Lazily initialized hashcode.
87   private int hashCode;
88 
89   /** The protection scheme type, or null if not applicable or unknown. */
90   @Nullable public final String schemeType;
91 
92   /**
93    * Number of {@link SchemeData}s.
94    */
95   public final int schemeDataCount;
96 
97   /**
98    * @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
99    */
DrmInitData(List<SchemeData> schemeDatas)100   public DrmInitData(List<SchemeData> schemeDatas) {
101     this(null, false, schemeDatas.toArray(new SchemeData[0]));
102   }
103 
104   /**
105    * @param schemeType See {@link #schemeType}.
106    * @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
107    */
DrmInitData(@ullable String schemeType, List<SchemeData> schemeDatas)108   public DrmInitData(@Nullable String schemeType, List<SchemeData> schemeDatas) {
109     this(schemeType, false, schemeDatas.toArray(new SchemeData[0]));
110   }
111 
112   /**
113    * @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
114    */
DrmInitData(SchemeData... schemeDatas)115   public DrmInitData(SchemeData... schemeDatas) {
116     this(null, schemeDatas);
117   }
118 
119   /**
120    * @param schemeType See {@link #schemeType}.
121    * @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
122    */
DrmInitData(@ullable String schemeType, SchemeData... schemeDatas)123   public DrmInitData(@Nullable String schemeType, SchemeData... schemeDatas) {
124     this(schemeType, true, schemeDatas);
125   }
126 
DrmInitData(@ullable String schemeType, boolean cloneSchemeDatas, SchemeData... schemeDatas)127   private DrmInitData(@Nullable String schemeType, boolean cloneSchemeDatas,
128       SchemeData... schemeDatas) {
129     this.schemeType = schemeType;
130     if (cloneSchemeDatas) {
131       schemeDatas = schemeDatas.clone();
132     }
133     this.schemeDatas = schemeDatas;
134     schemeDataCount = schemeDatas.length;
135     // Sorting ensures that universal scheme data (i.e. data that applies to all schemes) is matched
136     // last. It's also required by the equals and hashcode implementations.
137     Arrays.sort(this.schemeDatas, this);
138   }
139 
140   /* package */
DrmInitData(Parcel in)141   DrmInitData(Parcel in) {
142     schemeType = in.readString();
143     schemeDatas = Util.castNonNull(in.createTypedArray(SchemeData.CREATOR));
144     schemeDataCount = schemeDatas.length;
145   }
146 
147   /**
148    * Retrieves data for a given DRM scheme, specified by its UUID.
149    *
150    * @deprecated Use {@link #get(int)} and {@link SchemeData#matches(UUID)} instead.
151    * @param uuid The DRM scheme's UUID.
152    * @return The initialization data for the scheme, or null if the scheme is not supported.
153    */
154   @Deprecated
155   @Nullable
get(UUID uuid)156   public SchemeData get(UUID uuid) {
157     for (SchemeData schemeData : schemeDatas) {
158       if (schemeData.matches(uuid)) {
159         return schemeData;
160       }
161     }
162     return null;
163   }
164 
165   /**
166    * Retrieves the {@link SchemeData} at a given index.
167    *
168    * @param index The index of the scheme to return. Must not exceed {@link #schemeDataCount}.
169    * @return The {@link SchemeData} at the specified index.
170    */
get(int index)171   public SchemeData get(int index) {
172     return schemeDatas[index];
173   }
174 
175   /**
176    * Returns a copy with the specified protection scheme type.
177    *
178    * @param schemeType A protection scheme type. May be null.
179    * @return A copy with the specified protection scheme type.
180    */
copyWithSchemeType(@ullable String schemeType)181   public DrmInitData copyWithSchemeType(@Nullable String schemeType) {
182     if (Util.areEqual(this.schemeType, schemeType)) {
183       return this;
184     }
185     return new DrmInitData(schemeType, false, schemeDatas);
186   }
187 
188   /**
189    * Returns an instance containing the {@link #schemeDatas} from both this and {@code other}. The
190    * {@link #schemeType} of the instances being merged must either match, or at least one scheme
191    * type must be {@code null}.
192    *
193    * @param drmInitData The instance to merge.
194    * @return The merged result.
195    */
merge(DrmInitData drmInitData)196   public DrmInitData merge(DrmInitData drmInitData) {
197     Assertions.checkState(
198         schemeType == null
199             || drmInitData.schemeType == null
200             || TextUtils.equals(schemeType, drmInitData.schemeType));
201     String mergedSchemeType = schemeType != null ? this.schemeType : drmInitData.schemeType;
202     SchemeData[] mergedSchemeDatas =
203         Util.nullSafeArrayConcatenation(schemeDatas, drmInitData.schemeDatas);
204     return new DrmInitData(mergedSchemeType, mergedSchemeDatas);
205   }
206 
207   @Override
hashCode()208   public int hashCode() {
209     if (hashCode == 0) {
210       int result = (schemeType == null ? 0 : schemeType.hashCode());
211       result = 31 * result + Arrays.hashCode(schemeDatas);
212       hashCode = result;
213     }
214     return hashCode;
215   }
216 
217   @Override
equals(@ullable Object obj)218   public boolean equals(@Nullable Object obj) {
219     if (this == obj) {
220       return true;
221     }
222     if (obj == null || getClass() != obj.getClass()) {
223       return false;
224     }
225     DrmInitData other = (DrmInitData) obj;
226     return Util.areEqual(schemeType, other.schemeType)
227         && Arrays.equals(schemeDatas, other.schemeDatas);
228   }
229 
230   @Override
compare(SchemeData first, SchemeData second)231   public int compare(SchemeData first, SchemeData second) {
232     return C.UUID_NIL.equals(first.uuid) ? (C.UUID_NIL.equals(second.uuid) ? 0 : 1)
233         : first.uuid.compareTo(second.uuid);
234   }
235 
236   // Parcelable implementation.
237 
238   @Override
describeContents()239   public int describeContents() {
240     return 0;
241   }
242 
243   @Override
writeToParcel(Parcel dest, int flags)244   public void writeToParcel(Parcel dest, int flags) {
245     dest.writeString(schemeType);
246     dest.writeTypedArray(schemeDatas, 0);
247   }
248 
249   public static final Parcelable.Creator<DrmInitData> CREATOR =
250       new Parcelable.Creator<DrmInitData>() {
251 
252     @Override
253     public DrmInitData createFromParcel(Parcel in) {
254       return new DrmInitData(in);
255     }
256 
257     @Override
258     public DrmInitData[] newArray(int size) {
259       return new DrmInitData[size];
260     }
261 
262   };
263 
264   // Internal methods.
265 
containsSchemeDataWithUuid( ArrayList<SchemeData> datas, int limit, UUID uuid)266   private static boolean containsSchemeDataWithUuid(
267       ArrayList<SchemeData> datas, int limit, UUID uuid) {
268     for (int i = 0; i < limit; i++) {
269       if (datas.get(i).uuid.equals(uuid)) {
270         return true;
271       }
272     }
273     return false;
274   }
275 
276   /**
277    * Scheme initialization data.
278    */
279   public static final class SchemeData implements Parcelable {
280 
281     // Lazily initialized hashcode.
282     private int hashCode;
283 
284     /**
285      * The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is universal (i.e.
286      * applies to all schemes).
287      */
288     public final UUID uuid;
289     /** The URL of the server to which license requests should be made. May be null if unknown. */
290     @Nullable public final String licenseServerUrl;
291     /** The mimeType of {@link #data}. */
292     public final String mimeType;
293     /** The initialization data. May be null for scheme support checks only. */
294     @Nullable public final byte[] data;
295 
296     /**
297      * @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is
298      *     universal (i.e. applies to all schemes).
299      * @param mimeType See {@link #mimeType}.
300      * @param data See {@link #data}.
301      */
SchemeData(UUID uuid, String mimeType, @Nullable byte[] data)302     public SchemeData(UUID uuid, String mimeType, @Nullable byte[] data) {
303       this(uuid, /* licenseServerUrl= */ null, mimeType, data);
304     }
305 
306     /**
307      * @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is
308      *     universal (i.e. applies to all schemes).
309      * @param licenseServerUrl See {@link #licenseServerUrl}.
310      * @param mimeType See {@link #mimeType}.
311      * @param data See {@link #data}.
312      */
SchemeData( UUID uuid, @Nullable String licenseServerUrl, String mimeType, @Nullable byte[] data)313     public SchemeData(
314         UUID uuid, @Nullable String licenseServerUrl, String mimeType, @Nullable byte[] data) {
315       this.uuid = Assertions.checkNotNull(uuid);
316       this.licenseServerUrl = licenseServerUrl;
317       this.mimeType = Assertions.checkNotNull(mimeType);
318       this.data = data;
319     }
320 
SchemeData(Parcel in)321     /* package */ SchemeData(Parcel in) {
322       uuid = new UUID(in.readLong(), in.readLong());
323       licenseServerUrl = in.readString();
324       mimeType = Util.castNonNull(in.readString());
325       data = in.createByteArray();
326     }
327 
328     /**
329      * Returns whether this initialization data applies to the specified scheme.
330      *
331      * @param schemeUuid The scheme {@link UUID}.
332      * @return Whether this initialization data applies to the specified scheme.
333      */
matches(UUID schemeUuid)334     public boolean matches(UUID schemeUuid) {
335       return C.UUID_NIL.equals(uuid) || schemeUuid.equals(uuid);
336     }
337 
338     /**
339      * Returns whether this {@link SchemeData} can be used to replace {@code other}.
340      *
341      * @param other A {@link SchemeData}.
342      * @return Whether this {@link SchemeData} can be used to replace {@code other}.
343      */
canReplace(SchemeData other)344     public boolean canReplace(SchemeData other) {
345       return hasData() && !other.hasData() && matches(other.uuid);
346     }
347 
348     /**
349      * Returns whether {@link #data} is non-null.
350      */
hasData()351     public boolean hasData() {
352       return data != null;
353     }
354 
355     /**
356      * Returns a copy of this instance with the specified data.
357      *
358      * @param data The data to include in the copy.
359      * @return The new instance.
360      */
copyWithData(@ullable byte[] data)361     public SchemeData copyWithData(@Nullable byte[] data) {
362       return new SchemeData(uuid, licenseServerUrl, mimeType, data);
363     }
364 
365     @Override
equals(@ullable Object obj)366     public boolean equals(@Nullable Object obj) {
367       if (!(obj instanceof SchemeData)) {
368         return false;
369       }
370       if (obj == this) {
371         return true;
372       }
373       SchemeData other = (SchemeData) obj;
374       return Util.areEqual(licenseServerUrl, other.licenseServerUrl)
375           && Util.areEqual(mimeType, other.mimeType)
376           && Util.areEqual(uuid, other.uuid)
377           && Arrays.equals(data, other.data);
378     }
379 
380     @Override
hashCode()381     public int hashCode() {
382       if (hashCode == 0) {
383         int result = uuid.hashCode();
384         result = 31 * result + (licenseServerUrl == null ? 0 : licenseServerUrl.hashCode());
385         result = 31 * result + mimeType.hashCode();
386         result = 31 * result + Arrays.hashCode(data);
387         hashCode = result;
388       }
389       return hashCode;
390     }
391 
392     // Parcelable implementation.
393 
394     @Override
describeContents()395     public int describeContents() {
396       return 0;
397     }
398 
399     @Override
writeToParcel(Parcel dest, int flags)400     public void writeToParcel(Parcel dest, int flags) {
401       dest.writeLong(uuid.getMostSignificantBits());
402       dest.writeLong(uuid.getLeastSignificantBits());
403       dest.writeString(licenseServerUrl);
404       dest.writeString(mimeType);
405       dest.writeByteArray(data);
406     }
407 
408     public static final Parcelable.Creator<SchemeData> CREATOR =
409         new Parcelable.Creator<SchemeData>() {
410 
411       @Override
412       public SchemeData createFromParcel(Parcel in) {
413         return new SchemeData(in);
414       }
415 
416       @Override
417       public SchemeData[] newArray(int size) {
418         return new SchemeData[size];
419       }
420 
421     };
422 
423   }
424 
425 }
426