Add getDrmInitData() API to MediaExtractorCompat

Note: `androidx.media3.common.DrmInitData` is returned instead of the platform `android.media.DrmInitData` because the platform class has a package-private constructor, making it impossible to implement the abstract class outside the platform.
PiperOrigin-RevId: 694096542
This commit is contained in:
rohks 2024-11-07 06:31:09 -08:00 committed by Copybara-Service
parent 19d71266ec
commit 91dfaab93f
2 changed files with 108 additions and 9 deletions

View File

@ -15,6 +15,11 @@
*/
package androidx.media3.exoplayer;
import static androidx.media3.common.C.PLAYREADY_UUID;
import static androidx.media3.common.C.WIDEVINE_UUID;
import static androidx.media3.common.MimeTypes.AUDIO_AAC;
import static androidx.media3.common.MimeTypes.VIDEO_H264;
import static androidx.media3.test.utils.TestUtil.buildTestData;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
@ -27,6 +32,7 @@ import android.media.metrics.MediaMetricsManager;
import android.media.metrics.PlaybackSession;
import android.net.Uri;
import androidx.media3.common.C;
import androidx.media3.common.DrmInitData;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.ParserException;
@ -719,6 +725,74 @@ public class MediaExtractorCompatTest {
assertThat(mediaExtractorCompat.getLogSessionId()).isEqualTo(logSessionId);
}
@Test
public void getDrmInitData_withNoTracksHavingDrmInitData_returnsNull() throws IOException {
TrackOutput[] outputs = new TrackOutput[1];
fakeExtractor.addReadAction(
(input, seekPosition) -> {
outputs[0] = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_VIDEO);
outputs[0].format(PLACEHOLDER_FORMAT_VIDEO);
extractorOutput.endTracks();
return Extractor.RESULT_CONTINUE;
});
mediaExtractorCompat.setDataSource(PLACEHOLDER_URI, /* offset= */ 0);
assertThat(mediaExtractorCompat.getDrmInitData()).isNull();
}
@Test
public void getDrmInitData_withSingleTrackHavingDrmInitData_returnsDrmInitData()
throws IOException {
TrackOutput[] outputs = new TrackOutput[1];
DrmInitData.SchemeData schemeData =
new DrmInitData.SchemeData(
WIDEVINE_UUID, VIDEO_H264, buildTestData(128, 1 /* data seed */));
DrmInitData drmInitData = new DrmInitData(schemeData);
fakeExtractor.addReadAction(
(input, seekPosition) -> {
outputs[0] = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_VIDEO);
outputs[0].format(
PLACEHOLDER_FORMAT_VIDEO.buildUpon().setDrmInitData(drmInitData).build());
extractorOutput.endTracks();
return Extractor.RESULT_CONTINUE;
});
mediaExtractorCompat.setDataSource(PLACEHOLDER_URI, /* offset= */ 0);
assertThat(mediaExtractorCompat.getDrmInitData()).isEqualTo(drmInitData);
}
@Test
public void getDrmInitData_withMultipleTracksHavingDrmInitData_returnsFirstNonNullDrmInitData()
throws IOException {
TrackOutput[] outputs = new TrackOutput[3];
DrmInitData.SchemeData firstSchemeData =
new DrmInitData.SchemeData(WIDEVINE_UUID, AUDIO_AAC, buildTestData(128, 1 /* data seed */));
DrmInitData firstDrmInitData = new DrmInitData(firstSchemeData);
DrmInitData.SchemeData secondSchemeData =
new DrmInitData.SchemeData(
PLAYREADY_UUID, AUDIO_AAC, buildTestData(128, 2 /* data seed */));
DrmInitData secondDrmInitData = new DrmInitData(secondSchemeData);
fakeExtractor.addReadAction(
(input, seekPosition) -> {
outputs[0] = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_VIDEO);
outputs[0].format(PLACEHOLDER_FORMAT_VIDEO);
outputs[1] = extractorOutput.track(/* id= */ 1, C.TRACK_TYPE_AUDIO);
outputs[1].format(
PLACEHOLDER_FORMAT_AUDIO.buildUpon().setDrmInitData(firstDrmInitData).build());
outputs[2] = extractorOutput.track(/* id= */ 2, C.TRACK_TYPE_AUDIO);
outputs[2].format(
PLACEHOLDER_FORMAT_AUDIO.buildUpon().setDrmInitData(secondDrmInitData).build());
extractorOutput.endTracks();
return Extractor.RESULT_CONTINUE;
});
mediaExtractorCompat.setDataSource(PLACEHOLDER_URI, /* offset= */ 0);
assertThat(mediaExtractorCompat.getDrmInitData()).isEqualTo(firstDrmInitData);
}
// Internal methods.
private void assertReadSample(int trackIndex, long timeUs, int size, byte... sampleData) {

View File

@ -35,6 +35,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.C;
import androidx.media3.common.DrmInitData;
import androidx.media3.common.Format;
import androidx.media3.common.ParserException;
import androidx.media3.common.util.Assertions;
@ -588,6 +589,24 @@ public final class MediaExtractorCompat {
return logSessionId != null ? logSessionId : LogSessionId.LOG_SESSION_ID_NONE;
}
/**
* Extracts the DRM initialization data from the available tracks, if it exists.
*
* @return The {@link DrmInitData} in the content, or {@code null} if no recognizable DRM format
* is found.
*/
@Nullable
public DrmInitData getDrmInitData() {
for (int i = 0; i < tracks.size(); i++) {
Format format = tracks.get(i).getFormat(formatHolder, noDataBuffer);
if (format.drmInitData == null) {
continue;
}
return format.drmInitData;
}
return null;
}
@VisibleForTesting(otherwise = NONE)
public Allocator getAllocator() {
return allocator;
@ -832,15 +851,8 @@ public final class MediaExtractorCompat {
public MediaFormat createDownstreamMediaFormat(
FormatHolder scratchFormatHolder, DecoderInputBuffer scratchNoDataDecoderInputBuffer) {
scratchFormatHolder.clear();
sampleQueue.read(
scratchFormatHolder,
scratchNoDataDecoderInputBuffer,
FLAG_REQUIRE_FORMAT,
/* loadingFinished= */ false);
Format result = checkNotNull(scratchFormatHolder.format);
MediaFormat mediaFormatResult = MediaFormatUtil.createMediaFormatFromFormat(result);
scratchFormatHolder.clear();
Format format = getFormat(scratchFormatHolder, scratchNoDataDecoderInputBuffer);
MediaFormat mediaFormatResult = MediaFormatUtil.createMediaFormatFromFormat(format);
if (compatibilityTrackMimeType != null) {
if (Util.SDK_INT >= 29) {
mediaFormatResult.removeKey(MediaFormat.KEY_CODECS_STRING);
@ -850,6 +862,19 @@ public final class MediaExtractorCompat {
return mediaFormatResult;
}
private Format getFormat(
FormatHolder scratchFormatHolder, DecoderInputBuffer scratchNoDataDecoderInputBuffer) {
scratchFormatHolder.clear();
sampleQueue.read(
scratchFormatHolder,
scratchNoDataDecoderInputBuffer,
FLAG_REQUIRE_FORMAT,
/* loadingFinished= */ false);
Format format = checkNotNull(scratchFormatHolder.format);
scratchFormatHolder.clear();
return format;
}
public void discardFrontSample() {
sampleQueue.skip(/* count= */ 1);
sampleQueue.discardToRead();