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
a9c94185
authored
Jan 17, 2022
by
Dustin
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Working!
parent
33d22a12
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
571 additions
and
124 deletions
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/AviHeaderBox.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviSeekMap.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviTrack.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/BoxFactory.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/ResidentList.java → library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ListBox.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/StreamFormat.java → library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamFormatBox.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/StreamHeaderBox.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/UnboundedIntArray.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AudioFormat.java
View file @
a9c94185
...
@@ -5,14 +5,16 @@ import com.google.android.exoplayer2.util.MimeTypes;
...
@@ -5,14 +5,16 @@ import com.google.android.exoplayer2.util.MimeTypes;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
public
class
AudioFormat
{
public
class
AudioFormat
{
public
static
short
WAVE_FORMAT_PCM
=
1
;
public
static
final
short
WAVE_FORMAT_PCM
=
1
;
public
static
short
WAVE_FORMAT_MPEGLAYER3
=
0x55
;
private
static
final
short
WAVE_FORMAT_MPEGLAYER3
=
0x55
;
public
static
short
WAVE_FORMAT_DVM
=
0x2000
;
//AC3
private
static
final
short
WAVE_FORMAT_AAC
=
0xff
;
public
static
short
WAVE_FORMAT_DTS2
=
0x2001
;
//DTS
private
static
final
short
WAVE_FORMAT_DVM
=
0x2000
;
//AC3
private
static
final
short
WAVE_FORMAT_DTS2
=
0x2001
;
//DTS
private
static
final
SparseArray
<
String
>
FORMAT_MAP
=
new
SparseArray
<>();
private
static
final
SparseArray
<
String
>
FORMAT_MAP
=
new
SparseArray
<>();
static
{
static
{
FORMAT_MAP
.
put
(
WAVE_FORMAT_PCM
,
MimeTypes
.
AUDIO_RAW
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_PCM
,
MimeTypes
.
AUDIO_RAW
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_MPEGLAYER3
,
MimeTypes
.
AUDIO_MPEG
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_MPEGLAYER3
,
MimeTypes
.
AUDIO_MPEG
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_AAC
,
MimeTypes
.
AUDIO_AAC
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_DVM
,
MimeTypes
.
AUDIO_AC3
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_DVM
,
MimeTypes
.
AUDIO_AC3
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_DTS2
,
MimeTypes
.
AUDIO_DTS
);
FORMAT_MAP
.
put
(
WAVE_FORMAT_DTS2
,
MimeTypes
.
AUDIO_DTS
);
}
}
...
@@ -24,7 +26,7 @@ public class AudioFormat {
...
@@ -24,7 +26,7 @@ public class AudioFormat {
this
.
byteBuffer
=
byteBuffer
;
this
.
byteBuffer
=
byteBuffer
;
}
}
public
String
get
Codec
()
{
public
String
get
MimeType
()
{
return
FORMAT_MAP
.
get
(
getFormatTag
()
&
0xffff
);
return
FORMAT_MAP
.
get
(
getFormatTag
()
&
0xffff
);
}
}
...
@@ -38,12 +40,24 @@ public class AudioFormat {
...
@@ -38,12 +40,24 @@ public class AudioFormat {
return
byteBuffer
.
getInt
(
4
);
return
byteBuffer
.
getInt
(
4
);
}
}
// 8 - nAvgBytesPerSec(uint)
// 8 - nAvgBytesPerSec(uint)
// 12 - nBlockAlign(ushort)
public
int
getBlockAlign
()
{
return
byteBuffer
.
getShort
(
12
);
}
public
short
getBitsPerSample
()
{
public
short
getBitsPerSample
()
{
return
byteBuffer
.
getShort
(
14
);
return
byteBuffer
.
getShort
(
14
);
}
}
public
short
getCbSize
()
{
public
int
getCbSize
()
{
return
byteBuffer
.
getShort
(
16
);
return
byteBuffer
.
getShort
(
16
)
&
0xffff
;
}
public
byte
[]
getCodecData
()
{
final
int
size
=
getCbSize
();
final
ByteBuffer
temp
=
byteBuffer
.
duplicate
();
temp
.
clear
();
temp
.
position
(
18
);
temp
.
limit
(
18
+
size
);
final
byte
[]
data
=
new
byte
[
size
];
temp
.
get
(
data
);
return
data
;
}
}
//TODO: Deal with WAVEFORMATEXTENSIBLE
//TODO: Deal with WAVEFORMATEXTENSIBLE
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java
View file @
a9c94185
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
android.util.SparseArray
;
import
android.util.SparseIntArray
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
...
@@ -10,20 +13,34 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
...
@@ -10,20 +13,34 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.nio.ByteOrder
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.TreeMap
;
/**
* Based on the official MicroSoft spec
* https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
*/
public
class
AviExtractor
implements
Extractor
{
public
class
AviExtractor
implements
Extractor
{
static
final
String
TAG
=
"AviExtractor"
;
static
final
String
TAG
=
"AviExtractor"
;
static
final
int
KEY_FRAME_MASK
=
Integer
.
MIN_VALUE
;
private
static
final
int
PEEK_BYTES
=
28
;
private
static
final
int
PEEK_BYTES
=
28
;
private
final
int
STATE_READ_TRACKS
=
0
;
private
static
final
int
STATE_READ_TRACKS
=
0
;
private
final
int
STATE_FIND_MOVI
=
1
;
private
static
final
int
STATE_FIND_MOVI
=
1
;
private
final
int
STATE_FIND_IDX1
=
2
;
private
static
final
int
STATE_READ_IDX1
=
2
;
private
final
int
STATE_READ_SAMPLES
=
3
;
private
static
final
int
STATE_READ_SAMPLES
=
3
;
private
static
final
int
STATE_SEEK_START
=
4
;
private
static
final
int
AVIIF_KEYFRAME
=
16
;
static
final
int
RIFF
=
AviUtil
.
toInt
(
new
byte
[]{
'R'
,
'I'
,
'F'
,
'F'
});
static
final
int
RIFF
=
AviUtil
.
toInt
(
new
byte
[]{
'R'
,
'I'
,
'F'
,
'F'
});
static
final
int
AVI_
=
AviUtil
.
toInt
(
new
byte
[]{
'A'
,
'V'
,
'I'
,
' '
});
static
final
int
AVI_
=
AviUtil
.
toInt
(
new
byte
[]{
'A'
,
'V'
,
'I'
,
' '
});
...
@@ -36,13 +53,26 @@ public class AviExtractor implements Extractor {
...
@@ -36,13 +53,26 @@ public class AviExtractor implements Extractor {
//Index
//Index
static
final
int
IDX1
=
'i'
|
(
'd'
<<
8
)
|
(
'x'
<<
16
)
|
(
'1'
<<
24
);
static
final
int
IDX1
=
'i'
|
(
'd'
<<
8
)
|
(
'x'
<<
16
)
|
(
'1'
<<
24
);
private
final
int
flags
;
static
final
int
JUNK
=
'J'
|
(
'U'
<<
8
)
|
(
'N'
<<
16
)
|
(
'K'
<<
24
);
static
final
long
SEEK_GAP
=
2_000_000L
;
//Time between seek points in micro seconds
private
int
state
;
private
int
state
;
private
ExtractorOutput
output
;
private
ExtractorOutput
output
;
private
AviHeader
aviHeader
;
private
AviHeaderBox
aviHeader
;
private
SparseArray
<
AviTrack
>
idTrackMap
=
new
SparseArray
<>();
//After the movi position
//After the movi position
private
long
firstChunkPosition
;
private
long
moviOffset
;
private
long
moviEnd
;
private
AviSeekMap
aviSeekMap
;
private
int
flags
;
// private long indexOffset; //Usually chunkStart
//If partial read
private
transient
AviTrack
sampleTrack
;
private
transient
int
sampleRemaining
;
private
transient
int
sampleSize
;
public
AviExtractor
()
{
public
AviExtractor
()
{
this
(
0
);
this
(
0
);
...
@@ -63,6 +93,12 @@ public class AviExtractor implements Extractor {
...
@@ -63,6 +93,12 @@ public class AviExtractor implements Extractor {
byteBuffer
.
order
(
ByteOrder
.
LITTLE_ENDIAN
);
byteBuffer
.
order
(
ByteOrder
.
LITTLE_ENDIAN
);
return
byteBuffer
;
return
byteBuffer
;
}
}
private
void
setSeekMap
(
AviSeekMap
aviSeekMap
)
{
this
.
aviSeekMap
=
aviSeekMap
;
output
.
seekMap
(
aviSeekMap
);
}
boolean
peakHeaderList
(
ExtractorInput
input
)
throws
IOException
{
boolean
peakHeaderList
(
ExtractorInput
input
)
throws
IOException
{
final
ByteBuffer
byteBuffer
=
allocate
(
PEEK_BYTES
);
final
ByteBuffer
byteBuffer
=
allocate
(
PEEK_BYTES
);
input
.
peekFully
(
byteBuffer
.
array
(),
0
,
PEEK_BYTES
);
input
.
peekFully
(
byteBuffer
.
array
(),
0
,
PEEK_BYTES
);
...
@@ -80,23 +116,23 @@ public class AviExtractor implements Extractor {
...
@@ -80,23 +116,23 @@ public class AviExtractor implements Extractor {
return
false
;
return
false
;
}
}
final
int
list
=
byteBuffer
.
getInt
();
final
int
list
=
byteBuffer
.
getInt
();
if
(
list
!=
IAviList
.
LIST
)
{
if
(
list
!=
ListBox
.
LIST
)
{
return
false
;
return
false
;
}
}
//Len
//Len
byteBuffer
.
getInt
();
byteBuffer
.
getInt
();
final
int
hdrl
=
byteBuffer
.
getInt
();
final
int
hdrl
=
byteBuffer
.
getInt
();
if
(
hdrl
!=
IAviList
.
TYPE_HDRL
)
{
if
(
hdrl
!=
ListBox
.
TYPE_HDRL
)
{
return
false
;
return
false
;
}
}
final
int
avih
=
byteBuffer
.
getInt
();
final
int
avih
=
byteBuffer
.
getInt
();
if
(
avih
!=
AviHeader
.
AVIH
)
{
if
(
avih
!=
AviHeader
Box
.
AVIH
)
{
return
false
;
return
false
;
}
}
return
true
;
return
true
;
}
}
@Nullable
@Nullable
ResidentList
readHeaderList
(
ExtractorInput
input
)
throws
IOException
{
ListBox
readHeaderList
(
ExtractorInput
input
)
throws
IOException
{
final
ByteBuffer
byteBuffer
=
allocate
(
20
);
final
ByteBuffer
byteBuffer
=
allocate
(
20
);
input
.
readFully
(
byteBuffer
.
array
(),
0
,
byteBuffer
.
capacity
());
input
.
readFully
(
byteBuffer
.
array
(),
0
,
byteBuffer
.
capacity
());
final
int
riff
=
byteBuffer
.
getInt
();
final
int
riff
=
byteBuffer
.
getInt
();
...
@@ -112,12 +148,12 @@ public class AviExtractor implements Extractor {
...
@@ -112,12 +148,12 @@ public class AviExtractor implements Extractor {
if
(
avi
!=
AviExtractor
.
AVI_
)
{
if
(
avi
!=
AviExtractor
.
AVI_
)
{
return
null
;
return
null
;
}
}
final
ResidentList
header
=
ResidentList
.
getInstance
(
byteBuffer
,
input
,
ResidentList
.
class
);
final
ListBox
header
=
ListBox
.
getInstance
(
byteBuffer
,
input
,
ListBox
.
class
);
if
(
header
==
null
)
{
if
(
header
==
null
)
{
return
null
;
return
null
;
}
}
if
(
header
.
getListType
()
!=
IAviList
.
TYPE_HDRL
)
{
if
(
header
.
getListType
()
!=
ListBox
.
TYPE_HDRL
)
{
Log
.
e
(
TAG
,
"Expected "
+
AviUtil
.
toString
(
IAviList
.
TYPE_HDRL
)
+
", got: "
+
Log
.
e
(
TAG
,
"Expected "
+
AviUtil
.
toString
(
ListBox
.
TYPE_HDRL
)
+
", got: "
+
AviUtil
.
toString
(
header
.
getType
()));
AviUtil
.
toString
(
header
.
getType
()));
return
null
;
return
null
;
}
}
...
@@ -145,12 +181,13 @@ public class AviExtractor implements Extractor {
...
@@ -145,12 +181,13 @@ public class AviExtractor implements Extractor {
}
}
private
int
readTracks
(
ExtractorInput
input
)
throws
IOException
{
private
int
readTracks
(
ExtractorInput
input
)
throws
IOException
{
final
ResidentList
headerList
=
readHeaderList
(
input
);
final
ListBox
headerList
=
readHeaderList
(
input
);
if
(
headerList
==
null
)
{
if
(
headerList
==
null
)
{
throw
new
IOException
(
"AVI Header List not found"
);
throw
new
IOException
(
"AVI Header List not found"
);
}
}
final
List
<
ResidentBox
>
headerChildren
=
headerList
.
getBoxList
();
final
BoxFactory
boxFactory
=
new
BoxFactory
();
aviHeader
=
AviUtil
.
getBox
(
headerChildren
,
AviHeader
.
class
);
final
List
<
ResidentBox
>
headerChildren
=
headerList
.
getBoxList
(
boxFactory
);
aviHeader
=
AviUtil
.
getBox
(
headerChildren
,
AviHeaderBox
.
class
);
if
(
aviHeader
==
null
)
{
if
(
aviHeader
==
null
)
{
throw
new
IOException
(
"AviHeader not found"
);
throw
new
IOException
(
"AviHeader not found"
);
}
}
...
@@ -159,14 +196,14 @@ public class AviExtractor implements Extractor {
...
@@ -159,14 +196,14 @@ public class AviExtractor implements Extractor {
int
streamId
=
0
;
int
streamId
=
0
;
for
(
Box
box
:
headerChildren
)
{
for
(
Box
box
:
headerChildren
)
{
if
(
box
instanceof
ResidentList
&&
((
ResidentList
)
box
).
getListType
()
==
STRL
)
{
if
(
box
instanceof
ListBox
&&
((
ListBox
)
box
).
getListType
()
==
STRL
)
{
final
ResidentList
streamList
=
(
ResidentList
)
box
;
final
ListBox
streamList
=
(
ListBox
)
box
;
final
List
<
ResidentBox
>
streamChildren
=
streamList
.
getBoxList
();
final
List
<
ResidentBox
>
streamChildren
=
streamList
.
getBoxList
(
boxFactory
);
for
(
int
i
=
0
;
i
<
streamChildren
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
streamChildren
.
size
();
i
++)
{
final
ResidentBox
residentBox
=
streamChildren
.
get
(
i
);
final
ResidentBox
residentBox
=
streamChildren
.
get
(
i
);
if
(
residentBox
instanceof
StreamHeader
)
{
if
(
residentBox
instanceof
StreamHeader
Box
)
{
final
StreamHeader
streamHeader
=
(
StreamHeader
)
residentBox
;
final
StreamHeader
Box
streamHeader
=
(
StreamHeaderBox
)
residentBox
;
final
StreamFormat
streamFormat
=
(
StreamFormat
)
peekNext
(
streamChildren
,
i
,
StreamFormat
.
STRF
);
final
StreamFormat
Box
streamFormat
=
(
StreamFormatBox
)
peekNext
(
streamChildren
,
i
,
StreamFormatBox
.
STRF
);
if
(
streamFormat
!=
null
)
{
if
(
streamFormat
!=
null
)
{
i
++;
i
++;
if
(
streamHeader
.
isVideo
())
{
if
(
streamHeader
.
isVideo
())
{
...
@@ -184,14 +221,26 @@ public class AviExtractor implements Extractor {
...
@@ -184,14 +221,26 @@ public class AviExtractor implements Extractor {
builder
.
setWidth
(
videoFormat
.
getWidth
());
builder
.
setWidth
(
videoFormat
.
getWidth
());
builder
.
setHeight
(
videoFormat
.
getHeight
());
builder
.
setHeight
(
videoFormat
.
getHeight
());
builder
.
setFrameRate
(
streamHeader
.
getFrameRate
());
builder
.
setFrameRate
(
streamHeader
.
getFrameRate
());
builder
.
setCodecs
(
streamHeader
.
getCodec
());
final
String
mimeType
=
streamHeader
.
getMimeType
();
builder
.
setInitializationData
(
codecData
);
builder
.
setSampleMimeType
(
mimeType
);
if
(
MimeTypes
.
VIDEO_H263
.
equals
(
mimeType
))
{
builder
.
setSelectionFlags
(
C
.
SELECTION_FLAG_FORCED
);
}
//builder.setCodecs(streamHeader.getCodec());
if
(
codecData
!=
null
)
{
builder
.
setInitializationData
(
codecData
);
}
trackOutput
.
format
(
builder
.
build
());
trackOutput
.
format
(
builder
.
build
());
idTrackMap
.
put
(
'0'
|
((
'0'
+
streamId
)
<<
8
)
|
(
'd'
<<
16
)
|
(
'c'
<<
24
),
new
AviTrack
(
streamId
,
trackOutput
,
streamHeader
));
}
else
if
(
streamHeader
.
isAudio
())
{
}
else
if
(
streamHeader
.
isAudio
())
{
final
AudioFormat
audioFormat
=
streamFormat
.
getAudioFormat
();
final
AudioFormat
audioFormat
=
streamFormat
.
getAudioFormat
();
final
TrackOutput
trackOutput
=
output
.
track
(
streamId
,
C
.
TRACK_TYPE_AUDIO
);
final
TrackOutput
trackOutput
=
output
.
track
(
streamId
,
C
.
TRACK_TYPE_AUDIO
);
final
Format
.
Builder
builder
=
new
Format
.
Builder
();
final
Format
.
Builder
builder
=
new
Format
.
Builder
();
builder
.
setCodecs
(
audioFormat
.
getCodec
());
final
String
mimeType
=
audioFormat
.
getMimeType
();
builder
.
setSampleMimeType
(
mimeType
);
//builder.setCodecs(audioFormat.getCodec());
builder
.
setChannelCount
(
audioFormat
.
getChannels
());
builder
.
setChannelCount
(
audioFormat
.
getChannels
());
builder
.
setSampleRate
(
audioFormat
.
getSamplesPerSecond
());
builder
.
setSampleRate
(
audioFormat
.
getSamplesPerSecond
());
if
(
audioFormat
.
getFormatTag
()
==
AudioFormat
.
WAVE_FORMAT_PCM
)
{
if
(
audioFormat
.
getFormatTag
()
==
AudioFormat
.
WAVE_FORMAT_PCM
)
{
...
@@ -203,7 +252,12 @@ public class AviExtractor implements Extractor {
...
@@ -203,7 +252,12 @@ public class AviExtractor implements Extractor {
builder
.
setPcmEncoding
(
C
.
ENCODING_PCM_16BIT
);
builder
.
setPcmEncoding
(
C
.
ENCODING_PCM_16BIT
);
}
}
}
}
if
(
MimeTypes
.
AUDIO_AAC
.
equals
(
mimeType
)
&&
audioFormat
.
getCbSize
()
>
0
)
{
builder
.
setInitializationData
(
Collections
.
singletonList
(
audioFormat
.
getCodecData
()));
}
trackOutput
.
format
(
builder
.
build
());
trackOutput
.
format
(
builder
.
build
());
idTrackMap
.
put
(
'0'
|
((
'0'
+
streamId
)
<<
8
)
|
(
'w'
<<
16
)
|
(
'b'
<<
24
),
new
AviTrack
(
streamId
,
trackOutput
,
streamHeader
));
}
}
}
}
streamId
++;
streamId
++;
...
@@ -224,16 +278,17 @@ public class AviExtractor implements Extractor {
...
@@ -224,16 +278,17 @@ public class AviExtractor implements Extractor {
final
long
position
=
input
.
getPosition
();
final
long
position
=
input
.
getPosition
();
//-4 because we over read for the LIST type
//-4 because we over read for the LIST type
long
nextBox
=
position
+
size
-
4
;
long
nextBox
=
position
+
size
-
4
;
if
(
tag
==
IAviList
.
LIST
)
{
if
(
tag
==
ListBox
.
LIST
)
{
final
int
listType
=
byteBuffer
.
getInt
();
final
int
listType
=
byteBuffer
.
getInt
();
if
(
listType
==
MOVI
)
{
if
(
listType
==
MOVI
)
{
firstChunkPosition
=
position
;
moviOffset
=
position
-
4
;
moviEnd
=
moviOffset
+
size
;
if
(
aviHeader
.
hasIndex
())
{
if
(
aviHeader
.
hasIndex
())
{
state
=
STATE_
FIN
D_IDX1
;
state
=
STATE_
REA
D_IDX1
;
}
else
{
}
else
{
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
getDuration
()));
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
getDuration
()));
state
=
STATE_READ_TRACKS
;
state
=
STATE_READ_TRACKS
;
nextBox
=
firstChunkPosition
;
nextBox
=
moviOffset
+
4
;
}
}
}
}
}
}
...
@@ -241,54 +296,194 @@ public class AviExtractor implements Extractor {
...
@@ -241,54 +296,194 @@ public class AviExtractor implements Extractor {
return
RESULT_SEEK
;
return
RESULT_SEEK
;
}
}
int
findIdx1
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
{
/**
ByteBuffer
byteBuffer
=
allocate
(
8
);
* Reads the index and sets the keyFrames and creates the SeekMap
input
.
readFully
(
byteBuffer
.
array
(),
0
,
8
);
* @param input
final
int
tag
=
byteBuffer
.
getInt
();
* @param remaining
long
remaining
=
byteBuffer
.
getInt
()
&
AviUtil
.
UINT_MASK
;
* @throws IOException
//TODO: Sanity check on file length
*/
if
(
tag
==
IDX1
)
{
void
readIdx1
(
ExtractorInput
input
,
int
remaining
)
throws
IOException
{
final
ByteBuffer
index
=
allocate
(
4096
);
final
ByteBuffer
indexByteBuffer
=
allocate
(
Math
.
min
(
remaining
,
64
*
1024
));
final
byte
[]
bytes
=
index
.
array
();
final
byte
[]
bytes
=
indexByteBuffer
.
array
();
index
.
position
(
index
.
capacity
());
while
(
remaining
>
0
)
{
final
HashMap
<
Integer
,
UnboundedIntArray
>
audioIdFrameMap
=
new
HashMap
<>();
if
(!
index
.
hasRemaining
())
{
AviTrack
videoTrack
=
null
;
index
.
clear
();
//Video seek offsets
final
int
toRead
=
(
int
)
Math
.
min
(
4096
,
remaining
);
UnboundedIntArray
videoSeekOffset
=
new
UnboundedIntArray
();
if
(!
input
.
readFully
(
bytes
,
0
,
toRead
,
true
))
{
for
(
int
i
=
0
;
i
<
idTrackMap
.
size
();
i
++)
{
seekPosition
.
position
=
firstChunkPosition
;
final
AviTrack
aviTrack
=
idTrackMap
.
valueAt
(
i
);
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
getDuration
()));
if
(
videoTrack
==
null
&&
aviTrack
.
isVideo
())
{
break
;
videoTrack
=
aviTrack
;
}
else
{
audioIdFrameMap
.
put
(
idTrackMap
.
keyAt
(
i
),
new
UnboundedIntArray
());
}
}
if
(
videoTrack
==
null
)
{
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
getDuration
()));
Log
.
w
(
TAG
,
"No video track found"
);
return
;
}
resetFrames
();
final
int
seekFrameRate
=
(
int
)(
videoTrack
.
streamHeaderBox
.
getFrameRate
()
*
2
);
final
UnboundedIntArray
keyFrameList
=
new
UnboundedIntArray
();
while
(
remaining
>
0
)
{
final
int
toRead
=
Math
.
min
(
indexByteBuffer
.
remaining
(),
remaining
);
input
.
readFully
(
bytes
,
indexByteBuffer
.
position
(),
toRead
);
remaining
-=
toRead
;
while
(
indexByteBuffer
.
remaining
()
>=
16
)
{
final
int
id
=
indexByteBuffer
.
getInt
();
final
AviTrack
aviTrack
=
idTrackMap
.
get
(
id
);
if
(
aviTrack
==
null
)
{
Log
.
w
(
TAG
,
"Unknown Track Type: "
+
AviUtil
.
toString
(
id
));
indexByteBuffer
.
position
(
indexByteBuffer
.
position
()
+
12
);
continue
;
}
final
int
flags
=
indexByteBuffer
.
getInt
();
final
int
offset
=
indexByteBuffer
.
getInt
();
indexByteBuffer
.
position
(
indexByteBuffer
.
position
()
+
4
);
//int size = indexByteBuffer.getInt();
if
(
aviTrack
.
isVideo
())
{
if
((
flags
&
AVIIF_KEYFRAME
)
==
AVIIF_KEYFRAME
)
{
keyFrameList
.
add
(
aviTrack
.
frame
);
}
if
(
aviTrack
.
frame
%
seekFrameRate
==
0
)
{
videoSeekOffset
.
add
(
offset
);
for
(
Map
.
Entry
<
Integer
,
UnboundedIntArray
>
entry
:
audioIdFrameMap
.
entrySet
())
{
final
int
audioId
=
entry
.
getKey
();
final
UnboundedIntArray
videoFrameMap
=
entry
.
getValue
();
final
AviTrack
audioTrack
=
idTrackMap
.
get
(
audioId
);
videoFrameMap
.
add
(
audioTrack
.
frame
);
}
}
}
index
.
limit
(
toRead
);
remaining
-=
toRead
;
}
}
aviTrack
.
advance
();
}
indexByteBuffer
.
compact
();
}
videoSeekOffset
.
pack
();
keyFrameList
.
pack
();
final
int
[]
keyFrames
=
keyFrameList
.
array
;
videoTrack
.
setKeyFrames
(
keyFrames
);
final
SparseArray
<
int
[]>
idFrameArray
=
new
SparseArray
<>();
for
(
Map
.
Entry
<
Integer
,
UnboundedIntArray
>
entry
:
audioIdFrameMap
.
entrySet
())
{
entry
.
getValue
().
pack
();
idFrameArray
.
put
(
entry
.
getKey
(),
entry
.
getValue
().
array
);
final
AviTrack
aviTrack
=
idTrackMap
.
get
(
entry
.
getKey
());
//Sometimes this value is way off
long
calcUsPerSample
=
(
getDuration
()/
aviTrack
.
frame
);
float
deltaPercent
=
Math
.
abs
(
calcUsPerSample
-
aviTrack
.
usPerSample
)
/
(
float
)
aviTrack
.
usPerSample
;
if
(
deltaPercent
>.
01
)
{
aviTrack
.
usPerSample
=
getDuration
()/
aviTrack
.
frame
;
Log
.
d
(
TAG
,
"Frames act="
+
getDuration
()
+
" calc="
+
(
aviTrack
.
usPerSample
*
aviTrack
.
frame
));
}
}
//TODO
}
final
AviSeekMap
seekMap
=
new
AviSeekMap
(
videoTrack
,
seekFrameRate
,
videoSeekOffset
.
array
,
idFrameArray
,
moviOffset
,
getDuration
());
setSeekMap
(
seekMap
);
resetFrames
();
}
int
readSamples
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
{
if
(
sampleRemaining
!=
0
)
{
sampleRemaining
-=
sampleTrack
.
trackOutput
.
sampleData
(
input
,
sampleRemaining
,
false
);
}
else
{
}
else
{
seekPosition
.
position
=
input
.
getPosition
()
+
remaining
;
ByteBuffer
byteBuffer
=
allocate
(
8
);
final
byte
[]
bytes
=
byteBuffer
.
array
();
// This isn't documented anywhere, but most files are aligned to even bytes
// and can have gaps of zeros
if
((
input
.
getPosition
()
&
1
)
==
1
)
{
input
.
skipFully
(
1
);
}
input
.
readFully
(
bytes
,
0
,
1
);
while
(
bytes
[
0
]
==
0
)
{
input
.
readFully
(
bytes
,
0
,
1
);
}
if
(
input
.
getPosition
()
>=
moviEnd
)
{
return
RESULT_END_OF_INPUT
;
}
input
.
readFully
(
bytes
,
1
,
7
);
int
id
=
byteBuffer
.
getInt
();
sampleSize
=
byteBuffer
.
getInt
();
sampleTrack
=
idTrackMap
.
get
(
id
);
if
(
sampleTrack
==
null
)
{
if
(
id
!=
JUNK
)
{
Log
.
w
(
TAG
,
"Unknown tag="
+
AviUtil
.
toString
(
id
)
+
" pos="
+
(
input
.
getPosition
()
-
8
)
+
" size="
+
sampleSize
+
" moviEnd="
+
moviEnd
);
}
seekPosition
.
position
=
input
.
getPosition
()
+
sampleSize
;
return
RESULT_SEEK
;
}
else
{
//Log.d(TAG, "Sample pos=" + (input.getPosition() - 8) + " size=" + sampleSize + " video=" + sampleTrack.isVideo());
sampleRemaining
=
sampleSize
-
sampleTrack
.
trackOutput
.
sampleData
(
input
,
sampleSize
,
false
);
}
}
}
return
RESULT_SEEK
;
if
(
sampleRemaining
!=
0
)
{
return
RESULT_CONTINUE
;
}
sampleTrack
.
trackOutput
.
sampleMetadata
(
sampleTrack
.
getUs
(),
sampleTrack
.
isKeyFrame
()
?
C
.
BUFFER_FLAG_KEY_FRAME
:
0
,
sampleSize
,
0
,
null
);
//Log.d(TAG, "Frame: " + (sampleTrack.isVideo()? 'V' : 'A') + " us=" + sampleTrack.getUs());
sampleTrack
.
advance
();
return
RESULT_CONTINUE
;
}
}
@Override
@Override
public
int
read
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
{
public
int
read
(
@NonNull
ExtractorInput
input
,
@NonNull
PositionHolder
seekPosition
)
throws
IOException
{
switch
(
state
)
{
switch
(
state
)
{
case
STATE_READ_SAMPLES:
return
readSamples
(
input
,
seekPosition
);
case
STATE_SEEK_START:
state
=
STATE_READ_SAMPLES
;
seekPosition
.
position
=
moviOffset
+
4
;
return
RESULT_SEEK
;
case
STATE_READ_TRACKS:
case
STATE_READ_TRACKS:
return
readTracks
(
input
);
return
readTracks
(
input
);
case
STATE_FIND_MOVI:
case
STATE_FIND_MOVI:
return
findMovi
(
input
,
seekPosition
);
return
findMovi
(
input
,
seekPosition
);
case
STATE_FIND_IDX1:
case
STATE_READ_IDX1:
{
return
findIdx1
(
input
,
seekPosition
);
if
(
aviHeader
.
hasIndex
())
{
ByteBuffer
byteBuffer
=
allocate
(
8
);
input
.
readFully
(
byteBuffer
.
array
(),
0
,
8
);
final
int
tag
=
byteBuffer
.
getInt
();
final
int
size
=
byteBuffer
.
getInt
();
if
(
tag
==
IDX1
)
{
readIdx1
(
input
,
size
);
}
}
else
{
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
getDuration
()));
}
seekPosition
.
position
=
moviOffset
+
4
;
state
=
STATE_READ_SAMPLES
;
return
RESULT_SEEK
;
}
}
}
return
RESULT_CONTINUE
;
return
RESULT_CONTINUE
;
}
}
@Override
@Override
public
void
seek
(
long
position
,
long
timeUs
)
{
public
void
seek
(
long
position
,
long
timeUs
)
{
Log
.
d
(
"Test"
,
"Seek: pos="
+
position
+
" us="
+
timeUs
);
if
(
position
==
0
)
{
if
(
moviOffset
!=
0
)
{
resetFrames
();
state
=
STATE_SEEK_START
;
}
}
else
{
if
(
aviSeekMap
!=
null
)
{
aviSeekMap
.
setFrames
(
position
,
timeUs
,
idTrackMap
);
}
}
}
void
resetFrames
()
{
for
(
int
i
=
0
;
i
<
idTrackMap
.
size
();
i
++)
{
final
AviTrack
aviTrack
=
idTrackMap
.
valueAt
(
i
);
aviTrack
.
frame
=
0
;
}
}
}
@Override
@Override
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeader.java
→
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeader
Box
.java
View file @
a9c94185
...
@@ -2,13 +2,13 @@ package com.google.android.exoplayer2.extractor.avi;
...
@@ -2,13 +2,13 @@ package com.google.android.exoplayer2.extractor.avi;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
public
class
AviHeader
extends
ResidentBox
{
public
class
AviHeader
Box
extends
ResidentBox
{
public
static
final
int
AVIF_HASINDEX
=
0x10
;
public
static
final
int
AVIF_HASINDEX
=
0x10
;
static
final
int
AVIH
=
'a'
|
(
'v'
<<
8
)
|
(
'i'
<<
16
)
|
(
'h'
<<
24
);
static
final
int
AVIH
=
'a'
|
(
'v'
<<
8
)
|
(
'i'
<<
16
)
|
(
'h'
<<
24
);
//AVIMAINHEADER
//AVIMAINHEADER
AviHeader
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
AviHeader
Box
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
super
(
type
,
size
,
byteBuffer
);
}
}
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviSeekMap.java
0 → 100644
View file @
a9c94185
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
android.util.SparseArray
;
import
androidx.annotation.NonNull
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.SeekPoint
;
import
com.google.android.exoplayer2.util.Log
;
public
class
AviSeekMap
implements
SeekMap
{
final
AviTrack
videoTrack
;
/**
* Number of frames per index
* i.e. videoFrameOffsetMap[1] is frame 1 * seekIndexFactor
*/
final
int
seekIndexFactor
;
//Map from the Video Frame index to the offset
final
int
[]
videoFrameOffsetMap
;
//Holds a map of video frameIds to audioFrameIds for each audioId
final
SparseArray
<
int
[]>
audioIdMap
;
final
long
moviOffset
;
final
long
duration
;
public
AviSeekMap
(
AviTrack
videoTrack
,
int
seekIndexFactor
,
int
[]
videoFrameOffsetMap
,
SparseArray
<
int
[]>
audioIdMap
,
long
moviOffset
,
long
duration
)
{
this
.
videoTrack
=
videoTrack
;
this
.
seekIndexFactor
=
seekIndexFactor
;
this
.
videoFrameOffsetMap
=
videoFrameOffsetMap
;
this
.
audioIdMap
=
audioIdMap
;
this
.
moviOffset
=
moviOffset
;
this
.
duration
=
duration
;
}
@Override
public
boolean
isSeekable
()
{
return
true
;
}
@Override
public
long
getDurationUs
()
{
return
duration
;
}
private
int
getSeekFrameIndex
(
long
timeUs
)
{
final
int
reqFrame
=
(
int
)(
timeUs
/
videoTrack
.
usPerSample
);
int
reqFrameIndex
=
reqFrame
/
seekIndexFactor
;
if
(
reqFrameIndex
>=
videoFrameOffsetMap
.
length
)
{
reqFrameIndex
=
videoFrameOffsetMap
.
length
-
1
;
}
return
reqFrameIndex
;
}
@NonNull
@Override
public
SeekPoints
getSeekPoints
(
long
timeUs
)
{
final
int
seekFrameIndex
=
getSeekFrameIndex
(
timeUs
);
int
offset
=
videoFrameOffsetMap
[
seekFrameIndex
];
final
long
outUs
=
seekFrameIndex
*
seekIndexFactor
*
videoTrack
.
usPerSample
;
final
long
position
=
offset
+
moviOffset
;
Log
.
d
(
AviExtractor
.
TAG
,
"SeekPoint: us="
+
outUs
+
" pos="
+
position
);
return
new
SeekPoints
(
new
SeekPoint
(
outUs
,
position
));
}
public
void
setFrames
(
final
long
position
,
final
long
timeUs
,
final
SparseArray
<
AviTrack
>
idTrackMap
)
{
final
int
seekFrameIndex
=
getSeekFrameIndex
(
timeUs
);
videoTrack
.
frame
=
seekFrameIndex
*
seekIndexFactor
;
for
(
int
i
=
0
;
i
<
audioIdMap
.
size
();
i
++)
{
final
int
audioId
=
audioIdMap
.
keyAt
(
i
);
final
int
[]
video2AudioFrameMap
=
audioIdMap
.
get
(
audioId
);
final
AviTrack
audioTrack
=
idTrackMap
.
get
(
audioId
);
audioTrack
.
frame
=
video2AudioFrameMap
[
seekFrameIndex
];
}
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviTrack.java
0 → 100644
View file @
a9c94185
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
android.util.SparseIntArray
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.util.Arrays
;
/**
* Collection of info about a track
*/
public
class
AviTrack
{
final
int
id
;
@NonNull
final
TrackOutput
trackOutput
;
@NonNull
final
StreamHeaderBox
streamHeaderBox
;
long
usPerSample
;
/**
* True indicates all frames are key frames (e.g. Audio, MJPEG)
*/
boolean
allKeyFrames
;
/**
* Key is frame number value is offset
*/
@Nullable
int
[]
keyFrames
;
/**
* Current frame in the stream
* This needs to be updated on seek
* TODO: Should be offset from StreamHeaderBox.getStart()
*/
transient
int
frame
;
/**
*
* @param trackOutput
*/
AviTrack
(
int
id
,
@NonNull
TrackOutput
trackOutput
,
@NonNull
StreamHeaderBox
streamHeaderBox
)
{
this
.
id
=
id
;
this
.
trackOutput
=
trackOutput
;
this
.
streamHeaderBox
=
streamHeaderBox
;
this
.
usPerSample
=
streamHeaderBox
.
getUsPerSample
();
this
.
allKeyFrames
=
streamHeaderBox
.
isAudio
()
||
(
MimeTypes
.
IMAGE_JPEG
.
equals
(
streamHeaderBox
.
getMimeType
()));
}
public
boolean
isKeyFrame
()
{
if
(
allKeyFrames
)
{
return
true
;
}
return
keyFrames
!=
null
&&
Arrays
.
binarySearch
(
keyFrames
,
frame
)
>=
0
;
}
public
void
setKeyFrames
(
int
[]
keyFrames
)
{
this
.
keyFrames
=
keyFrames
;
}
public
long
getUs
()
{
return
frame
*
usPerSample
;
}
public
void
advance
()
{
frame
++;
}
public
boolean
isVideo
()
{
return
streamHeaderBox
.
isVideo
();
}
public
boolean
isAudio
()
{
return
streamHeaderBox
.
isAudio
();
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviUtil.java
View file @
a9c94185
...
@@ -65,17 +65,4 @@ public class AviUtil {
...
@@ -65,17 +65,4 @@ public class AviUtil {
}
}
return
null
;
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
View file @
a9c94185
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
/**
* This is referred to as a Chunk in the MS spec, but that gets confusing with AV chunks
*/
public
class
Box
{
public
class
Box
{
private
final
long
size
;
private
final
long
size
;
private
final
int
type
;
private
final
int
type
;
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/BoxFactory.java
0 → 100644
View file @
a9c94185
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.NonNull
;
import
java.nio.ByteBuffer
;
public
class
BoxFactory
{
@NonNull
public
ResidentBox
createBox
(
final
int
type
,
final
int
size
,
final
ByteBuffer
byteBuffer
)
{
final
ByteBuffer
boxBuffer
=
AviExtractor
.
allocate
(
size
);
AviUtil
.
copy
(
byteBuffer
,
boxBuffer
,
size
);
switch
(
type
)
{
case
AviHeaderBox
.
AVIH
:
return
new
AviHeaderBox
(
type
,
size
,
boxBuffer
);
case
ListBox
.
LIST
:
return
new
ListBox
(
type
,
size
,
boxBuffer
);
case
StreamHeaderBox
.
STRH
:
return
new
StreamHeaderBox
(
type
,
size
,
boxBuffer
);
case
StreamFormatBox
.
STRF
:
return
new
StreamFormatBox
(
type
,
size
,
boxBuffer
);
default
:
return
new
ResidentBox
(
type
,
size
,
boxBuffer
);
}
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/IAviList.java
deleted
100644 → 0
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/
ResidentList
.java
→
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/
ListBox
.java
View file @
a9c94185
...
@@ -5,15 +5,18 @@ import java.nio.ByteBuffer;
...
@@ -5,15 +5,18 @@ import java.nio.ByteBuffer;
/**
/**
* An AVI LIST box, memory resident
* An AVI LIST box, memory resident
*/
*/
public
class
ResidentList
extends
ResidentBox
implements
IAviList
{
public
class
ListBox
extends
ResidentBox
{
public
static
final
int
LIST
=
'L'
|
(
'I'
<<
8
)
|
(
'S'
<<
16
)
|
(
'T'
<<
24
);
//Header List
public
static
final
int
TYPE_HDRL
=
'h'
|
(
'd'
<<
8
)
|
(
'r'
<<
16
)
|
(
'l'
<<
24
);
private
final
int
listType
;
private
final
int
listType
;
ResidentList
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
ListBox
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
super
(
type
,
size
,
byteBuffer
);
listType
=
byteBuffer
.
getInt
(
0
);
listType
=
byteBuffer
.
getInt
(
0
);
}
}
@Override
public
int
getListType
()
{
public
int
getListType
()
{
return
listType
;
return
listType
;
}
}
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/ResidentBox.java
View file @
a9c94185
...
@@ -13,25 +13,28 @@ import java.nio.ByteOrder;
...
@@ -13,25 +13,28 @@ import java.nio.ByteOrder;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
/**
* A box that is resident in memory
*/
public
class
ResidentBox
extends
Box
{
public
class
ResidentBox
extends
Box
{
private
static
final
String
TAG
=
AviExtractor
.
TAG
;
private
static
final
String
TAG
=
AviExtractor
.
TAG
;
final
private
static
int
MAX_RESIDENT
=
2
*
1024
;
final
private
static
int
MAX_RESIDENT
=
64
*
1024
;
final
ByteBuffer
byteBuffer
;
final
ByteBuffer
byteBuffer
;
private
Class
<?
extends
ResidentBox
>
getClass
(
final
int
type
)
{
//
private Class<? extends ResidentBox> getClass(final int type) {
switch
(
type
)
{
//
switch (type) {
case
AviHeader
.
AVIH
:
// case AviHeaderBox
.AVIH:
return
AviHeader
.
class
;
// return AviHeaderBox
.class;
case
IAviList
.
LIST
:
// case ListBox
.LIST:
return
ResidentList
.
class
;
// return ListBox
.class;
case
StreamHeader
.
STRH
:
// case StreamHeaderBox
.STRH:
return
StreamHeader
.
class
;
// return StreamHeaderBox
.class;
case
StreamFormat
.
STRF
:
// case StreamFormatBox
.STRF:
return
StreamFormat
.
class
;
// return StreamFormatBox
.class;
default
:
//
default:
return
ResidentBox
.
class
;
//
return ResidentBox.class;
}
//
}
}
//
}
ResidentBox
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
ResidentBox
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
);
super
(
type
,
size
);
...
@@ -92,20 +95,14 @@ public class ResidentBox extends Box {
...
@@ -92,20 +95,14 @@ public class ResidentBox extends Box {
}
}
@NonNull
@NonNull
public
List
<
ResidentBox
>
getBoxList
()
{
public
List
<
ResidentBox
>
getBoxList
(
final
BoxFactory
boxFactory
)
{
final
ByteBuffer
temp
=
getByteBuffer
();
final
ByteBuffer
temp
=
getByteBuffer
();
temp
.
position
(
4
);
temp
.
position
(
4
);
final
List
<
ResidentBox
>
list
=
new
ArrayList
<>();
final
List
<
ResidentBox
>
list
=
new
ArrayList
<>();
while
(
temp
.
hasRemaining
())
{
while
(
temp
.
hasRemaining
())
{
final
int
type
=
temp
.
getInt
();
final
int
type
=
temp
.
getInt
();
final
int
size
=
temp
.
getInt
();
final
int
size
=
temp
.
getInt
();
final
Class
<?
extends
ResidentBox
>
clazz
=
getClass
(
type
);
final
ResidentBox
residentBox
=
boxFactory
.
createBox
(
type
,
size
,
temp
);
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
);
list
.
add
(
residentBox
);
}
}
return
list
;
return
list
;
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamFormat.java
→
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamFormat
Box
.java
View file @
a9c94185
...
@@ -3,10 +3,10 @@ package com.google.android.exoplayer2.extractor.avi;
...
@@ -3,10 +3,10 @@ package com.google.android.exoplayer2.extractor.avi;
import
androidx.annotation.NonNull
;
import
androidx.annotation.NonNull
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
public
class
StreamFormat
extends
ResidentBox
{
public
class
StreamFormat
Box
extends
ResidentBox
{
public
static
final
int
STRF
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'f'
<<
24
);
public
static
final
int
STRF
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'f'
<<
24
);
StreamFormat
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
StreamFormat
Box
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
super
(
type
,
size
,
byteBuffer
);
}
}
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeader.java
→
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeader
Box
.java
View file @
a9c94185
...
@@ -4,7 +4,10 @@ import android.util.SparseArray;
...
@@ -4,7 +4,10 @@ import android.util.SparseArray;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
public
class
StreamHeader
extends
ResidentBox
{
/**
* AVISTREAMHEADER
*/
public
class
StreamHeaderBox
extends
ResidentBox
{
public
static
final
int
STRH
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'h'
<<
24
);
public
static
final
int
STRH
=
's'
|
(
't'
<<
8
)
|
(
'r'
<<
16
)
|
(
'h'
<<
24
);
//Audio Stream
//Audio Stream
...
@@ -16,15 +19,23 @@ public class StreamHeader extends ResidentBox {
...
@@ -16,15 +19,23 @@ public class StreamHeader extends ResidentBox {
private
static
final
SparseArray
<
String
>
STREAM_MAP
=
new
SparseArray
<>();
private
static
final
SparseArray
<
String
>
STREAM_MAP
=
new
SparseArray
<>();
static
{
static
{
STREAM_MAP
.
put
(
'M'
|
(
'P'
<<
8
)
|
(
'4'
<<
16
)
|
(
'2'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
//Although other types are technically supported, AVI is almost exclusively MP4V and MJPEG
STREAM_MAP
.
put
(
'3'
|
(
'V'
<<
8
)
|
(
'I'
<<
16
)
|
(
'D'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
final
String
mimeType
=
MimeTypes
.
VIDEO_MP4V
;
STREAM_MAP
.
put
(
'x'
|
(
'v'
<<
8
)
|
(
'i'
<<
16
)
|
(
'd'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
//final String mimeType = MimeTypes.VIDEO_H263;
STREAM_MAP
.
put
(
'X'
|
(
'V'
<<
8
)
|
(
'I'
<<
16
)
|
(
'D'
<<
24
),
MimeTypes
.
VIDEO_MP4V
);
//Doesn't seem to be supported on Android
//STREAM_MAP.put('M' | ('P' << 8) | ('4' << 16) | ('2' << 24), MimeTypes.VIDEO_MP4);
STREAM_MAP
.
put
(
'H'
|
(
'2'
<<
8
)
|
(
'6'
<<
16
)
|
(
'4'
<<
24
),
MimeTypes
.
VIDEO_H264
);
STREAM_MAP
.
put
(
'a'
|
(
'v'
<<
8
)
|
(
'c'
<<
16
)
|
(
'1'
<<
24
),
MimeTypes
.
VIDEO_H264
);
STREAM_MAP
.
put
(
'A'
|
(
'V'
<<
8
)
|
(
'C'
<<
16
)
|
(
'1'
<<
24
),
MimeTypes
.
VIDEO_H264
);
STREAM_MAP
.
put
(
'3'
|
(
'V'
<<
8
)
|
(
'I'
<<
16
)
|
(
'D'
<<
24
),
mimeType
);
STREAM_MAP
.
put
(
'x'
|
(
'v'
<<
8
)
|
(
'i'
<<
16
)
|
(
'd'
<<
24
),
mimeType
);
STREAM_MAP
.
put
(
'X'
|
(
'V'
<<
8
)
|
(
'I'
<<
16
)
|
(
'D'
<<
24
),
mimeType
);
STREAM_MAP
.
put
(
'm'
|
(
'j'
<<
8
)
|
(
'p'
<<
16
)
|
(
'g'
<<
24
),
MimeTypes
.
IMAGE_JPEG
);
STREAM_MAP
.
put
(
'm'
|
(
'j'
<<
8
)
|
(
'p'
<<
16
)
|
(
'g'
<<
24
),
MimeTypes
.
IMAGE_JPEG
);
}
}
StreamHeader
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
StreamHeader
Box
(
int
type
,
int
size
,
ByteBuffer
byteBuffer
)
{
super
(
type
,
size
,
byteBuffer
);
super
(
type
,
size
,
byteBuffer
);
}
}
...
@@ -40,7 +51,15 @@ public class StreamHeader extends ResidentBox {
...
@@ -40,7 +51,15 @@ public class StreamHeader extends ResidentBox {
return
getRate
()
/
(
float
)
getScale
();
return
getRate
()
/
(
float
)
getScale
();
}
}
public
String
getCodec
()
{
/**
* How long each sample covers
* @return
*/
public
long
getUsPerSample
()
{
return
getScale
()
*
1_000_000L
/
getRate
();
}
public
String
getMimeType
()
{
return
STREAM_MAP
.
get
(
getFourCC
());
return
STREAM_MAP
.
get
(
getFourCC
());
}
}
...
@@ -67,8 +86,15 @@ public class StreamHeader extends ResidentBox {
...
@@ -67,8 +86,15 @@ public class StreamHeader extends ResidentBox {
public
int
getRate
()
{
public
int
getRate
()
{
return
byteBuffer
.
getInt
(
24
);
return
byteBuffer
.
getInt
(
24
);
}
}
//28 - dwStart
public
int
getStart
()
{
return
byteBuffer
.
getInt
(
28
);
}
public
long
getLength
()
{
public
long
getLength
()
{
return
byteBuffer
.
getInt
(
32
)
&
AviUtil
.
UINT_MASK
;
return
byteBuffer
.
getInt
(
32
)
&
AviUtil
.
UINT_MASK
;
}
}
//36 - dwSuggestedBufferSize
//40 - dwQuality
public
int
getSampleSize
()
{
return
byteBuffer
.
getInt
(
44
);
}
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/UnboundedIntArray.java
0 → 100644
View file @
a9c94185
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.NonNull
;
import
java.util.Arrays
;
public
class
UnboundedIntArray
{
@NonNull
int
[]
array
;
//unint
int
size
=
0
;
public
UnboundedIntArray
()
{
this
(
8
);
}
public
UnboundedIntArray
(
int
size
)
{
if
(
size
<
0
)
{
throw
new
IllegalArgumentException
(
"Initial size must be positive: "
+
size
);
}
array
=
new
int
[
size
];
}
public
void
add
(
int
v
)
{
if
(
size
==
array
.
length
)
{
grow
();
}
array
[
size
++]
=
v
;
}
public
int
getSize
()
{
return
size
;
}
public
void
pack
()
{
array
=
Arrays
.
copyOf
(
array
,
size
);
}
protected
void
grow
()
{
int
increase
=
Math
.
max
(
array
.
length
/
4
,
1
);
array
=
Arrays
.
copyOf
(
array
,
increase
+
array
.
length
+
size
);
}
/**
* Only works if values are in sequential order
* @param v
* @return
*/
public
int
indexOf
(
int
v
)
{
return
Arrays
.
binarySearch
(
array
,
v
);
}
}
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