Skip to content

Commit

Permalink
HLS: Fix key rotation
Browse files Browse the repository at this point in the history
Passing EXT-X-KEY DrmInitData through the FragmentedMp4Extractor
doesn't work for streams with key rotation, because an extractor
instance is used for multiple segments, but is only passed the
EXT-X-KEY DrmInitData corresponding to the first segment.

This change removes passing DrmInitData through the extractor,
and instead passes it via FormatAdjustingSampleQueue. This is
in-line with how manifest DrmInitData is handled during DASH
playbacks.

Issue: google#6903
PiperOrigin-RevId: 292323429
  • Loading branch information
ojw28 committed Feb 1, 2020
1 parent 850aef4 commit fd88115
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 52 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
([#6552](https://github.com/google/ExoPlayer/issues/6552)).
* Don't use notification chronometer if playback speed is != 1.0
([#6816](https://github.com/google/ExoPlayer/issues/6816)).
* HLS: Fix playback of DRM protected content that uses key rotation
([#6903](https://github.com/google/ExoPlayer/issues/6903)).
* WAV:
* Support IMA ADPCM encoded data.
* Improve support for G.711 A-law and mu-law encoded data.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ public class FragmentedMp4Extractor implements Extractor {

// Sideloaded data.
private final List<Format> closedCaptionFormats;
@Nullable private final DrmInitData sideloadedDrmInitData;

// Track-linked data bundle, accessible as a whole through trackID.
private final SparseArray<TrackBundle> trackBundles;
Expand Down Expand Up @@ -184,60 +183,56 @@ public FragmentedMp4Extractor() {
* @param flags Flags that control the extractor's behavior.
*/
public FragmentedMp4Extractor(@Flags int flags) {
this(flags, null);
this(flags, /* timestampAdjuster= */ null);
}

/**
* @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed.
*/
public FragmentedMp4Extractor(@Flags int flags, @Nullable TimestampAdjuster timestampAdjuster) {
this(flags, timestampAdjuster, null, null);
this(flags, timestampAdjuster, /* sideloadedTrack= */ null, Collections.emptyList());
}

/**
* @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed.
* @param sideloadedTrack Sideloaded track information, in the case that the extractor will not
* receive a moov box in the input data. Null if a moov box is expected.
* @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the
* pssh boxes (if present) will be used.
*/
public FragmentedMp4Extractor(
@Flags int flags,
@Nullable TimestampAdjuster timestampAdjuster,
@Nullable Track sideloadedTrack,
@Nullable DrmInitData sideloadedDrmInitData) {
this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData, Collections.emptyList());
@Nullable Track sideloadedTrack) {
this(flags, timestampAdjuster, sideloadedTrack, Collections.emptyList());
}

/**
* @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed.
* @param sideloadedTrack Sideloaded track information, in the case that the extractor will not
* receive a moov box in the input data. Null if a moov box is expected.
* @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the
* pssh boxes (if present) will be used.
* @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed
* caption channels to expose.
*/
public FragmentedMp4Extractor(
@Flags int flags,
@Nullable TimestampAdjuster timestampAdjuster,
@Nullable Track sideloadedTrack,
@Nullable DrmInitData sideloadedDrmInitData,
List<Format> closedCaptionFormats) {
this(flags, timestampAdjuster, sideloadedTrack, sideloadedDrmInitData,
closedCaptionFormats, null);
this(
flags,
timestampAdjuster,
sideloadedTrack,
closedCaptionFormats,
/* additionalEmsgTrackOutput= */ null);
}

/**
* @param flags Flags that control the extractor's behavior.
* @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed.
* @param sideloadedTrack Sideloaded track information, in the case that the extractor will not
* receive a moov box in the input data. Null if a moov box is expected.
* @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the
* pssh boxes (if present) will be used.
* @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed
* caption channels to expose.
* @param additionalEmsgTrackOutput An extra track output that will receive all emsg messages
Expand All @@ -248,13 +243,11 @@ public FragmentedMp4Extractor(
@Flags int flags,
@Nullable TimestampAdjuster timestampAdjuster,
@Nullable Track sideloadedTrack,
@Nullable DrmInitData sideloadedDrmInitData,
List<Format> closedCaptionFormats,
@Nullable TrackOutput additionalEmsgTrackOutput) {
this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0);
this.timestampAdjuster = timestampAdjuster;
this.sideloadedTrack = sideloadedTrack;
this.sideloadedDrmInitData = sideloadedDrmInitData;
this.closedCaptionFormats = Collections.unmodifiableList(closedCaptionFormats);
this.additionalEmsgTrackOutput = additionalEmsgTrackOutput;
eventMessageEncoder = new EventMessageEncoder();
Expand Down Expand Up @@ -469,8 +462,7 @@ private void onContainerAtomRead(ContainerAtom container) throws ParserException
private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException {
Assertions.checkState(sideloadedTrack == null, "Unexpected moov box.");

DrmInitData drmInitData = sideloadedDrmInitData != null ? sideloadedDrmInitData
: getDrmInitDataFromAtoms(moov.leafChildren);
@Nullable DrmInitData drmInitData = getDrmInitDataFromAtoms(moov.leafChildren);

// Read declaration of track fragments in the Moov box.
ContainerAtom mvex = moov.getContainerAtomOfType(Atom.TYPE_mvex);
Expand Down Expand Up @@ -548,9 +540,8 @@ private DefaultSampleValues getDefaultSampleValues(

private void onMoofContainerAtomRead(ContainerAtom moof) throws ParserException {
parseMoof(moof, trackBundles, flags, scratchBytes);
// If drm init data is sideloaded, we ignore pssh boxes.
DrmInitData drmInitData = sideloadedDrmInitData != null ? null
: getDrmInitDataFromAtoms(moof.leafChildren);

@Nullable DrmInitData drmInitData = getDrmInitDataFromAtoms(moof.leafChildren);
if (drmInitData != null) {
int trackCount = trackBundles.size();
for (int i = 0; i < trackCount; i++) {
Expand Down Expand Up @@ -1408,6 +1399,7 @@ private static TrackBundle getNextFragmentRun(SparseArray<TrackBundle> trackBund
}

/** Returns DrmInitData from leaf atoms. */
@Nullable
private static DrmInitData getDrmInitDataFromAtoms(List<Atom.LeafAtom> leafChildren) {
ArrayList<SchemeData> schemeDatas = null;
int leafChildrenSize = leafChildren.size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ public void testSampleWithSeiPayloadParsing() throws Exception {
ExtractorFactory extractorFactory =
getExtractorFactory(
Collections.singletonList(
Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, 0, null)));
Format.createTextSampleFormat(
null,
MimeTypes.APPLICATION_CEA608,
/* selectionFlags= */ 0,
/* language= */ null)));
ExtractorAsserts.assertBehavior(extractorFactory, "mp4/sample_fragmented_sei.mp4");
}

Expand All @@ -64,6 +68,11 @@ public void testSampleWithProtectedAc4Track() throws Exception {
}

private static ExtractorFactory getExtractorFactory(final List<Format> closedCaptionFormats) {
return () -> new FragmentedMp4Extractor(0, null, null, null, closedCaptionFormats);
return () ->
new FragmentedMp4Extractor(
/* flags= */ 0,
/* timestampAdjuster= */ null,
/* sideloadedTrack= */ null,
closedCaptionFormats);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -812,10 +812,12 @@ private static boolean mimeTypeIsRawText(String mimeType) {
}
extractor =
new FragmentedMp4Extractor(
flags, null, null, null, closedCaptionFormats, playerEmsgTrackOutput);
flags,
/* timestampAdjuster= */ null,
/* sideloadedTrack= */ null,
closedCaptionFormats,
playerEmsgTrackOutput);
}
// Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream,
// as per DASH IF Interoperability Recommendations V3.0, 7.5.3.
return new ChunkExtractorWrapper(extractor, trackType, representation.format);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
Expand Down Expand Up @@ -89,7 +88,6 @@ public Result createExtractor(
Uri uri,
Format format,
@Nullable List<Format> muxedCaptionFormats,
@Nullable DrmInitData drmInitData,
TimestampAdjuster timestampAdjuster,
Map<String, List<String>> responseHeaders,
ExtractorInput extractorInput)
Expand All @@ -111,8 +109,7 @@ public Result createExtractor(

// Try selecting the extractor by the file extension.
Extractor extractorByFileExtension =
createExtractorByFileExtension(
uri, format, muxedCaptionFormats, drmInitData, timestampAdjuster);
createExtractorByFileExtension(uri, format, muxedCaptionFormats, timestampAdjuster);
extractorInput.resetPeekPosition();
if (sniffQuietly(extractorByFileExtension, extractorInput)) {
return buildResult(extractorByFileExtension);
Expand Down Expand Up @@ -159,7 +156,7 @@ public Result createExtractor(

if (!(extractorByFileExtension instanceof FragmentedMp4Extractor)) {
FragmentedMp4Extractor fragmentedMp4Extractor =
createFragmentedMp4Extractor(timestampAdjuster, format, drmInitData, muxedCaptionFormats);
createFragmentedMp4Extractor(timestampAdjuster, format, muxedCaptionFormats);
if (sniffQuietly(fragmentedMp4Extractor, extractorInput)) {
return buildResult(fragmentedMp4Extractor);
}
Expand All @@ -186,7 +183,6 @@ private Extractor createExtractorByFileExtension(
Uri uri,
Format format,
@Nullable List<Format> muxedCaptionFormats,
@Nullable DrmInitData drmInitData,
TimestampAdjuster timestampAdjuster) {
String lastPathSegment = uri.getLastPathSegment();
if (lastPathSegment == null) {
Expand All @@ -209,8 +205,7 @@ private Extractor createExtractorByFileExtension(
|| lastPathSegment.startsWith(M4_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 4)
|| lastPathSegment.startsWith(MP4_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 5)
|| lastPathSegment.startsWith(CMF_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 5)) {
return createFragmentedMp4Extractor(
timestampAdjuster, format, drmInitData, muxedCaptionFormats);
return createFragmentedMp4Extractor(timestampAdjuster, format, muxedCaptionFormats);
} else {
// For any other file extension, we assume TS format.
return createTsExtractor(
Expand Down Expand Up @@ -270,15 +265,13 @@ private static TsExtractor createTsExtractor(
private static FragmentedMp4Extractor createFragmentedMp4Extractor(
TimestampAdjuster timestampAdjuster,
Format format,
@Nullable DrmInitData drmInitData,
@Nullable List<Format> muxedCaptionFormats) {
// Only enable the EMSG TrackOutput if this is the 'variant' track (i.e. the main one) to avoid
// creating a separate EMSG track for every audio track in a video stream.
return new FragmentedMp4Extractor(
/* flags= */ isFmp4Variant(format) ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK : 0,
timestampAdjuster,
/* sideloadedTrack= */ null,
drmInitData,
muxedCaptionFormats != null ? muxedCaptionFormats : Collections.emptyList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder;
Expand Down Expand Up @@ -71,7 +70,6 @@ public Result(Extractor extractor, boolean isPackedAudioExtractor, boolean isReu
* @param format A {@link Format} associated with the chunk to extract.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist.
* @param drmInitData {@link DrmInitData} associated with the chunk.
* @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number.
* @param responseHeaders The HTTP response headers associated with the media segment or
* initialization section to extract.
Expand All @@ -87,7 +85,6 @@ Result createExtractor(
Uri uri,
Format format,
@Nullable List<Format> muxedCaptionFormats,
@Nullable DrmInitData drmInitData,
TimestampAdjuster timestampAdjuster,
Map<String, List<String>> responseHeaders,
ExtractorInput sniffingExtractorInput)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ private void feedDataToExtractor(
private DefaultExtractorInput prepareExtraction(DataSource dataSource, DataSpec dataSpec)
throws IOException, InterruptedException {
long bytesToRead = dataSource.open(dataSpec);

DefaultExtractorInput extractorInput =
new DefaultExtractorInput(dataSource, dataSpec.absoluteStreamPosition, bytesToRead);

Expand All @@ -402,7 +401,6 @@ private DefaultExtractorInput prepareExtraction(DataSource dataSource, DataSpec
dataSpec.uri,
trackFormat,
muxedCaptionFormats,
drmInitData,
timestampAdjuster,
dataSource.getResponseHeaders(),
extractorInput);
Expand All @@ -421,7 +419,7 @@ private DefaultExtractorInput prepareExtraction(DataSource dataSource, DataSpec
output.onNewExtractor();
extractor.init(output);
}

output.setDrmInitData(drmInitData);
return extractorInput;
}

Expand Down
Loading

0 comments on commit fd88115

Please sign in to comment.