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
33d22a12
authored
Jan 15, 2022
by
Dustin
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Tracks parsing, SeekMap (Index) started
parent
f216fa20
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
791 additions
and
2 deletions
demos/main/src/main/assets/media.exolist.json
library/common/src/main/java/com/google/android/exoplayer2/util/FileTypes.java
library/common/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AudioFormat.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeader.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviUtil.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/Box.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/IAviList.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentBox.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentList.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamFormat.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeader.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/VideoFormat.java
demos/main/src/main/assets/media.exolist.json
View file @
33d22a12
...
@@ -543,6 +543,11 @@
...
@@ -543,6 +543,11 @@
"name"
:
"Misc"
,
"name"
:
"Misc"
,
"samples"
:
[
"samples"
:
[
{
{
"name"
:
"AVI"
,
"uri"
:
"https://drive.google.com/u/0/uc?id=1K6oLKCS56WFbhz33TgilTJBqfMYFTeUd&?export=download"
,
"extension"
:
"avi"
},
{
"name"
:
"Dizzy (MP4)"
,
"name"
:
"Dizzy (MP4)"
,
"uri"
:
"https://html5demos.com/assets/dizzy.mp4"
"uri"
:
"https://html5demos.com/assets/dizzy.mp4"
},
},
...
...
library/common/src/main/java/com/google/android/exoplayer2/util/FileTypes.java
View file @
33d22a12
...
@@ -38,7 +38,7 @@ public final class FileTypes {
...
@@ -38,7 +38,7 @@ public final class FileTypes {
@Documented
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
@IntDef
({
UNKNOWN
,
AC3
,
AC4
,
ADTS
,
AMR
,
FLAC
,
FLV
,
MATROSKA
,
MP3
,
MP4
,
OGG
,
PS
,
TS
,
WAV
,
WEBVTT
,
JPEG
UNKNOWN
,
AC3
,
AC4
,
ADTS
,
AMR
,
FLAC
,
FLV
,
MATROSKA
,
MP3
,
MP4
,
OGG
,
PS
,
TS
,
WAV
,
WEBVTT
,
JPEG
,
AVI
})
})
public
@interface
Type
{}
public
@interface
Type
{}
/** Unknown file type. */
/** Unknown file type. */
...
@@ -73,6 +73,8 @@ public final class FileTypes {
...
@@ -73,6 +73,8 @@ public final class FileTypes {
public
static
final
int
WEBVTT
=
13
;
public
static
final
int
WEBVTT
=
13
;
/** File type for the JPEG format. */
/** File type for the JPEG format. */
public
static
final
int
JPEG
=
14
;
public
static
final
int
JPEG
=
14
;
/** File type for the AVI format. */
public
static
final
int
AVI
=
15
;
@VisibleForTesting
/* package */
static
final
String
HEADER_CONTENT_TYPE
=
"Content-Type"
;
@VisibleForTesting
/* package */
static
final
String
HEADER_CONTENT_TYPE
=
"Content-Type"
;
...
@@ -105,6 +107,7 @@ public final class FileTypes {
...
@@ -105,6 +107,7 @@ public final class FileTypes {
private
static
final
String
EXTENSION_WEBVTT
=
".webvtt"
;
private
static
final
String
EXTENSION_WEBVTT
=
".webvtt"
;
private
static
final
String
EXTENSION_JPG
=
".jpg"
;
private
static
final
String
EXTENSION_JPG
=
".jpg"
;
private
static
final
String
EXTENSION_JPEG
=
".jpeg"
;
private
static
final
String
EXTENSION_JPEG
=
".jpeg"
;
private
static
final
String
EXTENSION_AVI
=
".avi"
;
private
FileTypes
()
{}
private
FileTypes
()
{}
...
@@ -167,6 +170,8 @@ public final class FileTypes {
...
@@ -167,6 +170,8 @@ public final class FileTypes {
return
FileTypes
.
WEBVTT
;
return
FileTypes
.
WEBVTT
;
case
MimeTypes
.
IMAGE_JPEG
:
case
MimeTypes
.
IMAGE_JPEG
:
return
FileTypes
.
JPEG
;
return
FileTypes
.
JPEG
;
case
MimeTypes
.
VIDEO_AVI
:
return
FileTypes
.
AVI
;
default
:
default
:
return
FileTypes
.
UNKNOWN
;
return
FileTypes
.
UNKNOWN
;
}
}
...
@@ -229,6 +234,8 @@ public final class FileTypes {
...
@@ -229,6 +234,8 @@ public final class FileTypes {
return
FileTypes
.
WEBVTT
;
return
FileTypes
.
WEBVTT
;
}
else
if
(
filename
.
endsWith
(
EXTENSION_JPG
)
||
filename
.
endsWith
(
EXTENSION_JPEG
))
{
}
else
if
(
filename
.
endsWith
(
EXTENSION_JPG
)
||
filename
.
endsWith
(
EXTENSION_JPEG
))
{
return
FileTypes
.
JPEG
;
return
FileTypes
.
JPEG
;
}
else
if
(
filename
.
endsWith
(
EXTENSION_AVI
))
{
return
FileTypes
.
AVI
;
}
else
{
}
else
{
return
FileTypes
.
UNKNOWN
;
return
FileTypes
.
UNKNOWN
;
}
}
...
...
library/common/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
View file @
33d22a12
...
@@ -54,6 +54,7 @@ public final class MimeTypes {
...
@@ -54,6 +54,7 @@ public final class MimeTypes {
public
static
final
String
VIDEO_FLV
=
BASE_TYPE_VIDEO
+
"/x-flv"
;
public
static
final
String
VIDEO_FLV
=
BASE_TYPE_VIDEO
+
"/x-flv"
;
public
static
final
String
VIDEO_DOLBY_VISION
=
BASE_TYPE_VIDEO
+
"/dolby-vision"
;
public
static
final
String
VIDEO_DOLBY_VISION
=
BASE_TYPE_VIDEO
+
"/dolby-vision"
;
public
static
final
String
VIDEO_OGG
=
BASE_TYPE_VIDEO
+
"/ogg"
;
public
static
final
String
VIDEO_OGG
=
BASE_TYPE_VIDEO
+
"/ogg"
;
public
static
final
String
VIDEO_AVI
=
BASE_TYPE_VIDEO
+
"/x-msvideo"
;
public
static
final
String
VIDEO_UNKNOWN
=
BASE_TYPE_VIDEO
+
"/x-unknown"
;
public
static
final
String
VIDEO_UNKNOWN
=
BASE_TYPE_VIDEO
+
"/x-unknown"
;
// audio/ MIME types
// audio/ MIME types
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
View file @
33d22a12
...
@@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
...
@@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.extractor.amr.AmrExtractor
;
import
com.google.android.exoplayer2.extractor.amr.AmrExtractor
;
import
com.google.android.exoplayer2.extractor.avi.AviExtractor
;
import
com.google.android.exoplayer2.extractor.flac.FlacExtractor
;
import
com.google.android.exoplayer2.extractor.flac.FlacExtractor
;
import
com.google.android.exoplayer2.extractor.flv.FlvExtractor
;
import
com.google.android.exoplayer2.extractor.flv.FlvExtractor
;
import
com.google.android.exoplayer2.extractor.jpeg.JpegExtractor
;
import
com.google.android.exoplayer2.extractor.jpeg.JpegExtractor
;
...
@@ -99,6 +100,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
...
@@ -99,6 +100,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
FileTypes
.
AC4
,
FileTypes
.
AC4
,
FileTypes
.
MP3
,
FileTypes
.
MP3
,
FileTypes
.
JPEG
,
FileTypes
.
JPEG
,
FileTypes
.
AVI
,
};
};
private
static
final
FlacExtensionLoader
FLAC_EXTENSION_LOADER
=
new
FlacExtensionLoader
();
private
static
final
FlacExtensionLoader
FLAC_EXTENSION_LOADER
=
new
FlacExtensionLoader
();
...
@@ -300,7 +302,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
...
@@ -300,7 +302,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
@Override
@Override
public
synchronized
Extractor
[]
createExtractors
(
public
synchronized
Extractor
[]
createExtractors
(
Uri
uri
,
Map
<
String
,
List
<
String
>>
responseHeaders
)
{
Uri
uri
,
Map
<
String
,
List
<
String
>>
responseHeaders
)
{
List
<
Extractor
>
extractors
=
new
ArrayList
<>(
/* initialCapacity= */
1
4
);
List
<
Extractor
>
extractors
=
new
ArrayList
<>(
/* initialCapacity= */
1
5
);
@FileTypes
.
Type
@FileTypes
.
Type
int
responseHeadersInferredFileType
=
inferFileTypeFromResponseHeaders
(
responseHeaders
);
int
responseHeadersInferredFileType
=
inferFileTypeFromResponseHeaders
(
responseHeaders
);
...
@@ -397,6 +399,9 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
...
@@ -397,6 +399,9 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
case
FileTypes
.
JPEG
:
case
FileTypes
.
JPEG
:
extractors
.
add
(
new
JpegExtractor
());
extractors
.
add
(
new
JpegExtractor
());
break
;
break
;
case
FileTypes
.
AVI
:
extractors
.
add
(
new
AviExtractor
());
break
;
case
FileTypes
.
WEBVTT
:
case
FileTypes
.
WEBVTT
:
case
FileTypes
.
UNKNOWN
:
case
FileTypes
.
UNKNOWN
:
default
:
default
:
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AudioFormat.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
android.util.SparseArray
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.nio.ByteBuffer
;
public
class
AudioFormat
{
public
static
short
WAVE_FORMAT_PCM
=
1
;
public
static
short
WAVE_FORMAT_MPEGLAYER3
=
0x55
;
public
static
short
WAVE_FORMAT_DVM
=
0x2000
;
//AC3
public
static
short
WAVE_FORMAT_DTS2
=
0x2001
;
//DTS
private
static
final
SparseArray
<
String
>
FORMAT_MAP
=
new
SparseArray
<>();
static
{
FORMAT_MAP
.
put
(
WAVE_FORMAT_PCM
,
MimeTypes
.
AUDIO_RAW
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_MPEGLAYER3
,
MimeTypes
.
AUDIO_MPEG
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_DVM
,
MimeTypes
.
AUDIO_AC3
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_DTS2
,
MimeTypes
.
AUDIO_DTS
);
}
private
ByteBuffer
byteBuffer
;
//WAVEFORMATEX
public
AudioFormat
(
ByteBuffer
byteBuffer
)
{
this
.
byteBuffer
=
byteBuffer
;
}
public
String
getCodec
()
{
return
FORMAT_MAP
.
get
(
getFormatTag
()
&
0xffff
);
}
public
short
getFormatTag
()
{
return
byteBuffer
.
getShort
(
0
);
}
public
short
getChannels
()
{
return
byteBuffer
.
getShort
(
2
);
}
public
int
getSamplesPerSecond
()
{
return
byteBuffer
.
getInt
(
4
);
}
// 8 - nAvgBytesPerSec(uint)
// 12 - nBlockAlign(ushort)
public
short
getBitsPerSample
()
{
return
byteBuffer
.
getShort
(
14
);
}
public
short
getCbSize
()
{
return
byteBuffer
.
getShort
(
16
);
}
//TODO: Deal with WAVEFORMATEXTENSIBLE
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.Extractor
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.Log
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.util.Collections
;
import
java.util.List
;
public
class
AviExtractor
implements
Extractor
{
static
final
String
TAG
=
"AviExtractor"
;
private
static
final
int
PEEK_BYTES
=
28
;
private
final
int
STATE_READ_TRACKS
=
0
;
private
final
int
STATE_FIND_MOVI
=
1
;
private
final
int
STATE_FIND_IDX1
=
2
;
private
final
int
STATE_READ_SAMPLES
=
3
;
static
final
int
RIFF
=
AviUtil
.
toInt
(
new
byte
[]{
'R'
,
'I'
,
'F'
,
'F'
});
static
final
int
AVI_
=
AviUtil
.
toInt
(
new
byte
[]{
'A'
,
'V'
,
'I'
,
' '
});
//Stream List
static
final
int
STRL
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'l'
<<
24
);
//Stream CODEC data
static
final
int
STRD
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'd'
<<
24
);
//movie data box
static
final
int
MOVI
=
'm'
|
(
'o'
<<
8
)
|
(
'v'
<<
16
)
|
(
'i'
<<
24
);
//Index
static
final
int
IDX1
=
'i'
|
(
'd'
<<
8
)
|
(
'x'
<<
16
)
|
(
'1'
<<
24
);
private
final
int
flags
;
private
int
state
;
private
ExtractorOutput
output
;
private
AviHeader
aviHeader
;
//After the movi position
private
long
firstChunkPosition
;
public
AviExtractor
()
{
this
(
0
);
}
public
AviExtractor
(
int
flags
)
{
this
.
flags
=
flags
;
}
@Override
public
boolean
sniff
(
ExtractorInput
input
)
throws
IOException
{
return
peakHeaderList
(
input
);
}
static
ByteBuffer
allocate
(
int
bytes
)
{
final
byte
[]
buffer
=
new
byte
[
bytes
];
final
ByteBuffer
byteBuffer
=
ByteBuffer
.
wrap
(
buffer
);
byteBuffer
.
order
(
ByteOrder
.
LITTLE_ENDIAN
);
return
byteBuffer
;
}
boolean
peakHeaderList
(
ExtractorInput
input
)
throws
IOException
{
final
ByteBuffer
byteBuffer
=
allocate
(
PEEK_BYTES
);
input
.
peekFully
(
byteBuffer
.
array
(),
0
,
PEEK_BYTES
);
final
int
riff
=
byteBuffer
.
getInt
();
if
(
riff
!=
AviExtractor
.
RIFF
)
{
return
false
;
}
long
reportedLen
=
AviUtil
.
getUInt
(
byteBuffer
)
+
byteBuffer
.
position
();
final
long
inputLen
=
input
.
getLength
();
if
(
inputLen
!=
C
.
LENGTH_UNSET
&&
inputLen
!=
reportedLen
)
{
Log
.
w
(
TAG
,
"Header length doesn't match stream length"
);
}
int
avi
=
byteBuffer
.
getInt
();
if
(
avi
!=
AviExtractor
.
AVI_
)
{
return
false
;
}
final
int
list
=
byteBuffer
.
getInt
();
if
(
list
!=
IAviList
.
LIST
)
{
return
false
;
}
//Len
byteBuffer
.
getInt
();
final
int
hdrl
=
byteBuffer
.
getInt
();
if
(
hdrl
!=
IAviList
.
TYPE_HDRL
)
{
return
false
;
}
final
int
avih
=
byteBuffer
.
getInt
();
if
(
avih
!=
AviHeader
.
AVIH
)
{
return
false
;
}
return
true
;
}
@Nullable
ResidentList
readHeaderList
(
ExtractorInput
input
)
throws
IOException
{
final
ByteBuffer
byteBuffer
=
allocate
(
20
);
input
.
readFully
(
byteBuffer
.
array
(),
0
,
byteBuffer
.
capacity
());
final
int
riff
=
byteBuffer
.
getInt
();
if
(
riff
!=
AviExtractor
.
RIFF
)
{
return
null
;
}
long
reportedLen
=
AviUtil
.
getUInt
(
byteBuffer
)
+
byteBuffer
.
position
();
final
long
inputLen
=
input
.
getLength
();
if
(
inputLen
!=
C
.
LENGTH_UNSET
&&
inputLen
!=
reportedLen
)
{
Log
.
w
(
TAG
,
"Header length doesn't match stream length"
);
}
int
avi
=
byteBuffer
.
getInt
();
if
(
avi
!=
AviExtractor
.
AVI_
)
{
return
null
;
}
final
ResidentList
header
=
ResidentList
.
getInstance
(
byteBuffer
,
input
,
ResidentList
.
class
);
if
(
header
==
null
)
{
return
null
;
}
if
(
header
.
getListType
()
!=
IAviList
.
TYPE_HDRL
)
{
Log
.
e
(
TAG
,
"Expected "
+
AviUtil
.
toString
(
IAviList
.
TYPE_HDRL
)
+
", got: "
+
AviUtil
.
toString
(
header
.
getType
()));
return
null
;
}
return
header
;
}
long
getDuration
()
{
if
(
aviHeader
==
null
)
{
return
C
.
TIME_UNSET
;
}
return
aviHeader
.
getFrames
()
*
(
long
)
aviHeader
.
getMicroSecPerFrame
();
}
@Override
public
void
init
(
ExtractorOutput
output
)
{
this
.
state
=
STATE_READ_TRACKS
;
this
.
output
=
output
;
}
private
static
ResidentBox
peekNext
(
final
List
<
ResidentBox
>
streams
,
int
i
,
int
type
)
{
if
(
i
+
1
<
streams
.
size
()
&&
streams
.
get
(
i
+
1
).
getType
()
==
type
)
{
return
streams
.
get
(
i
+
1
);
}
return
null
;
}
private
int
readTracks
(
ExtractorInput
input
)
throws
IOException
{
final
ResidentList
headerList
=
readHeaderList
(
input
);
if
(
headerList
==
null
)
{
throw
new
IOException
(
"AVI Header List not found"
);
}
final
List
<
ResidentBox
>
headerChildren
=
headerList
.
getBoxList
();
aviHeader
=
AviUtil
.
getBox
(
headerChildren
,
AviHeader
.
class
);
if
(
aviHeader
==
null
)
{
throw
new
IOException
(
"AviHeader not found"
);
}
headerChildren
.
remove
(
aviHeader
);
//headerChildren should only be Stream Lists now
int
streamId
=
0
;
for
(
Box
box
:
headerChildren
)
{
if
(
box
instanceof
ResidentList
&&
((
ResidentList
)
box
).
getListType
()
==
STRL
)
{
final
ResidentList
streamList
=
(
ResidentList
)
box
;
final
List
<
ResidentBox
>
streamChildren
=
streamList
.
getBoxList
();
for
(
int
i
=
0
;
i
<
streamChildren
.
size
();
i
++)
{
final
ResidentBox
residentBox
=
streamChildren
.
get
(
i
);
if
(
residentBox
instanceof
StreamHeader
)
{
final
StreamHeader
streamHeader
=
(
StreamHeader
)
residentBox
;
final
StreamFormat
streamFormat
=
(
StreamFormat
)
peekNext
(
streamChildren
,
i
,
StreamFormat
.
STRF
);
if
(
streamFormat
!=
null
)
{
i
++;
if
(
streamHeader
.
isVideo
())
{
final
VideoFormat
videoFormat
=
streamFormat
.
getVideoFormat
();
final
ResidentBox
codecBox
=
(
ResidentBox
)
peekNext
(
streamChildren
,
i
,
STRD
);
final
List
<
byte
[]>
codecData
;
if
(
codecBox
!=
null
)
{
codecData
=
Collections
.
singletonList
(
codecBox
.
byteBuffer
.
array
());
i
++;
}
else
{
codecData
=
null
;
}
final
TrackOutput
trackOutput
=
output
.
track
(
streamId
,
C
.
TRACK_TYPE_VIDEO
);
final
Format
.
Builder
builder
=
new
Format
.
Builder
();
builder
.
setWidth
(
videoFormat
.
getWidth
());
builder
.
setHeight
(
videoFormat
.
getHeight
());
builder
.
setFrameRate
(
streamHeader
.
getFrameRate
());
builder
.
setCodecs
(
streamHeader
.
getCodec
());
builder
.
setInitializationData
(
codecData
);
trackOutput
.
format
(
builder
.
build
());
}
else
if
(
streamHeader
.
isAudio
())
{
final
AudioFormat
audioFormat
=
streamFormat
.
getAudioFormat
();
final
TrackOutput
trackOutput
=
output
.
track
(
streamId
,
C
.
TRACK_TYPE_AUDIO
);
final
Format
.
Builder
builder
=
new
Format
.
Builder
();
builder
.
setCodecs
(
audioFormat
.
getCodec
());
builder
.
setChannelCount
(
audioFormat
.
getChannels
());
builder
.
setSampleRate
(
audioFormat
.
getSamplesPerSecond
());
if
(
audioFormat
.
getFormatTag
()
==
AudioFormat
.
WAVE_FORMAT_PCM
)
{
//TODO: Determine if this is LE or BE - Most likely LE
final
short
bps
=
audioFormat
.
getBitsPerSample
();
if
(
bps
==
8
)
{
builder
.
setPcmEncoding
(
C
.
ENCODING_PCM_8BIT
);
}
else
if
(
bps
==
16
){
builder
.
setPcmEncoding
(
C
.
ENCODING_PCM_16BIT
);
}
}
trackOutput
.
format
(
builder
.
build
());
}
}
streamId
++;
}
}
}
}
output
.
endTracks
();
state
=
STATE_FIND_MOVI
;
return
RESULT_CONTINUE
;
}
int
findMovi
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
{
ByteBuffer
byteBuffer
=
allocate
(
12
);
input
.
readFully
(
byteBuffer
.
array
(),
0
,
12
);
final
int
tag
=
byteBuffer
.
getInt
();
final
long
size
=
byteBuffer
.
getInt
()
&
AviUtil
.
UINT_MASK
;
final
long
position
=
input
.
getPosition
();
//-4 because we over read for the LIST type
long
nextBox
=
position
+
size
-
4
;
if
(
tag
==
IAviList
.
LIST
)
{
final
int
listType
=
byteBuffer
.
getInt
();
if
(
listType
==
MOVI
)
{
firstChunkPosition
=
position
;
if
(
aviHeader
.
hasIndex
())
{
state
=
STATE_FIND_IDX1
;
}
else
{
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
getDuration
()));
state
=
STATE_READ_TRACKS
;
nextBox
=
firstChunkPosition
;
}
}
}
seekPosition
.
position
=
nextBox
;
return
RESULT_SEEK
;
}
int
findIdx1
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
{
ByteBuffer
byteBuffer
=
allocate
(
8
);
input
.
readFully
(
byteBuffer
.
array
(),
0
,
8
);
final
int
tag
=
byteBuffer
.
getInt
();
long
remaining
=
byteBuffer
.
getInt
()
&
AviUtil
.
UINT_MASK
;
//TODO: Sanity check on file length
if
(
tag
==
IDX1
)
{
final
ByteBuffer
index
=
allocate
(
4096
);
final
byte
[]
bytes
=
index
.
array
();
index
.
position
(
index
.
capacity
());
while
(
remaining
>
0
)
{
if
(!
index
.
hasRemaining
())
{
index
.
clear
();
final
int
toRead
=
(
int
)
Math
.
min
(
4096
,
remaining
);
if
(!
input
.
readFully
(
bytes
,
0
,
toRead
,
true
))
{
seekPosition
.
position
=
firstChunkPosition
;
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
getDuration
()));
break
;
}
index
.
limit
(
toRead
);
remaining
-=
toRead
;
}
}
//TODO
}
else
{
seekPosition
.
position
=
input
.
getPosition
()
+
remaining
;
}
return
RESULT_SEEK
;
}
@Override
public
int
read
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
{
switch
(
state
)
{
case
STATE_READ_TRACKS:
return
readTracks
(
input
);
case
STATE_FIND_MOVI:
return
findMovi
(
input
,
seekPosition
);
case
STATE_FIND_IDX1:
return
findIdx1
(
input
,
seekPosition
);
}
return
RESULT_CONTINUE
;
}
@Override
public
void
seek
(
long
position
,
long
timeUs
)
{
}
@Override
public
void
release
()
{
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeader.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
java.nio.ByteBuffer
;
public
class
AviHeader
extends
ResidentBox
{
public
static
final
int
AVIF_HASINDEX
=
0x10
;
static
final
int
AVIH
=
'a'
|
(
'v'
<<
8
)
|
(
'i'
<<
16
)
|
(
'h'
<<
24
);
//AVIMAINHEADER
AviHeader
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
}
@Override
boolean
assertType
()
{
return
simpleAssert
(
AVIH
);
}
boolean
hasIndex
()
{
return
(
getFlags
()
&
AVIF_HASINDEX
)
>
0
;
}
int
getMicroSecPerFrame
()
{
return
byteBuffer
.
getInt
(
0
);
}
//4 = dwMaxBytesPerSec
//8 = dwPaddingGranularity
int
getFlags
()
{
return
byteBuffer
.
getInt
(
12
);
}
int
getFrames
()
{
return
byteBuffer
.
getInt
(
16
);
}
//20 = dwInitialFrames
int
getSuggestedBufferSize
()
{
return
byteBuffer
.
getInt
(
24
);
}
int
getWidth
()
{
return
byteBuffer
.
getInt
(
28
);
}
int
getHeight
()
{
return
byteBuffer
.
getInt
(
32
);
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviUtil.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.util.List
;
public
class
AviUtil
{
static
final
long
UINT_MASK
=
0xffffffff
L
;
static
int
toInt
(
byte
[]
bytes
)
{
int
i
=
0
;
for
(
int
b
=
bytes
.
length
-
1
;
b
>=
0
;
b
--)
{
i
<<=
8
;
i
|=
bytes
[
b
];
}
return
i
;
}
static
long
getUInt
(
ByteBuffer
byteBuffer
)
{
return
byteBuffer
.
getInt
()
&
UINT_MASK
;
}
static
void
copy
(
ByteBuffer
source
,
ByteBuffer
dest
,
int
bytes
)
{
final
int
inLimit
=
source
.
limit
();
source
.
limit
(
source
.
position
()
+
bytes
);
dest
.
put
(
source
);
source
.
limit
(
inLimit
);
}
static
ByteBuffer
getByteBuffer
(
final
ByteBuffer
source
,
final
int
size
,
final
ExtractorInput
input
)
throws
IOException
{
final
ByteBuffer
byteBuffer
=
AviExtractor
.
allocate
(
size
);
if
(
size
<
source
.
remaining
())
{
copy
(
source
,
byteBuffer
,
size
);
}
else
{
final
int
copy
=
source
.
remaining
();
copy
(
source
,
byteBuffer
,
copy
);
int
remaining
=
size
-
copy
;
final
int
offset
=
byteBuffer
.
position
()
+
byteBuffer
.
arrayOffset
();
input
.
readFully
(
byteBuffer
.
array
(),
offset
,
remaining
,
false
);
}
return
byteBuffer
;
}
@NonNull
static
String
toString
(
int
tag
)
{
final
StringBuilder
sb
=
new
StringBuilder
(
4
);
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
sb
.
append
((
char
)(
tag
&
0xff
));
tag
>>=
8
;
}
return
sb
.
toString
();
}
@Nullable
static
<
T
extends
Box
>
T
getBox
(
List
<?
extends
Box
>
list
,
Class
<
T
>
clazz
)
{
for
(
Box
box
:
list
)
{
if
(
box
.
getClass
()
==
clazz
)
{
return
(
T
)
box
;
}
}
return
null
;
}
@Nullable
static
IAviList
getSubList
(
List
<?
extends
Box
>
list
,
int
listType
)
{
for
(
Box
box
:
list
)
{
if
(
IAviList
.
class
.
isInstance
(
box
))
{
final
IAviList
aviList
=
(
IAviList
)
box
;
if
(
aviList
.
getListType
()
==
listType
)
{
return
aviList
;
}
}
}
return
null
;
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/Box.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
public
class
Box
{
private
final
long
size
;
private
final
int
type
;
Box
(
int
type
,
long
size
)
{
this
.
type
=
type
;
this
.
size
=
size
;
}
public
long
getSize
()
{
return
size
;
}
public
int
getType
()
{
return
type
;
}
boolean
simpleAssert
(
final
int
expected
)
{
return
getType
()
==
expected
;
}
boolean
assertType
()
{
//Generic box, nothing to assert
return
true
;
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/IAviList.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
public
interface
IAviList
{
int
LIST
=
'L'
|
(
'I'
<<
8
)
|
(
'S'
<<
16
)
|
(
'T'
<<
24
);
//Header List
int
TYPE_HDRL
=
'h'
|
(
'd'
<<
8
)
|
(
'r'
<<
16
)
|
(
'l'
<<
24
);
int
getListType
();
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentBox.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
com.google.android.exoplayer2.util.Log
;
import
java.io.IOException
;
import
java.lang.reflect.Constructor
;
import
java.nio.BufferOverflowException
;
import
java.nio.BufferUnderflowException
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.util.ArrayList
;
import
java.util.List
;
public
class
ResidentBox
extends
Box
{
private
static
final
String
TAG
=
AviExtractor
.
TAG
;
final
private
static
int
MAX_RESIDENT
=
2
*
1024
;
final
ByteBuffer
byteBuffer
;
private
Class
<?
extends
ResidentBox
>
getClass
(
final
int
type
)
{
switch
(
type
)
{
case
AviHeader
.
AVIH
:
return
AviHeader
.
class
;
case
IAviList
.
LIST
:
return
ResidentList
.
class
;
case
StreamHeader
.
STRH
:
return
StreamHeader
.
class
;
case
StreamFormat
.
STRF
:
return
StreamFormat
.
class
;
default
:
return
ResidentBox
.
class
;
}
}
ResidentBox
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
);
this
.
byteBuffer
=
byteBuffer
;
}
/**
* List is not yet populated
* @param byteBuffer
* @return
* @throws IOException
*/
@Nullable
public
static
<
T
extends
ResidentBox
>
T
getInstance
(
final
ByteBuffer
byteBuffer
,
ExtractorInput
input
,
Class
<
T
>
boxClass
)
throws
IOException
{
if
(
byteBuffer
.
remaining
()
<
8
)
{
//Should not happen
throw
new
BufferUnderflowException
();
}
final
int
type
=
byteBuffer
.
getInt
();
final
long
size
=
AviUtil
.
getUInt
(
byteBuffer
);
if
(
size
>
MAX_RESIDENT
)
{
throw
new
BufferOverflowException
();
}
final
ByteBuffer
boxBuffer
=
AviUtil
.
getByteBuffer
(
byteBuffer
,
(
int
)
size
,
input
);
return
newInstance
(
type
,
(
int
)
size
,
boxBuffer
,
boxClass
);
}
@Nullable
private
static
<
T
extends
ResidentBox
>
T
newInstance
(
int
type
,
int
size
,
ByteBuffer
boxBuffer
,
Class
<
T
>
boxClass
)
{
try
{
final
Constructor
<
T
>
constructor
=
boxClass
.
getDeclaredConstructor
(
int
.
class
,
int
.
class
,
ByteBuffer
.
class
);
T
box
=
constructor
.
newInstance
(
type
,
size
,
boxBuffer
);
if
(!
box
.
assertType
())
{
Log
.
e
(
TAG
,
"Expected "
+
AviUtil
.
toString
(
type
)
+
" got "
+
AviUtil
.
toString
(
box
.
getType
()));
return
null
;
}
return
box
;
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"Create box failed "
+
AviUtil
.
toString
(
type
));
return
null
;
}
}
/**
* Returns shallow copy of this ByteBuffer with the position at 0
* @return
*/
@NonNull
public
ByteBuffer
getByteBuffer
()
{
final
ByteBuffer
clone
=
byteBuffer
.
duplicate
();
clone
.
order
(
ByteOrder
.
LITTLE_ENDIAN
);
clone
.
clear
();
return
clone
;
}
@NonNull
public
List
<
ResidentBox
>
getBoxList
()
{
final
ByteBuffer
temp
=
getByteBuffer
();
temp
.
position
(
4
);
final
List
<
ResidentBox
>
list
=
new
ArrayList
<>();
while
(
temp
.
hasRemaining
())
{
final
int
type
=
temp
.
getInt
();
final
int
size
=
temp
.
getInt
();
final
Class
<?
extends
ResidentBox
>
clazz
=
getClass
(
type
);
final
ByteBuffer
boxBuffer
=
AviExtractor
.
allocate
(
size
);
AviUtil
.
copy
(
temp
,
boxBuffer
,
size
);
final
ResidentBox
residentBox
=
newInstance
(
type
,
size
,
boxBuffer
,
clazz
);
if
(
residentBox
==
null
)
{
break
;
}
list
.
add
(
residentBox
);
}
return
list
;
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentList.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
java.nio.ByteBuffer
;
/**
* An AVI LIST box, memory resident
*/
public
class
ResidentList
extends
ResidentBox
implements
IAviList
{
private
final
int
listType
;
ResidentList
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
listType
=
byteBuffer
.
getInt
(
0
);
}
@Override
public
int
getListType
()
{
return
listType
;
}
@Override
boolean
assertType
()
{
return
simpleAssert
(
LIST
);
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamFormat.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.NonNull
;
import
java.nio.ByteBuffer
;
public
class
StreamFormat
extends
ResidentBox
{
public
static
final
int
STRF
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'f'
<<
24
);
StreamFormat
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
}
@NonNull
public
VideoFormat
getVideoFormat
()
{
return
new
VideoFormat
(
byteBuffer
);
}
@NonNull
public
AudioFormat
getAudioFormat
()
{
return
new
AudioFormat
(
byteBuffer
);
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeader.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
android.util.SparseArray
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.nio.ByteBuffer
;
public
class
StreamHeader
extends
ResidentBox
{
public
static
final
int
STRH
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'h'
<<
24
);
//Audio Stream
private
static
final
int
AUDS
=
'a'
|
(
'u'
<<
8
)
|
(
'd'
<<
16
)
|
(
's'
<<
24
);
//Videos Stream
private
static
final
int
VIDS
=
'v'
|
(
'i'
<<
8
)
|
(
'd'
<<
16
)
|
(
's'
<<
24
);
private
static
final
SparseArray
<
String
>
STREAM_MAP
=
new
SparseArray
<>();
static
{
STREAM_MAP
.
put
(
'M'
|
(
'P'
<<
8
)
|
(
'4'
<<
16
)
|
(
'2'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
STREAM_MAP
.
put
(
'3'
|
(
'V'
<<
8
)
|
(
'I'
<<
16
)
|
(
'D'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
STREAM_MAP
.
put
(
'x'
|
(
'v'
<<
8
)
|
(
'i'
<<
16
)
|
(
'd'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
STREAM_MAP
.
put
(
'X'
|
(
'V'
<<
8
)
|
(
'I'
<<
16
)
|
(
'D'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
STREAM_MAP
.
put
(
'm'
|
(
'j'
<<
8
)
|
(
'p'
<<
16
)
|
(
'g'
<<
24
),
MimeTypes
.
IMAGE_JPEG
);
}
StreamHeader
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
}
public
boolean
isAudio
()
{
return
getSteamType
()
==
AUDS
;
}
public
boolean
isVideo
()
{
return
getSteamType
()
==
VIDS
;
}
public
float
getFrameRate
()
{
return
getRate
()
/
(
float
)
getScale
();
}
public
String
getCodec
()
{
return
STREAM_MAP
.
get
(
getFourCC
());
}
public
int
getSteamType
()
{
return
byteBuffer
.
getInt
(
0
);
}
/**
* Only meaningful for video
* @return FourCC
*/
public
int
getFourCC
()
{
return
byteBuffer
.
getInt
(
4
);
}
//8 - dwFlags
//12 - wPriority
//14 - wLanguage
public
int
getInitialFrames
()
{
return
byteBuffer
.
getInt
(
16
);
}
public
int
getScale
()
{
return
byteBuffer
.
getInt
(
20
);
}
public
int
getRate
()
{
return
byteBuffer
.
getInt
(
24
);
}
//28 - dwStart
public
long
getLength
()
{
return
byteBuffer
.
getInt
(
32
)
&
AviUtil
.
UINT_MASK
;
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/VideoFormat.java
0 → 100644
View file @
33d22a12
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
java.nio.ByteBuffer
;
public
class
VideoFormat
{
private
final
ByteBuffer
byteBuffer
;
public
VideoFormat
(
final
ByteBuffer
byteBuffer
)
{
this
.
byteBuffer
=
byteBuffer
;
}
//biSize - (uint)
public
int
getWidth
()
{
return
byteBuffer
.
getInt
(
4
);
}
public
int
getHeight
()
{
return
byteBuffer
.
getInt
(
8
);
}
}
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