Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
88b36abc
authored
Jun 11, 2020
by
kimvde
Committed by
Oliver Woodman
Jun 11, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Sniff all inferred extractors in DefaultHlsExtractorFactory
PiperOrigin-RevId: 315857270
parent
2fcd759e
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
252 additions
and
89 deletions
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactoryTest.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
View file @
88b36abc
...
...
@@ -279,26 +279,26 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
@FileTypes
.
Type
int
responseHeadersInferredFileType
=
inferFileTypeFromResponseHeaders
(
responseHeaders
);
if
(
responseHeadersInferredFileType
!=
FileTypes
.
UNKNOWN
)
{
addExtractorsForF
ormat
(
responseHeadersInferredFileType
,
extractors
);
addExtractorsForF
ileType
(
responseHeadersInferredFileType
,
extractors
);
}
@FileTypes
.
Type
int
uriInferredFileType
=
inferFileTypeFromUri
(
uri
);
if
(
uriInferredFileType
!=
FileTypes
.
UNKNOWN
&&
uriInferredFileType
!=
responseHeadersInferredFileType
)
{
addExtractorsForF
ormat
(
uriInferredFileType
,
extractors
);
addExtractorsForF
ileType
(
uriInferredFileType
,
extractors
);
}
for
(
int
f
ormat
:
DEFAULT_EXTRACTOR_ORDER
)
{
if
(
f
ormat
!=
responseHeadersInferredFileType
&&
format
!=
uriInferredFileType
)
{
addExtractorsForF
ormat
(
format
,
extractors
);
for
(
int
f
ileType
:
DEFAULT_EXTRACTOR_ORDER
)
{
if
(
f
ileType
!=
responseHeadersInferredFileType
&&
fileType
!=
uriInferredFileType
)
{
addExtractorsForF
ileType
(
fileType
,
extractors
);
}
}
return
extractors
.
toArray
(
new
Extractor
[
extractors
.
size
()]);
}
private
void
addExtractorsForF
ormat
(
@FileTypes
.
Type
int
fileFormat
,
List
<
Extractor
>
extractors
)
{
switch
(
file
Format
)
{
private
void
addExtractorsForF
ileType
(
@FileTypes
.
Type
int
fileType
,
List
<
Extractor
>
extractors
)
{
switch
(
file
Type
)
{
case
FileTypes
.
AC3
:
extractors
.
add
(
new
Ac3Extractor
());
break
;
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java
View file @
88b36abc
...
...
@@ -15,7 +15,7 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
FileTypes
.
inferFileTypeFromUri
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
android.net.Uri
;
import
android.text.TextUtils
;
...
...
@@ -31,12 +31,12 @@ import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
import
com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory
;
import
com.google.android.exoplayer2.extractor.ts.TsExtractor
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.FileTypes
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.TimestampAdjuster
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
...
...
@@ -46,6 +46,19 @@ import java.util.Map;
*/
public
final
class
DefaultHlsExtractorFactory
implements
HlsExtractorFactory
{
// Extractors order is optimized according to
// https://docs.google.com/document/d/1w2mKaWMxfz2Ei8-LdxqbPs1VLe_oudB-eryXXw9OvQQ.
private
static
final
int
[]
DEFAULT_EXTRACTOR_ORDER
=
new
int
[]
{
FileTypes
.
MP4
,
FileTypes
.
WEBVTT
,
FileTypes
.
TS
,
FileTypes
.
ADTS
,
FileTypes
.
AC3
,
FileTypes
.
AC4
,
FileTypes
.
MP3
,
};
@DefaultTsPayloadReaderFactory
.
Flags
private
final
int
payloadReaderFactoryFlags
;
private
final
boolean
exposeCea608WhenMissingDeclarations
;
...
...
@@ -90,6 +103,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
if
(
isReusable
(
previousExtractor
))
{
return
buildResult
(
previousExtractor
);
}
else
{
@Nullable
Result
result
=
buildResultForSameExtractorType
(
previousExtractor
,
format
,
timestampAdjuster
);
if
(
result
==
null
)
{
...
...
@@ -99,100 +113,56 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
}
}
// Try selecting the extractor by the file extension.
@Nullable
Extractor
inferredExtractor
=
createInferredExtractor
(
uri
,
format
,
muxedCaptionFormats
,
timestampAdjuster
,
responseHeaders
);
extractorInput
.
resetPeekPosition
();
if
(
inferredExtractor
!=
null
&&
sniffQuietly
(
inferredExtractor
,
extractorInput
))
{
return
buildResult
(
inferredExtractor
);
}
// We need to manually sniff each known type, without retrying the one selected by file
// extension. Extractors order is optimized according to
// https://docs.google.com/document/d/1w2mKaWMxfz2Ei8-LdxqbPs1VLe_oudB-eryXXw9OvQQ.
// Extractor to be used if the type is not recognized.
@Nullable
Extractor
fallBackExtractor
=
inferredExtractor
;
if
(!(
inferredExtractor
instanceof
FragmentedMp4Extractor
))
{
FragmentedMp4Extractor
fragmentedMp4Extractor
=
createFragmentedMp4Extractor
(
timestampAdjuster
,
format
,
muxedCaptionFormats
);
if
(
sniffQuietly
(
fragmentedMp4Extractor
,
extractorInput
))
{
return
buildResult
(
fragmentedMp4Extractor
);
}
}
if
(!(
inferredExtractor
instanceof
WebvttExtractor
))
{
WebvttExtractor
webvttExtractor
=
new
WebvttExtractor
(
format
.
language
,
timestampAdjuster
);
if
(
sniffQuietly
(
webvttExtractor
,
extractorInput
))
{
return
buildResult
(
webvttExtractor
);
}
}
@FileTypes
.
Type
int
formatInferredFileType
=
FileTypes
.
inferFileTypeFromMimeType
(
format
.
sampleMimeType
);
@FileTypes
.
Type
int
responseHeadersInferredFileType
=
FileTypes
.
inferFileTypeFromResponseHeaders
(
responseHeaders
);
@FileTypes
.
Type
int
uriInferredFileType
=
FileTypes
.
inferFileTypeFromUri
(
uri
);
if
(!(
inferredExtractor
instanceof
TsExtractor
))
{
TsExtractor
tsExtractor
=
createTsExtractor
(
payloadReaderFactoryFlags
,
exposeCea608WhenMissingDeclarations
,
format
,
muxedCaptionFormats
,
timestampAdjuster
);
if
(
sniffQuietly
(
tsExtractor
,
extractorInput
))
{
return
buildResult
(
tsExtractor
);
}
if
(
fallBackExtractor
==
null
)
{
fallBackExtractor
=
tsExtractor
;
}
// Defines the order in which to try the extractors.
List
<
Integer
>
fileTypeOrder
=
new
ArrayList
<>(
/* initialCapacity= */
DEFAULT_EXTRACTOR_ORDER
.
length
);
addFileTypeIfNotPresent
(
formatInferredFileType
,
fileTypeOrder
);
addFileTypeIfNotPresent
(
responseHeadersInferredFileType
,
fileTypeOrder
);
addFileTypeIfNotPresent
(
uriInferredFileType
,
fileTypeOrder
);
for
(
int
fileType
:
DEFAULT_EXTRACTOR_ORDER
)
{
addFileTypeIfNotPresent
(
fileType
,
fileTypeOrder
);
}
if
(!(
inferredExtractor
instanceof
AdtsExtractor
))
{
AdtsExtractor
adtsExtractor
=
new
AdtsExtractor
();
if
(
sniffQuietly
(
adtsExtractor
,
extractorInput
))
{
return
buildResult
(
adtsExtractor
);
// Extractor to be used if the type is not recognized.
@Nullable
Extractor
fallBackExtractor
=
null
;
extractorInput
.
resetPeekPosition
();
for
(
int
i
=
0
;
i
<
fileTypeOrder
.
size
();
i
++)
{
int
fileType
=
fileTypeOrder
.
get
(
i
);
Extractor
extractor
=
checkNotNull
(
createExtractorByFileType
(
fileType
,
format
,
muxedCaptionFormats
,
timestampAdjuster
));
if
(
sniffQuietly
(
extractor
,
extractorInput
))
{
return
buildResult
(
extractor
);
}
}
if
(!(
inferredExtractor
instanceof
Ac3Extractor
))
{
Ac3Extractor
ac3Extractor
=
new
Ac3Extractor
();
if
(
sniffQuietly
(
ac3Extractor
,
extractorInput
))
{
return
buildResult
(
ac3Extractor
);
if
(
fileType
==
FileTypes
.
TS
)
{
fallBackExtractor
=
extractor
;
}
}
if
(!(
inferredExtractor
instanceof
Ac4Extractor
))
{
Ac4Extractor
ac4Extractor
=
new
Ac4Extractor
();
if
(
sniffQuietly
(
ac4Extractor
,
extractorInput
))
{
return
buildResult
(
ac4Extractor
);
}
}
return
buildResult
(
checkNotNull
(
fallBackExtractor
));
}
if
(!(
inferredExtractor
instanceof
Mp3Extractor
))
{
Mp3Extractor
mp3Extractor
=
new
Mp3Extractor
(
/* flags= */
0
,
/* forcedFirstSampleTimestampUs= */
0
);
if
(
sniffQuietly
(
mp3Extractor
,
extractorInput
))
{
return
buildResult
(
mp3Extractor
);
}
private
static
void
addFileTypeIfNotPresent
(
@FileTypes
.
Type
int
fileType
,
List
<
Integer
>
fileTypes
)
{
if
(
fileType
==
FileTypes
.
UNKNOWN
||
fileTypes
.
contains
(
fileType
))
{
return
;
}
return
buildResult
(
Assertions
.
checkNotNull
(
fallBackExtractor
));
fileTypes
.
add
(
fileType
);
}
@Nullable
private
Extractor
create
InferredExtractor
(
Uri
uri
,
private
Extractor
create
ExtractorByFileType
(
@FileTypes
.
Type
int
fileType
,
Format
format
,
@Nullable
List
<
Format
>
muxedCaptionFormats
,
TimestampAdjuster
timestampAdjuster
,
Map
<
String
,
List
<
String
>>
responseHeaders
)
{
@FileTypes
.
Type
int
fileType
=
FileTypes
.
inferFileTypeFromMimeType
(
format
.
sampleMimeType
);
if
(
fileType
==
FileTypes
.
UNKNOWN
)
{
fileType
=
FileTypes
.
inferFileTypeFromResponseHeaders
(
responseHeaders
);
}
if
(
fileType
==
FileTypes
.
UNKNOWN
)
{
fileType
=
inferFileTypeFromUri
(
uri
);
}
TimestampAdjuster
timestampAdjuster
)
{
switch
(
fileType
)
{
case
FileTypes
.
WEBVTT
:
return
new
WebvttExtractor
(
format
.
language
,
timestampAdjuster
);
...
...
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactoryTest.java
0 → 100644
View file @
88b36abc
/*
* Copyright 2020 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
*
* http://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
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
android.net.Uri
;
import
androidx.test.core.app.ApplicationProvider
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
;
import
com.google.android.exoplayer2.extractor.Extractor
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
com.google.android.exoplayer2.extractor.mp3.Mp3Extractor
;
import
com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor
;
import
com.google.android.exoplayer2.extractor.ts.Ac3Extractor
;
import
com.google.android.exoplayer2.extractor.ts.TsExtractor
;
import
com.google.android.exoplayer2.testutil.FakeExtractorInput
;
import
com.google.android.exoplayer2.testutil.TestUtil
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.TimestampAdjuster
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
/** Unit test for {@link DefaultExtractorsFactory}. */
@RunWith
(
AndroidJUnit4
.
class
)
public
class
DefaultHlsExtractorFactoryTest
{
private
Extractor
fMp4Extractor
;
private
Uri
tsUri
;
private
Format
webVttFormat
;
private
TimestampAdjuster
timestampAdjuster
;
private
Map
<
String
,
List
<
String
>>
ac3ResponseHeaders
;
@Before
public
void
setUp
()
{
fMp4Extractor
=
new
FragmentedMp4Extractor
();
tsUri
=
Uri
.
parse
(
"http://path/filename.ts"
);
webVttFormat
=
new
Format
.
Builder
().
setSampleMimeType
(
MimeTypes
.
TEXT_VTT
).
build
();
timestampAdjuster
=
new
TimestampAdjuster
(
/* firstSampleTimestampUs= */
0
);
ac3ResponseHeaders
=
new
HashMap
<>();
ac3ResponseHeaders
.
put
(
"Content-Type"
,
Collections
.
singletonList
(
MimeTypes
.
AUDIO_AC3
));
}
@Test
public
void
createExtractor_withPreviousExtractor_returnsSameExtractorType
()
throws
Exception
{
ExtractorInput
extractorInput
=
new
FakeExtractorInput
.
Builder
().
build
();
HlsExtractorFactory
.
Result
result
=
new
DefaultHlsExtractorFactory
()
.
createExtractor
(
/* previousExtractor= */
fMp4Extractor
,
tsUri
,
webVttFormat
,
/* muxedCaptionFormats= */
null
,
timestampAdjuster
,
ac3ResponseHeaders
,
extractorInput
);
assertThat
(
result
.
extractor
.
getClass
()).
isEqualTo
(
FragmentedMp4Extractor
.
class
);
}
@Test
public
void
createExtractor_withFileTypeInFormat_returnsExtractorMatchingFormat
()
throws
Exception
{
ExtractorInput
webVttExtractorInput
=
new
FakeExtractorInput
.
Builder
()
.
setData
(
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
"webvtt/typical"
))
.
build
();
HlsExtractorFactory
.
Result
result
=
new
DefaultHlsExtractorFactory
()
.
createExtractor
(
/* previousExtractor= */
null
,
tsUri
,
webVttFormat
,
/* muxedCaptionFormats= */
null
,
timestampAdjuster
,
ac3ResponseHeaders
,
webVttExtractorInput
);
assertThat
(
result
.
extractor
.
getClass
()).
isEqualTo
(
WebvttExtractor
.
class
);
}
@Test
public
void
createExtractor_withFileTypeInResponseHeaders_returnsExtractorMatchingResponseHeaders
()
throws
Exception
{
ExtractorInput
ac3ExtractorInput
=
new
FakeExtractorInput
.
Builder
()
.
setData
(
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
"ts/sample.ac3"
))
.
build
();
HlsExtractorFactory
.
Result
result
=
new
DefaultHlsExtractorFactory
()
.
createExtractor
(
/* previousExtractor= */
null
,
tsUri
,
webVttFormat
,
/* muxedCaptionFormats= */
null
,
timestampAdjuster
,
ac3ResponseHeaders
,
ac3ExtractorInput
);
assertThat
(
result
.
extractor
.
getClass
()).
isEqualTo
(
Ac3Extractor
.
class
);
}
@Test
public
void
createExtractor_withFileTypeInUri_returnsExtractorMatchingUri
()
throws
Exception
{
ExtractorInput
tsExtractorInput
=
new
FakeExtractorInput
.
Builder
()
.
setData
(
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
"ts/sample_ac3.ts"
))
.
build
();
HlsExtractorFactory
.
Result
result
=
new
DefaultHlsExtractorFactory
()
.
createExtractor
(
/* previousExtractor= */
null
,
tsUri
,
webVttFormat
,
/* muxedCaptionFormats= */
null
,
timestampAdjuster
,
ac3ResponseHeaders
,
tsExtractorInput
);
assertThat
(
result
.
extractor
.
getClass
()).
isEqualTo
(
TsExtractor
.
class
);
}
@Test
public
void
createExtractor_withFileTypeNotInMediaInfo_returnsExpectedExtractor
()
throws
Exception
{
ExtractorInput
mp3ExtractorInput
=
new
FakeExtractorInput
.
Builder
()
.
setData
(
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
"mp3/bear-id3.mp3"
))
.
build
();
HlsExtractorFactory
.
Result
result
=
new
DefaultHlsExtractorFactory
()
.
createExtractor
(
/* previousExtractor= */
null
,
tsUri
,
webVttFormat
,
/* muxedCaptionFormats= */
null
,
timestampAdjuster
,
ac3ResponseHeaders
,
mp3ExtractorInput
);
assertThat
(
result
.
extractor
.
getClass
()).
isEqualTo
(
Mp3Extractor
.
class
);
}
@Test
public
void
createExtractor_withNoMatchingExtractor_fallsBackOnTsExtractor
()
throws
Exception
{
ExtractorInput
emptyExtractorInput
=
new
FakeExtractorInput
.
Builder
().
build
();
HlsExtractorFactory
.
Result
result
=
new
DefaultHlsExtractorFactory
()
.
createExtractor
(
/* previousExtractor= */
null
,
tsUri
,
webVttFormat
,
/* muxedCaptionFormats= */
null
,
timestampAdjuster
,
ac3ResponseHeaders
,
emptyExtractorInput
);
assertThat
(
result
.
extractor
.
getClass
()).
isEqualTo
(
TsExtractor
.
class
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment