Avoid invalid extractors in DefaultHlsExtractorFactory

This change fixes playback of playlists where segments have the
extension and Content-Type of JPEG pictures (although in reality)
they are transport streams. File inferrence before this change will
cause an exception when assuming the inferred file type is one of
the allowed HLS containers.

#minor-release
Issue: #8733
PiperOrigin-RevId: 363641277
This commit is contained in:
aquilescanta 2021-03-18 13:08:11 +00:00 committed by Ian Baker
parent c30a6e5119
commit 8337991be3
3 changed files with 35 additions and 7 deletions

View File

@ -39,6 +39,9 @@
`EXT-X-DISCONTINUITY` tags in different media playlists occur at `EXT-X-DISCONTINUITY` tags in different media playlists occur at
different positions in time different positions in time
([#8372](https://github.com/google/ExoPlayer/issues/8372)). ([#8372](https://github.com/google/ExoPlayer/issues/8372)).
* Fix container type detection for segments with incorrect file extension
or HTTP Content-Type
([#8733](https://github.com/google/ExoPlayer/issues/8733)).
* Remove deprecated symbols: * Remove deprecated symbols:
* Remove `Player.DefaultEventListener`. Use `Player.EventListener` * Remove `Player.DefaultEventListener`. Use `Player.EventListener`
instead. instead.

View File

@ -35,6 +35,7 @@ import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.FileTypes; import com.google.android.exoplayer2.util.FileTypes;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.common.primitives.Ints;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -107,11 +108,11 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
// Defines the order in which to try the extractors. // Defines the order in which to try the extractors.
List<Integer> fileTypeOrder = List<Integer> fileTypeOrder =
new ArrayList<>(/* initialCapacity= */ DEFAULT_EXTRACTOR_ORDER.length); new ArrayList<>(/* initialCapacity= */ DEFAULT_EXTRACTOR_ORDER.length);
addFileTypeIfNotPresent(formatInferredFileType, fileTypeOrder); addFileTypeIfValidAndNotPresent(formatInferredFileType, fileTypeOrder);
addFileTypeIfNotPresent(responseHeadersInferredFileType, fileTypeOrder); addFileTypeIfValidAndNotPresent(responseHeadersInferredFileType, fileTypeOrder);
addFileTypeIfNotPresent(uriInferredFileType, fileTypeOrder); addFileTypeIfValidAndNotPresent(uriInferredFileType, fileTypeOrder);
for (int fileType : DEFAULT_EXTRACTOR_ORDER) { for (int fileType : DEFAULT_EXTRACTOR_ORDER) {
addFileTypeIfNotPresent(fileType, fileTypeOrder); addFileTypeIfValidAndNotPresent(fileType, fileTypeOrder);
} }
// Extractor to be used if the type is not recognized. // Extractor to be used if the type is not recognized.
@ -140,9 +141,9 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
checkNotNull(fallBackExtractor), format, timestampAdjuster); checkNotNull(fallBackExtractor), format, timestampAdjuster);
} }
private static void addFileTypeIfNotPresent( private static void addFileTypeIfValidAndNotPresent(
@FileTypes.Type int fileType, List<Integer> fileTypes) { @FileTypes.Type int fileType, List<Integer> fileTypes) {
if (fileType == FileTypes.UNKNOWN || fileTypes.contains(fileType)) { if (Ints.indexOf(DEFAULT_EXTRACTOR_ORDER, fileType) == -1 || fileTypes.contains(fileType)) {
return; return;
} }
fileTypes.add(fileType); fileTypes.add(fileType);

View File

@ -31,6 +31,7 @@ import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -44,8 +45,9 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class DefaultHlsExtractorFactoryTest { public class DefaultHlsExtractorFactoryTest {
private static final Uri URI_WITH_TS_EXTENSION = Uri.parse("http://path/filename.ts"); private static final Uri URI_WITH_JPEG_EXTENSION = Uri.parse("http://path/filename.jpg");
private static final Uri URI_WITH_MP4_EXTENSION = Uri.parse("http://path/filename.mp4"); private static final Uri URI_WITH_MP4_EXTENSION = Uri.parse("http://path/filename.mp4");
private static final Uri URI_WITH_TS_EXTENSION = Uri.parse("http://path/filename.ts");
private Format webVttFormat; private Format webVttFormat;
private TimestampAdjuster timestampAdjuster; private TimestampAdjuster timestampAdjuster;
@ -151,6 +153,28 @@ public class DefaultHlsExtractorFactoryTest {
assertThat(result.extractor.getClass()).isEqualTo(Mp3Extractor.class); assertThat(result.extractor.getClass()).isEqualTo(Mp3Extractor.class);
} }
@Test
public void createExtractor_withInvalidFileTypeInUri_returnsSniffedType() throws Exception {
ExtractorInput tsExtractorInput =
new FakeExtractorInput.Builder()
.setData(
TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), "media/ts/sample_ac3.ts"))
.build();
BundledHlsMediaChunkExtractor result =
new DefaultHlsExtractorFactory()
.createExtractor(
URI_WITH_JPEG_EXTENSION,
webVttFormat,
/* muxedCaptionFormats= */ null,
timestampAdjuster,
ImmutableMap.of("Content-Type", ImmutableList.of(MimeTypes.IMAGE_JPEG)),
tsExtractorInput);
assertThat(result.extractor.getClass()).isEqualTo(TsExtractor.class);
}
@Test @Test
public void createExtractor_onFailedSniff_fallsBackOnFormatInferred() throws Exception { public void createExtractor_onFailedSniff_fallsBackOnFormatInferred() throws Exception {
ExtractorInput emptyExtractorInput = new FakeExtractorInput.Builder().build(); ExtractorInput emptyExtractorInput = new FakeExtractorInput.Builder().build();