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
4c76bf1a
authored
Jan 21, 2022
by
Dustin
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add file chooser to UI, Fixed timing issues on H264, Fixed PAR on XVID and H264
parent
d9afe510
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
382 additions
and
74 deletions
demos/main/src/main/assets/media.exolist.json
demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AvcAviTrack.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/AviHeaderBox.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/Mp4vAviTrack.java
demos/main/src/main/assets/media.exolist.json
View file @
4c76bf1a
...
...
@@ -543,9 +543,8 @@
"name"
:
"Misc"
,
"samples"
:
[
{
"name"
:
"AVI"
,
"uri"
:
"https://drive.google.com/u/0/uc?id=1K6oLKCS56WFbhz33TgilTJBqfMYFTeUd&?export=download"
,
"extension"
:
"avi"
"name"
:
"User File"
,
"uri"
:
"content://user"
},
{
"name"
:
"Dizzy (MP4)"
,
...
...
demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java
View file @
4c76bf1a
...
...
@@ -19,6 +19,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
android.content.ContentResolver
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.content.SharedPreferences
;
...
...
@@ -40,6 +41,8 @@ import android.widget.ExpandableListView.OnChildClickListener;
import
android.widget.ImageButton
;
import
android.widget.TextView
;
import
android.widget.Toast
;
import
androidx.activity.result.ActivityResultLauncher
;
import
androidx.activity.result.contract.ActivityResultContracts
;
import
androidx.annotation.Nullable
;
import
androidx.appcompat.app.AppCompatActivity
;
import
com.google.android.exoplayer2.MediaItem
;
...
...
@@ -73,6 +76,7 @@ public class SampleChooserActivity extends AppCompatActivity
private
static
final
String
TAG
=
"SampleChooserActivity"
;
private
static
final
String
GROUP_POSITION_PREFERENCE_KEY
=
"sample_chooser_group_position"
;
private
static
final
String
CHILD_POSITION_PREFERENCE_KEY
=
"sample_chooser_child_position"
;
private
static
final
Uri
USER_CONTENT
=
new
Uri
.
Builder
().
scheme
(
ContentResolver
.
SCHEME_CONTENT
).
authority
(
"user"
).
build
();
private
String
[]
uris
;
private
boolean
useExtensionRenderers
;
...
...
@@ -80,6 +84,13 @@ public class SampleChooserActivity extends AppCompatActivity
private
SampleAdapter
sampleAdapter
;
private
MenuItem
preferExtensionDecodersMenuItem
;
private
ExpandableListView
sampleListView
;
private
final
ActivityResultLauncher
<
String
[]>
openDocumentLauncher
=
registerForActivityResult
(
new
ActivityResultContracts
.
OpenDocument
(),
uri
->
{
if
(
uri
!=
null
)
{
final
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setUri
(
uri
).
build
();
startPlayer
(
Collections
.
singletonList
(
mediaItem
));
}
});
@Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
...
...
@@ -223,13 +234,25 @@ public class SampleChooserActivity extends AppCompatActivity
prefEditor
.
apply
();
PlaylistHolder
playlistHolder
=
(
PlaylistHolder
)
view
.
getTag
();
final
List
<
MediaItem
>
mediaItems
=
playlistHolder
.
mediaItems
;
if
(!
mediaItems
.
isEmpty
())
{
final
MediaItem
mediaItem
=
mediaItems
.
get
(
0
);
if
(
mediaItem
.
localConfiguration
!=
null
&&
USER_CONTENT
.
equals
(
mediaItem
.
localConfiguration
.
uri
))
{
openDocumentLauncher
.
launch
(
new
String
[]{
"video/*"
});
return
true
;
}
}
startPlayer
(
playlistHolder
.
mediaItems
);
return
true
;
}
private
void
startPlayer
(
final
List
<
MediaItem
>
mediaItems
)
{
Intent
intent
=
new
Intent
(
this
,
PlayerActivity
.
class
);
intent
.
putExtra
(
IntentUtil
.
PREFER_EXTENSION_DECODERS_EXTRA
,
isNonNullAndChecked
(
preferExtensionDecodersMenuItem
));
IntentUtil
.
addToIntent
(
playlistHolder
.
mediaItems
,
intent
);
IntentUtil
.
addToIntent
(
mediaItems
,
intent
);
startActivity
(
intent
);
return
true
;
}
private
void
onSampleDownloadButtonClicked
(
PlaylistHolder
playlistHolder
)
{
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AvcAviTrack.java
0 → 100644
View file @
4c76bf1a
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.NonNull
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.NalUnitUtil
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableNalUnitBitArray
;
import
java.io.IOException
;
public
class
AvcAviTrack
extends
AviTrack
{
private
static
final
int
NAL_TYPE_IRD
=
5
;
private
static
final
int
NAL_TYPE_SEI
=
6
;
private
static
final
int
NAL_TYPE_SPS
=
7
;
private
static
final
int
NAL_MASK
=
0x1f
;
private
Format
.
Builder
formatBuilder
;
private
float
pixelWidthHeightRatio
=
1
f
;
private
NalUnitUtil
.
SpsData
spsData
;
//The frame as a calculated from the picCount
private
int
picFrame
;
private
int
lastPicFrame
;
//Largest picFrame, used when we hit an I frame
private
int
maxPicFrame
=-
1
;
private
int
maxPicCount
;
private
int
posHalf
;
private
int
negHalf
;
AvcAviTrack
(
int
id
,
@NonNull
StreamHeaderBox
streamHeaderBox
,
@NonNull
TrackOutput
trackOutput
,
@NonNull
Format
.
Builder
formatBuilder
)
{
super
(
id
,
streamHeaderBox
,
trackOutput
);
this
.
formatBuilder
=
formatBuilder
;
}
public
void
setFormatBuilder
(
Format
.
Builder
formatBuilder
)
{
this
.
formatBuilder
=
formatBuilder
;
}
private
int
seekNal
(
final
ParsableByteArray
parsableByteArray
)
{
final
byte
[]
buffer
=
parsableByteArray
.
getData
();
for
(
int
i
=
parsableByteArray
.
getPosition
();
i
<
buffer
.
length
-
5
;
i
++)
{
if
(
buffer
[
i
]
==
0
&&
buffer
[
i
+
1
]
==
0
)
{
if
(
buffer
[
i
+
2
]
==
1
)
{
parsableByteArray
.
setPosition
(
i
+
3
);
}
else
if
(
buffer
[
i
+
2
]
==
0
&&
buffer
[
i
+
3
]
==
1
)
{
parsableByteArray
.
setPosition
(
i
+
4
);
}
else
{
continue
;
}
return
(
parsableByteArray
.
readUnsignedByte
()
&
NAL_MASK
);
}
}
return
-
1
;
}
private
void
processIdr
()
{
lastPicFrame
=
0
;
picFrame
=
maxPicFrame
+
1
;
}
private
void
readSps
(
int
size
,
ExtractorInput
input
)
throws
IOException
{
final
byte
[]
buffer
=
new
byte
[
size
];
input
.
readFully
(
buffer
,
0
,
size
,
false
);
final
ParsableByteArray
parsableByteArray
=
new
ParsableByteArray
(
buffer
);
int
nal
;
while
((
nal
=
seekNal
(
parsableByteArray
))
>=
0
)
{
if
(
nal
==
NAL_TYPE_SPS
)
{
spsData
=
NalUnitUtil
.
parseSpsNalUnitPayload
(
parsableByteArray
.
getData
(),
parsableByteArray
.
getPosition
(),
parsableByteArray
.
capacity
());
maxPicCount
=
1
<<
(
spsData
.
picOrderCntLsbLength
);
posHalf
=
maxPicCount
/
2
;
//Not sure why pics are 2x
negHalf
=
-
posHalf
;
//Not sure if this works after the fact
if
(
spsData
.
pixelWidthHeightRatio
!=
pixelWidthHeightRatio
)
{
formatBuilder
.
setPixelWidthHeightRatio
(
spsData
.
pixelWidthHeightRatio
);
trackOutput
.
format
(
formatBuilder
.
build
());
}
Log
.
d
(
AviExtractor
.
TAG
,
"SPS Frame: maxPicCount="
+
maxPicCount
);
}
else
if
(
nal
==
NAL_TYPE_IRD
)
{
processIdr
();
}
}
parsableByteArray
.
setPosition
(
0
);
trackOutput
.
sampleData
(
parsableByteArray
,
parsableByteArray
.
capacity
());
int
flags
=
0
;
if
(
isKeyFrame
())
{
flags
|=
C
.
BUFFER_FLAG_KEY_FRAME
;
}
trackOutput
.
sampleMetadata
(
getUs
(
frame
),
flags
,
parsableByteArray
.
capacity
(),
0
,
null
);
Log
.
d
(
AviExtractor
.
TAG
,
"SPS Frame: "
+
(
isVideo
()?
'V'
:
'A'
)
+
" us="
+
getUs
()
+
" size="
+
size
+
" frame="
+
frame
+
" usFrame="
+
getUsFrame
());
advance
();
}
@Override
int
getUsFrame
()
{
return
picFrame
;
}
int
getPicOrderCountLsb
(
byte
[]
peek
)
{
if
(
peek
[
3
]
!=
1
)
{
return
0
;
}
final
ParsableNalUnitBitArray
in
=
new
ParsableNalUnitBitArray
(
peek
,
5
,
peek
.
length
);
//slide_header()
in
.
readUnsignedExpGolombCodedInt
();
//first_mb_in_slice
in
.
readUnsignedExpGolombCodedInt
();
//slice_type
in
.
readUnsignedExpGolombCodedInt
();
//pic_parameter_set_id
if
(
spsData
.
separateColorPlaneFlag
)
{
in
.
skipBits
(
2
);
//colour_plane_id
}
in
.
readBits
(
spsData
.
frameNumLength
);
//frame_num
if
(!
spsData
.
frameMbsOnlyFlag
)
{
boolean
field_pic_flag
=
in
.
readBit
();
// field_pic_flag
if
(
field_pic_flag
)
{
in
.
readBit
();
// bottom_field_flag
}
}
//We skip IDR in the switch
if
(
spsData
.
picOrderCountType
==
0
)
{
int
picOrderCountLsb
=
in
.
readBits
(
spsData
.
picOrderCntLsbLength
);
Log
.
d
(
"Test"
,
"FrameNum: "
+
frame
+
" cnt="
+
picOrderCountLsb
);
return
picOrderCountLsb
;
}
return
0
;
}
@Override
public
boolean
newChunk
(
int
tag
,
int
size
,
ExtractorInput
input
)
throws
IOException
{
final
int
peekSize
=
Math
.
min
(
size
,
16
);
byte
[]
peek
=
new
byte
[
peekSize
];
input
.
peekFully
(
peek
,
0
,
peekSize
);
final
int
nalType
=
peek
[
4
]
&
NAL_MASK
;
switch
(
nalType
)
{
case
1
:
case
2
:
case
3
:
case
4
:
{
final
int
myPicCount
=
getPicOrderCountLsb
(
peek
);
int
delta
=
myPicCount
-
lastPicFrame
;
if
(
delta
<
negHalf
)
{
delta
+=
maxPicCount
;
}
else
if
(
delta
>
posHalf
)
{
delta
-=
maxPicCount
;
}
picFrame
+=
delta
/
2
;
lastPicFrame
=
myPicCount
;
if
(
maxPicFrame
<
picFrame
)
{
maxPicFrame
=
picFrame
;
}
break
;
}
case
NAL_TYPE_IRD:
processIdr
();
break
;
case
NAL_TYPE_SEI:
case
NAL_TYPE_SPS:
readSps
(
size
,
input
);
return
true
;
}
return
super
.
newChunk
(
tag
,
size
,
input
);
}
public
static
String
toString
(
byte
[]
buffer
,
int
i
,
final
int
len
)
{
final
StringBuilder
sb
=
new
StringBuilder
((
len
-
i
)
*
3
);
while
(
i
<
len
)
{
String
hex
=
Integer
.
toHexString
(
buffer
[
i
]
&
0xff
);
if
(
hex
.
length
()
==
1
)
{
sb
.
append
(
'0'
);
}
sb
.
append
(
hex
);
sb
.
append
(
' '
);
i
++;
}
return
sb
.
toString
();
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java
View file @
4c76bf1a
...
...
@@ -82,9 +82,7 @@ public class AviExtractor implements Extractor {
// private long indexOffset; //Usually chunkStart
//If partial read
private
transient
AviTrack
sampleTrack
;
private
transient
int
sampleRemaining
;
private
transient
int
sampleSize
;
private
transient
AviTrack
chunkHandler
;
public
AviExtractor
()
{
this
(
0
);
...
...
@@ -213,14 +211,6 @@ public class AviExtractor implements Extractor {
i
++;
if
(
streamHeader
.
isVideo
())
{
final
VideoFormat
videoFormat
=
streamFormat
.
getVideoFormat
();
final
StreamDataBox
codecBox
=
(
StreamDataBox
)
peekNext
(
streamChildren
,
i
,
StreamDataBox
.
STRD
);
final
List
<
byte
[]>
codecData
;
if
(
codecBox
!=
null
)
{
codecData
=
Collections
.
singletonList
(
codecBox
.
getData
());
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
());
...
...
@@ -228,17 +218,30 @@ public class AviExtractor implements Extractor {
builder
.
setFrameRate
(
streamHeader
.
getFrameRate
());
final
String
mimeType
=
streamHeader
.
getMimeType
();
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
);
// final StreamDataBox codecBox = (StreamDataBox) peekNext(streamChildren, i, StreamDataBox.STRD);
// final List<byte[]> codecData;
// if (codecBox != null) {
// codecData = Collections.singletonList(codecBox.getData());
// i++;
// } else {
// codecData = null;
// }
// if (codecData != null) {
// builder.setInitializationData(codecData);
// }
final
AviTrack
aviTrack
;
switch
(
mimeType
)
{
case
MimeTypes
.
VIDEO_MP4V
:
aviTrack
=
new
Mp4vAviTrack
(
streamId
,
streamHeader
,
trackOutput
,
builder
);
break
;
case
MimeTypes
.
VIDEO_H264
:
aviTrack
=
new
AvcAviTrack
(
streamId
,
streamHeader
,
trackOutput
,
builder
);
break
;
default
:
aviTrack
=
new
AviTrack
(
streamId
,
streamHeader
,
trackOutput
);
}
trackOutput
.
format
(
builder
.
build
());
idTrackMap
.
put
(
'0'
|
((
'0'
+
streamId
)
<<
8
)
|
(
'd'
<<
16
)
|
(
'c'
<<
24
),
new
AviTrack
(
streamId
,
trackOutput
,
streamHeader
));
idTrackMap
.
put
(
'0'
|
((
'0'
+
streamId
)
<<
8
)
|
(
'd'
<<
16
)
|
(
'c'
<<
24
),
aviTrack
);
durationUs
=
streamHeader
.
getUsPerSample
()
*
streamHeader
.
getLength
();
}
else
if
(
streamHeader
.
isAudio
())
{
final
AudioFormat
audioFormat
=
streamFormat
.
getAudioFormat
();
...
...
@@ -262,7 +265,7 @@ public class AviExtractor implements Extractor {
}
trackOutput
.
format
(
builder
.
build
());
idTrackMap
.
put
(
'0'
|
((
'0'
+
streamId
)
<<
8
)
|
(
'w'
<<
16
)
|
(
'b'
<<
24
),
new
AviTrack
(
streamId
,
trackOutput
,
streamHeader
));
new
AviTrack
(
streamId
,
streamHeader
,
trackOutput
));
}
}
streamId
++;
...
...
@@ -374,20 +377,20 @@ public class AviExtractor implements Extractor {
final
int
[]
keyFrames
=
keyFrameList
.
array
;
videoTrack
.
setKeyFrames
(
keyFrames
);
//Correct the timings
durationUs
=
videoTrack
.
usPerSample
*
videoTrack
.
frame
;
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
());
//If the index isn't sparse, double check the audio length
if
(
videoTrack
.
frame
==
videoTrack
.
streamHeaderBox
.
getLength
())
{
//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
));
}
//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
));
}
}
final
AviSeekMap
seekMap
=
new
AviSeekMap
(
videoTrack
,
seekFrameRate
,
videoSeekOffset
.
array
,
...
...
@@ -397,8 +400,10 @@ public class AviExtractor implements Extractor {
}
int
readSamples
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
{
if
(
sampleRemaining
!=
0
)
{
sampleRemaining
-=
sampleTrack
.
trackOutput
.
sampleData
(
input
,
sampleRemaining
,
false
);
if
(
chunkHandler
!=
null
)
{
if
(
chunkHandler
.
resume
(
input
))
{
chunkHandler
=
null
;
}
}
else
{
ByteBuffer
byteBuffer
=
allocate
(
8
);
final
byte
[]
bytes
=
byteBuffer
.
array
();
...
...
@@ -415,33 +420,26 @@ public class AviExtractor implements Extractor {
return
RESULT_END_OF_INPUT
;
}
input
.
readFully
(
bytes
,
1
,
7
);
int
id
=
byteBuffer
.
getInt
();
sampleS
ize
=
byteBuffer
.
getInt
();
sampleTrack
=
idTrackMap
.
get
(
id
);
final
int
id
=
byteBuffer
.
getInt
();
final
int
s
ize
=
byteBuffer
.
getInt
();
AviTrack
sampleTrack
=
idTrackMap
.
get
(
id
);
if
(
sampleTrack
==
null
)
{
if
(
id
==
ListBox
.
LIST
)
{
seekPosition
.
position
=
input
.
getPosition
()
+
4
;
}
else
{
seekPosition
.
position
=
input
.
getPosition
()
+
s
ampleS
ize
;
seekPosition
.
position
=
input
.
getPosition
()
+
size
;
if
(
id
!=
JUNK
)
{
Log
.
w
(
TAG
,
"Unknown tag="
+
toString
(
id
)
+
" pos="
+
(
input
.
getPosition
()
-
8
)
+
" size="
+
s
ampleS
ize
+
" moviEnd="
+
moviEnd
);
+
" size="
+
size
+
" moviEnd="
+
moviEnd
);
}
}
return
RESULT_SEEK
;
}
else
{
//sampleOffset = (int)(input.getPosition() - 8 - moviOffset);
sampleRemaining
=
sampleSize
-
sampleTrack
.
trackOutput
.
sampleData
(
input
,
sampleSize
,
false
)
;
//Log.d(TAG, "Sample pos=" + (input.getPosition() - 8) + " size=" + sampleSize + " video=" + sampleTrack.isVideo());
if
(!
sampleTrack
.
newChunk
(
id
,
size
,
input
))
{
chunkHandler
=
sampleTrack
;
}
}
}
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() + " size=" + sampleSize);
sampleTrack
.
advance
();
return
RESULT_CONTINUE
;
}
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java
View file @
4c76bf1a
...
...
@@ -3,7 +3,8 @@ package com.google.android.exoplayer2.extractor.avi;
import
java.nio.ByteBuffer
;
public
class
AviHeaderBox
extends
ResidentBox
{
public
static
final
int
AVIF_HASINDEX
=
0x10
;
private
static
final
int
AVIF_HASINDEX
=
0x10
;
private
static
int
AVIF_MUSTUSEINDEX
=
0x20
;
static
final
int
AVIH
=
'a'
|
(
'v'
<<
8
)
|
(
'i'
<<
16
)
|
(
'h'
<<
24
);
//AVIMAINHEADER
...
...
@@ -12,19 +13,20 @@ public class AviHeaderBox extends ResidentBox {
super
(
type
,
size
,
byteBuffer
);
}
boolean
hasIndex
()
{
return
(
getFlags
()
&
AVIF_HASINDEX
)
>
0
;
}
int
getMicroSecPerFrame
()
{
return
byteBuffer
.
getInt
(
0
);
}
//4 = dwMaxBytesPerSec
//Always 0, but should be 2
// int getPaddingGranularity() {
// return byteBuffer.getInt(8);
// }
//8 = dwPaddingGranularity - Always 0, but should be 2
public
boolean
hasIndex
()
{
return
(
getFlags
()
&
AVIF_HASINDEX
)
==
AVIF_HASINDEX
;
}
public
boolean
mustUseIndex
()
{
return
(
getFlags
()
&
AVIF_MUSTUSEINDEX
)
==
AVIF_MUSTUSEINDEX
;
}
int
getFlags
()
{
return
byteBuffer
.
getInt
(
12
);
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviTrack.java
View file @
4c76bf1a
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
android.util.SparseIntArray
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.io.IOException
;
import
java.util.Arrays
;
/**
...
...
@@ -14,9 +16,6 @@ public class AviTrack {
final
int
id
;
@NonNull
final
TrackOutput
trackOutput
;
@NonNull
final
StreamHeaderBox
streamHeaderBox
;
long
usPerSample
;
...
...
@@ -26,24 +25,26 @@ public class AviTrack {
*/
boolean
allKeyFrames
;
@NonNull
TrackOutput
trackOutput
;
/**
* Key is frame number value is offset
*/
@Nullable
int
[]
keyFrames
;
transient
int
chunkSize
;
transient
int
chunkRemaining
;
/**
* Current frame in the stream
* This needs to be updated on seek
* TODO: Should be offset from StreamHeaderBox.getStart()
*/
transient
int
frame
;
int
frame
;
/**
*
* @param trackOutput
*/
AviTrack
(
int
id
,
@NonNull
TrackOutput
trackOutput
,
@NonNull
StreamHeaderBox
streamHeaderBox
)
{
AviTrack
(
int
id
,
@NonNull
StreamHeaderBox
streamHeaderBox
,
@NonNull
TrackOutput
trackOutput
)
{
this
.
id
=
id
;
this
.
trackOutput
=
trackOutput
;
this
.
streamHeaderBox
=
streamHeaderBox
;
...
...
@@ -67,11 +68,11 @@ public class AviTrack {
}
public
long
getUs
()
{
return
frame
*
usPerSample
;
return
getUs
(
getUsFrame
())
;
}
public
void
advance
(
)
{
frame
++
;
public
long
getUs
(
final
int
myFrame
)
{
return
myFrame
*
usPerSample
;
}
public
boolean
isVideo
()
{
...
...
@@ -81,4 +82,45 @@ public class AviTrack {
public
boolean
isAudio
()
{
return
streamHeaderBox
.
isAudio
();
}
public
void
advance
()
{
frame
++;
}
/**
* Get the frame number used to calculate the timeUs
* @return
*/
int
getUsFrame
()
{
return
frame
;
}
public
boolean
newChunk
(
int
tag
,
int
size
,
ExtractorInput
input
)
throws
IOException
{
final
int
remaining
=
size
-
trackOutput
.
sampleData
(
input
,
size
,
false
);
if
(
remaining
==
0
)
{
done
(
size
);
return
true
;
}
else
{
chunkSize
=
size
;
chunkRemaining
=
remaining
;
return
false
;
}
}
public
boolean
resume
(
ExtractorInput
input
)
throws
IOException
{
chunkRemaining
-=
trackOutput
.
sampleData
(
input
,
chunkRemaining
,
false
);
if
(
chunkRemaining
==
0
)
{
done
(
chunkSize
);
return
true
;
}
else
{
return
false
;
}
}
void
done
(
final
int
size
)
{
trackOutput
.
sampleMetadata
(
getUs
(),
(
isKeyFrame
()
?
C
.
BUFFER_FLAG_KEY_FRAME
:
0
),
size
,
0
,
null
);
//Log.d(AviExtractor.TAG, "Frame: " + (isVideo()? 'V' : 'A') + " us=" + getUs() + " size=" + size + " frame=" + frame + " usFrame=" + getUsFrame());
advance
();
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/Mp4vAviTrack.java
0 → 100644
View file @
4c76bf1a
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
avi
;
import
androidx.annotation.NonNull
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.ParsableNalUnitBitArray
;
import
java.io.IOException
;
public
class
Mp4vAviTrack
extends
AviTrack
{
private
static
final
byte
SEQUENCE_START_CODE
=
(
byte
)
0xb0
;
private
static
final
int
LAYER_START_CODE
=
0x20
;
private
static
final
float
[]
ASPECT_RATIO
=
{
0
f
,
1
f
,
12
f
/
11
f
,
10
f
/
11
f
,
16
f
/
11
f
,
40
f
/
33
f
};
private
static
final
int
Extended_PAR
=
0xf
;
private
final
Format
.
Builder
formatBuilder
;
private
float
pixelWidthHeightRatio
=
1
f
;
Mp4vAviTrack
(
int
id
,
@NonNull
StreamHeaderBox
streamHeaderBox
,
@NonNull
TrackOutput
trackOutput
,
@NonNull
Format
.
Builder
formatBuilder
)
{
super
(
id
,
streamHeaderBox
,
trackOutput
);
this
.
formatBuilder
=
formatBuilder
;
}
private
void
processLayerStart
(
byte
[]
peek
,
int
offset
)
{
final
ParsableNalUnitBitArray
in
=
new
ParsableNalUnitBitArray
(
peek
,
offset
,
peek
.
length
);
in
.
skipBit
();
// random_accessible_vol
in
.
skipBits
(
8
);
// video_object_type_indication
boolean
is_object_layer_identifier
=
in
.
readBit
();
if
(
is_object_layer_identifier
)
{
in
.
skipBits
(
7
);
// video_object_layer_verid, video_object_layer_priority
}
int
aspect_ratio_info
=
in
.
readBits
(
4
);
final
float
aspectRatio
;
if
(
aspect_ratio_info
==
Extended_PAR
)
{
float
par_width
=
(
float
)
in
.
readBits
(
8
);
float
par_height
=
(
float
)
in
.
readBits
(
8
);
aspectRatio
=
par_width
/
par_height
;
}
else
{
aspectRatio
=
ASPECT_RATIO
[
aspect_ratio_info
];
}
if
(
aspectRatio
!=
pixelWidthHeightRatio
)
{
trackOutput
.
format
(
formatBuilder
.
setPixelWidthHeightRatio
(
aspectRatio
).
build
());
pixelWidthHeightRatio
=
aspectRatio
;
}
}
private
void
seekLayerStart
(
ExtractorInput
input
)
throws
IOException
{
byte
[]
peek
=
new
byte
[
128
];
input
.
peekFully
(
peek
,
0
,
peek
.
length
);
for
(
int
i
=
4
;
i
<
peek
.
length
-
4
;
i
++)
{
if
(
peek
[
i
]
==
0
&&
peek
[
i
+
1
]
==
0
&&
peek
[
i
+
2
]
==
1
&&
(
peek
[
i
+
3
]
&
0xf0
)
==
LAYER_START_CODE
)
{
processLayerStart
(
peek
,
i
+
4
);
break
;
}
}
}
@Override
public
boolean
newChunk
(
int
tag
,
int
size
,
ExtractorInput
input
)
throws
IOException
{
final
byte
[]
peek
=
new
byte
[
4
];
input
.
peekFully
(
peek
,
0
,
peek
.
length
);
if
(
peek
[
0
]
==
0
&&
peek
[
1
]
==
0
&&
peek
[
2
]
==
1
&&
peek
[
3
]
==
SEQUENCE_START_CODE
)
{
seekLayerStart
(
input
);
}
return
super
.
newChunk
(
tag
,
size
,
input
);
}
}
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