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
d3995eaa
authored
Aug 13, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Fix rotation handling as far as is possible.
Issue: #91
parent
8db13310
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
152 additions
and
61 deletions
demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java
library/src/androidTest/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java
library/src/androidTest/java/com/google/android/exoplayer/extractor/mp4/Mp4ExtractorTest.java
library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java
library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/MediaFormat.java
library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java
library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java
demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java
View file @
d3995eaa
...
...
@@ -76,8 +76,10 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
}
@Override
public
void
onVideoSizeChanged
(
int
width
,
int
height
,
float
pixelWidthHeightRatio
)
{
Log
.
d
(
TAG
,
"videoSizeChanged ["
+
width
+
", "
+
height
+
", "
+
pixelWidthHeightRatio
+
"]"
);
public
void
onVideoSizeChanged
(
int
width
,
int
height
,
int
unappliedRotationDegrees
,
float
pixelWidthHeightRatio
)
{
Log
.
d
(
TAG
,
"videoSizeChanged ["
+
width
+
", "
+
height
+
", "
+
unappliedRotationDegrees
+
", "
+
pixelWidthHeightRatio
+
"]"
);
}
// DemoPlayer.InfoListener
...
...
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
View file @
d3995eaa
...
...
@@ -341,7 +341,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
}
@Override
public
void
onVideoSizeChanged
(
int
width
,
int
height
,
float
pixelWidthAspectRatio
)
{
public
void
onVideoSizeChanged
(
int
width
,
int
height
,
int
unappliedRotationDegrees
,
float
pixelWidthAspectRatio
)
{
shutterView
.
setVisibility
(
View
.
GONE
);
videoFrame
.
setAspectRatio
(
height
==
0
?
1
:
(
width
*
pixelWidthAspectRatio
)
/
height
);
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java
View file @
d3995eaa
...
...
@@ -89,7 +89,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
public
interface
Listener
{
void
onStateChanged
(
boolean
playWhenReady
,
int
playbackState
);
void
onError
(
Exception
e
);
void
onVideoSizeChanged
(
int
width
,
int
height
,
float
pixelWidthHeightRatio
);
void
onVideoSizeChanged
(
int
width
,
int
height
,
int
unappliedRotationDegrees
,
float
pixelWidthHeightRatio
);
}
/**
...
...
@@ -449,9 +450,10 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
}
@Override
public
void
onVideoSizeChanged
(
int
width
,
int
height
,
float
pixelWidthHeightRatio
)
{
public
void
onVideoSizeChanged
(
int
width
,
int
height
,
int
unappliedRotationDegrees
,
float
pixelWidthHeightRatio
)
{
for
(
Listener
listener
:
listeners
)
{
listener
.
onVideoSizeChanged
(
width
,
height
,
pixelWidthHeightRatio
);
listener
.
onVideoSizeChanged
(
width
,
height
,
unappliedRotationDegrees
,
pixelWidthHeightRatio
);
}
}
...
...
library/src/androidTest/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java
View file @
d3995eaa
...
...
@@ -91,7 +91,7 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
public
void
testMaxVideoDimensions
()
{
DashChunkSource
chunkSource
=
new
DashChunkSource
(
generateVodMpd
(),
AdaptationSet
.
TYPE_VIDEO
,
null
,
null
,
null
);
MediaFormat
format
=
MediaFormat
.
createVideoFormat
(
"video/h264"
,
1
,
1
,
1
,
1
,
null
);
MediaFormat
format
=
MediaFormat
.
createVideoFormat
(
"video/h264"
,
1
,
1
,
1
,
1
,
1
,
null
);
format
=
chunkSource
.
getWithMaxVideoDimensions
(
format
);
assertEquals
(
WIDE_WIDTH
,
format
.
maxWidth
);
...
...
@@ -121,7 +121,7 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
Representation
.
newInstance
(
0
,
0
,
null
,
0
,
WIDE_VIDEO
,
segmentBase2
);
DashChunkSource
chunkSource
=
new
DashChunkSource
(
null
,
null
,
representation1
,
representation2
);
MediaFormat
format
=
MediaFormat
.
createVideoFormat
(
"video/h264"
,
1
,
1
,
1
,
1
,
null
);
MediaFormat
format
=
MediaFormat
.
createVideoFormat
(
"video/h264"
,
1
,
1
,
1
,
1
,
1
,
null
);
format
=
chunkSource
.
getWithMaxVideoDimensions
(
format
);
assertEquals
(
WIDE_WIDTH
,
format
.
maxWidth
);
...
...
library/src/androidTest/java/com/google/android/exoplayer/extractor/mp4/Mp4ExtractorTest.java
View file @
d3995eaa
...
...
@@ -77,8 +77,9 @@ public final class Mp4ExtractorTest extends TestCase {
+
"000000000000000000000000000003"
);
/** String of hexadecimal bytes containing a tkhd payload with an unknown duration. */
private
static
final
byte
[]
TKHD_PAYLOAD
=
getByteArray
(
"0000000000000000000000000000000000000000FFFFFFFF"
);
private
static
final
byte
[]
TKHD_PAYLOAD
=
getByteArray
(
"00000007D1F0C7BFD1F0C7BF0000000000000000FFFFFFFF00000000000000000000000000000000000100"
+
"0000000000000000000000000000010000000000000000000000000000400000000780000004380000"
);
/** Video frame timestamps in time units. */
private
static
final
int
[]
SAMPLE_TIMESTAMPS
=
{
0
,
2
,
3
,
5
,
6
,
7
};
...
...
@@ -87,7 +88,7 @@ public final class Mp4ExtractorTest extends TestCase {
/** Indices of key-frames. */
private
static
final
boolean
[]
SAMPLE_IS_SYNC
=
{
true
,
false
,
false
,
false
,
true
,
true
};
/** Indices of video frame chunk offsets. */
private
static
final
int
[]
CHUNK_OFFSETS
=
{
1
080
,
2000
,
3000
,
400
0
};
private
static
final
int
[]
CHUNK_OFFSETS
=
{
1
200
,
2120
,
3120
,
412
0
};
/** Numbers of video frames in each chunk. */
private
static
final
int
[]
SAMPLES_IN_CHUNK
=
{
2
,
2
,
1
,
1
};
/** The mdat box must be large enough to avoid reading chunk sample data out of bounds. */
...
...
@@ -399,7 +400,7 @@ public final class Mp4ExtractorTest extends TestCase {
atom
(
Atom
.
TYPE_stsc
,
getStsc
()),
atom
(
Atom
.
TYPE_stsz
,
getStsz
()),
atom
(
Atom
.
TYPE_stco
,
getStco
())))))),
atom
(
Atom
.
TYPE_mdat
,
getMdat
(
mp4vFormat
?
1
048
:
103
8
,
!
mp4vFormat
)));
atom
(
Atom
.
TYPE_mdat
,
getMdat
(
mp4vFormat
?
1
168
:
115
8
,
!
mp4vFormat
)));
}
/** Gets a valid MP4 file with audio/video tracks and without a synchronization table. */
...
...
@@ -435,7 +436,7 @@ public final class Mp4ExtractorTest extends TestCase {
atom
(
Atom
.
TYPE_stsc
,
getStsc
()),
atom
(
Atom
.
TYPE_stsz
,
getStsz
()),
atom
(
Atom
.
TYPE_stco
,
getStco
())))))),
atom
(
Atom
.
TYPE_mdat
,
getMdat
(
mp4vFormat
?
992
:
98
2
,
!
mp4vFormat
)));
atom
(
Atom
.
TYPE_mdat
,
getMdat
(
mp4vFormat
?
1112
:
110
2
,
!
mp4vFormat
)));
}
private
static
Mp4Atom
atom
(
int
type
,
Mp4Atom
...
containedMp4Atoms
)
{
...
...
library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java
View file @
d3995eaa
...
...
@@ -302,6 +302,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
int
maxInputSize
=
getOptionalIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_MAX_INPUT_SIZE
);
int
width
=
getOptionalIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_WIDTH
);
int
height
=
getOptionalIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_HEIGHT
);
int
rotationDegrees
=
getOptionalIntegerV16
(
format
,
"rotation-degrees"
);
int
channelCount
=
getOptionalIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
sampleRate
=
getOptionalIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_SAMPLE_RATE
);
ArrayList
<
byte
[]>
initializationData
=
new
ArrayList
<>();
...
...
@@ -314,9 +315,9 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
}
long
durationUs
=
format
.
containsKey
(
android
.
media
.
MediaFormat
.
KEY_DURATION
)
?
format
.
getLong
(
android
.
media
.
MediaFormat
.
KEY_DURATION
)
:
C
.
UNKNOWN_TIME_US
;
return
new
MediaFormat
(
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
MediaFormat
.
NO_VALUE
,
channelCount
,
sampleRate
,
language
,
initializationData
,
MediaFormat
.
NO_VALUE
,
MediaFormat
.
NO_VALUE
);
return
new
MediaFormat
(
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
rotationDegrees
,
MediaFormat
.
NO_VALUE
,
channelCount
,
sampleRate
,
language
,
initializationData
,
MediaFormat
.
NO_VALUE
,
MediaFormat
.
NO_VALUE
);
}
@TargetApi
(
16
)
...
...
library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java
View file @
d3995eaa
...
...
@@ -26,6 +26,7 @@ import android.media.MediaCrypto;
import
android.os.Handler
;
import
android.os.SystemClock
;
import
android.view.Surface
;
import
android.view.TextureView
;
import
java.nio.ByteBuffer
;
...
...
@@ -59,11 +60,19 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
*
* @param width The video width in pixels.
* @param height The video height in pixels.
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
* rotation in degrees that the application should apply for the video for it to be rendered
* in the correct orientation. This value will always be zero on API levels 21 and above,
* since the renderer will apply all necessary rotations internally. On earlier API levels
* this is not possible. Applications that use {@link TextureView} can apply the rotation by
* calling {@link TextureView#setTransform}. Applications that do not expect to encounter
* rotated videos can safely ignore this parameter.
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case
* of square pixels this will be equal to 1.0. Different values are indicative of anamorphic
* content.
*/
void
onVideoSizeChanged
(
int
width
,
int
height
,
float
pixelWidthHeightRatio
);
void
onVideoSizeChanged
(
int
width
,
int
height
,
int
unappliedRotationDegrees
,
float
pixelWidthHeightRatio
);
/**
* Invoked when a frame is rendered to a surface for the first time following that surface
...
...
@@ -129,12 +138,15 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
private
long
droppedFrameAccumulationStartTimeMs
;
private
int
droppedFrameCount
;
private
int
pendingRotationDegrees
;
private
float
pendingPixelWidthHeightRatio
;
private
int
currentWidth
;
private
int
currentHeight
;
private
int
currentUnappliedRotationDegrees
;
private
float
currentPixelWidthHeightRatio
;
private
float
pendingPixelWidthHeightRatio
;
private
int
lastReportedWidth
;
private
int
lastReportedHeight
;
private
int
lastReportedUnappliedRotationDegrees
;
private
float
lastReportedPixelWidthHeightRatio
;
/**
...
...
@@ -374,6 +386,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
super
.
onInputFormatChanged
(
holder
);
pendingPixelWidthHeightRatio
=
holder
.
format
.
pixelWidthHeightRatio
==
MediaFormat
.
NO_VALUE
?
1
:
holder
.
format
.
pixelWidthHeightRatio
;
pendingRotationDegrees
=
holder
.
format
.
rotationDegrees
==
MediaFormat
.
NO_VALUE
?
0
:
holder
.
format
.
rotationDegrees
;
}
/**
...
...
@@ -395,6 +409,20 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
?
outputFormat
.
getInteger
(
KEY_CROP_BOTTOM
)
-
outputFormat
.
getInteger
(
KEY_CROP_TOP
)
+
1
:
outputFormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_HEIGHT
);
currentPixelWidthHeightRatio
=
pendingPixelWidthHeightRatio
;
if
(
Util
.
SDK_INT
>=
21
)
{
// On API level 21 and above the decoder applies the rotation when rendering to the surface.
// Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need
// to flip the width, height and pixel aspect ratio to reflect the rotation that was applied.
if
(
pendingRotationDegrees
==
90
||
pendingRotationDegrees
==
270
)
{
int
rotatedHeight
=
currentWidth
;
currentWidth
=
currentHeight
;
currentHeight
=
rotatedHeight
;
currentPixelWidthHeightRatio
=
1
/
currentPixelWidthHeightRatio
;
}
}
else
{
// On API level 20 and below the decoder does not apply the rotation.
currentUnappliedRotationDegrees
=
pendingRotationDegrees
;
}
}
@Override
...
...
@@ -520,22 +548,26 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
private
void
maybeNotifyVideoSizeChanged
()
{
if
(
eventHandler
==
null
||
eventListener
==
null
||
(
lastReportedWidth
==
currentWidth
&&
lastReportedHeight
==
currentHeight
&&
lastReportedUnappliedRotationDegrees
==
currentUnappliedRotationDegrees
&&
lastReportedPixelWidthHeightRatio
==
currentPixelWidthHeightRatio
))
{
return
;
}
// Make final copies to ensure the runnable reports the correct values.
final
int
currentWidth
=
this
.
currentWidth
;
final
int
currentHeight
=
this
.
currentHeight
;
final
int
currentUnappliedRotationDegrees
=
this
.
currentUnappliedRotationDegrees
;
final
float
currentPixelWidthHeightRatio
=
this
.
currentPixelWidthHeightRatio
;
eventHandler
.
post
(
new
Runnable
()
{
@Override
public
void
run
()
{
eventListener
.
onVideoSizeChanged
(
currentWidth
,
currentHeight
,
currentPixelWidthHeightRatio
);
eventListener
.
onVideoSizeChanged
(
currentWidth
,
currentHeight
,
currentUnappliedRotationDegrees
,
currentPixelWidthHeightRatio
);
}
});
// Update the last reported values.
lastReportedWidth
=
currentWidth
;
lastReportedHeight
=
currentHeight
;
lastReportedUnappliedRotationDegrees
=
currentUnappliedRotationDegrees
;
lastReportedPixelWidthHeightRatio
=
currentPixelWidthHeightRatio
;
}
...
...
library/src/main/java/com/google/android/exoplayer/MediaFormat.java
View file @
d3995eaa
...
...
@@ -44,6 +44,7 @@ public final class MediaFormat {
public
final
int
width
;
public
final
int
height
;
public
final
int
rotationDegrees
;
public
final
float
pixelWidthHeightRatio
;
public
final
int
channelCount
;
...
...
@@ -64,19 +65,21 @@ public final class MediaFormat {
public
static
MediaFormat
createVideoFormat
(
String
mimeType
,
int
maxInputSize
,
int
width
,
int
height
,
List
<
byte
[]>
initializationData
)
{
return
createVideoFormat
(
mimeType
,
maxInputSize
,
C
.
UNKNOWN_TIME_US
,
width
,
height
,
initializationData
);
mimeType
,
maxInputSize
,
C
.
UNKNOWN_TIME_US
,
width
,
height
,
NO_VALUE
,
initializationData
);
}
public
static
MediaFormat
createVideoFormat
(
String
mimeType
,
int
maxInputSize
,
long
durationUs
,
int
width
,
int
height
,
List
<
byte
[]>
initializationData
)
{
int
width
,
int
height
,
int
rotationDegrees
,
List
<
byte
[]>
initializationData
)
{
return
createVideoFormat
(
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
1
,
initializationData
);
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
rotationDegrees
,
NO_VALUE
,
initializationData
);
}
public
static
MediaFormat
createVideoFormat
(
String
mimeType
,
int
maxInputSize
,
long
durationUs
,
int
width
,
int
height
,
float
pixelWidthHeightRatio
,
List
<
byte
[]>
initializationData
)
{
return
new
MediaFormat
(
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
pixelWidthHeightRatio
,
NO_VALUE
,
NO_VALUE
,
null
,
initializationData
,
NO_VALUE
,
NO_VALUE
);
int
width
,
int
height
,
int
rotationDegrees
,
float
pixelWidthHeightRatio
,
List
<
byte
[]>
initializationData
)
{
return
new
MediaFormat
(
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
rotationDegrees
,
pixelWidthHeightRatio
,
NO_VALUE
,
NO_VALUE
,
null
,
initializationData
,
NO_VALUE
,
NO_VALUE
);
}
public
static
MediaFormat
createAudioFormat
(
String
mimeType
,
int
maxInputSize
,
int
channelCount
,
...
...
@@ -88,7 +91,7 @@ public final class MediaFormat {
public
static
MediaFormat
createAudioFormat
(
String
mimeType
,
int
maxInputSize
,
long
durationUs
,
int
channelCount
,
int
sampleRate
,
List
<
byte
[]>
initializationData
)
{
return
new
MediaFormat
(
mimeType
,
maxInputSize
,
durationUs
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
channelCount
,
sampleRate
,
null
,
initializationData
,
NO_VALUE
,
NO_VALUE
);
NO_VALUE
,
channelCount
,
sampleRate
,
null
,
initializationData
,
NO_VALUE
,
NO_VALUE
);
}
public
static
MediaFormat
createTextFormat
(
String
mimeType
,
String
language
)
{
...
...
@@ -97,7 +100,7 @@ public final class MediaFormat {
public
static
MediaFormat
createTextFormat
(
String
mimeType
,
String
language
,
long
durationUs
)
{
return
new
MediaFormat
(
mimeType
,
NO_VALUE
,
durationUs
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
language
,
null
,
NO_VALUE
,
NO_VALUE
);
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
language
,
null
,
NO_VALUE
,
NO_VALUE
);
}
public
static
MediaFormat
createFormatForMimeType
(
String
mimeType
)
{
...
...
@@ -106,17 +109,19 @@ public final class MediaFormat {
public
static
MediaFormat
createFormatForMimeType
(
String
mimeType
,
long
durationUs
)
{
return
new
MediaFormat
(
mimeType
,
NO_VALUE
,
durationUs
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
null
,
null
,
NO_VALUE
,
NO_VALUE
);
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
null
,
null
,
NO_VALUE
,
NO_VALUE
);
}
/* package */
MediaFormat
(
String
mimeType
,
int
maxInputSize
,
long
durationUs
,
int
width
,
int
height
,
float
pixelWidthHeightRatio
,
int
channelCount
,
int
sampleRate
,
String
language
,
List
<
byte
[]>
initializationData
,
int
maxWidth
,
int
maxHeight
)
{
int
height
,
int
rotationDegrees
,
float
pixelWidthHeightRatio
,
int
channelCount
,
int
sampleRate
,
String
language
,
List
<
byte
[]>
initializationData
,
int
maxWidth
,
int
maxHeight
)
{
this
.
mimeType
=
mimeType
;
this
.
maxInputSize
=
maxInputSize
;
this
.
durationUs
=
durationUs
;
this
.
width
=
width
;
this
.
height
=
height
;
this
.
rotationDegrees
=
rotationDegrees
;
this
.
pixelWidthHeightRatio
=
pixelWidthHeightRatio
;
this
.
channelCount
=
channelCount
;
this
.
sampleRate
=
sampleRate
;
...
...
@@ -128,8 +133,9 @@ public final class MediaFormat {
}
public
MediaFormat
copyWithMaxVideoDimension
(
int
maxWidth
,
int
maxHeight
)
{
return
new
MediaFormat
(
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
pixelWidthHeightRatio
,
channelCount
,
sampleRate
,
language
,
initializationData
,
maxWidth
,
maxHeight
);
return
new
MediaFormat
(
mimeType
,
maxInputSize
,
durationUs
,
width
,
height
,
rotationDegrees
,
pixelWidthHeightRatio
,
channelCount
,
sampleRate
,
language
,
initializationData
,
maxWidth
,
maxHeight
);
}
/**
...
...
@@ -145,6 +151,7 @@ public final class MediaFormat {
maybeSetIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_MAX_INPUT_SIZE
,
maxInputSize
);
maybeSetIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_WIDTH
,
width
);
maybeSetIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_HEIGHT
,
height
);
maybeSetIntegerV16
(
format
,
"rotation-degrees"
,
rotationDegrees
);
maybeSetIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_MAX_WIDTH
,
maxWidth
);
maybeSetIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_MAX_HEIGHT
,
maxHeight
);
maybeSetIntegerV16
(
format
,
android
.
media
.
MediaFormat
.
KEY_CHANNEL_COUNT
,
channelCount
);
...
...
@@ -163,8 +170,8 @@ public final class MediaFormat {
@Override
public
String
toString
()
{
return
"MediaFormat("
+
mimeType
+
", "
+
maxInputSize
+
", "
+
width
+
", "
+
height
+
", "
+
pixelWidthHeightRatio
+
", "
+
channelCount
+
", "
+
sampleRate
+
", "
+
language
+
", "
+
durationUs
+
", "
+
maxWidth
+
", "
+
maxHeight
+
")"
;
+
rotationDegrees
+
", "
+
pixelWidthHeightRatio
+
", "
+
channelCount
+
", "
+
sampleRate
+
", "
+
language
+
", "
+
durationUs
+
", "
+
maxWidth
+
", "
+
maxHeight
+
")"
;
}
@Override
...
...
@@ -175,6 +182,7 @@ public final class MediaFormat {
result
=
31
*
result
+
maxInputSize
;
result
=
31
*
result
+
width
;
result
=
31
*
result
+
height
;
result
=
31
*
result
+
rotationDegrees
;
result
=
31
*
result
+
Float
.
floatToRawIntBits
(
pixelWidthHeightRatio
);
result
=
31
*
result
+
(
int
)
durationUs
;
result
=
31
*
result
+
maxWidth
;
...
...
@@ -213,6 +221,7 @@ public final class MediaFormat {
private
boolean
equalsInternal
(
MediaFormat
other
,
boolean
ignoreMaxDimensions
)
{
if
(
maxInputSize
!=
other
.
maxInputSize
||
width
!=
other
.
width
||
height
!=
other
.
height
||
rotationDegrees
!=
other
.
rotationDegrees
||
pixelWidthHeightRatio
!=
other
.
pixelWidthHeightRatio
||
(!
ignoreMaxDimensions
&&
(
maxWidth
!=
other
.
maxWidth
||
maxHeight
!=
other
.
maxHeight
))
||
channelCount
!=
other
.
channelCount
||
sampleRate
!=
other
.
sampleRate
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java
View file @
d3995eaa
...
...
@@ -50,9 +50,8 @@ import java.util.List;
return
null
;
}
Pair
<
Integer
,
Long
>
header
=
parseTkhd
(
trak
.
getLeafAtomOfType
(
Atom
.
TYPE_tkhd
).
data
);
int
id
=
header
.
first
;
long
duration
=
header
.
second
;
TkhdData
tkhdData
=
parseTkhd
(
trak
.
getLeafAtomOfType
(
Atom
.
TYPE_tkhd
).
data
);
long
duration
=
tkhdData
.
duration
;
long
movieTimescale
=
parseMvhd
(
mvhd
.
data
);
long
durationUs
;
if
(
duration
==
-
1
)
{
...
...
@@ -64,10 +63,10 @@ import java.util.List;
.
getContainerAtomOfType
(
Atom
.
TYPE_stbl
);
Pair
<
Long
,
String
>
mdhdData
=
parseMdhd
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_mdhd
).
data
);
StsdData
Holder
stsdData
=
parseStsd
(
stbl
.
getLeafAtomOfType
(
Atom
.
TYPE_stsd
).
data
,
durationUs
,
mdhdData
.
second
);
StsdData
stsdData
=
parseStsd
(
stbl
.
getLeafAtomOfType
(
Atom
.
TYPE_stsd
).
data
,
durationUs
,
tkhdData
.
rotationDegrees
,
mdhdData
.
second
);
return
stsdData
.
mediaFormat
==
null
?
null
:
new
Track
(
id
,
trackType
,
mdhdData
.
first
,
durationUs
,
stsdData
.
mediaFormat
,
:
new
Track
(
tkhdData
.
id
,
trackType
,
mdhdData
.
first
,
durationUs
,
stsdData
.
mediaFormat
,
stsdData
.
trackEncryptionBoxes
,
stsdData
.
nalUnitLengthFieldLength
);
}
...
...
@@ -268,19 +267,17 @@ import java.util.List;
/**
* Parses a tkhd atom (defined in 14496-12).
*
* @return A {@link Pair} consisting of the track id and duration (in the timescale indicated in
* the movie header box). The duration is set to -1 if the duration is unspecified.
* @return An object containing the parsed data.
*/
private
static
Pair
<
Integer
,
Long
>
parseTkhd
(
ParsableByteArray
tkhd
)
{
private
static
TkhdData
parseTkhd
(
ParsableByteArray
tkhd
)
{
tkhd
.
setPosition
(
Atom
.
HEADER_SIZE
);
int
fullAtom
=
tkhd
.
readInt
();
int
version
=
Atom
.
parseFullAtomVersion
(
fullAtom
);
tkhd
.
skipBytes
(
version
==
0
?
8
:
16
);
int
trackId
=
tkhd
.
readInt
();
tkhd
.
skipBytes
(
4
);
tkhd
.
skipBytes
(
4
);
boolean
durationUnknown
=
true
;
int
durationPosition
=
tkhd
.
getPosition
();
int
durationByteCount
=
version
==
0
?
4
:
8
;
...
...
@@ -298,7 +295,27 @@ import java.util.List;
duration
=
version
==
0
?
tkhd
.
readUnsignedInt
()
:
tkhd
.
readUnsignedLongToLong
();
}
return
Pair
.
create
(
trackId
,
duration
);
tkhd
.
skipBytes
(
16
);
int
a00
=
tkhd
.
readInt
();
int
a01
=
tkhd
.
readInt
();
tkhd
.
skipBytes
(
4
);
int
a10
=
tkhd
.
readInt
();
int
a11
=
tkhd
.
readInt
();
int
rotationDegrees
;
int
fixedOne
=
65536
;
if
(
a00
==
0
&&
a01
==
fixedOne
&&
a10
==
-
fixedOne
&&
a11
==
0
)
{
rotationDegrees
=
90
;
}
else
if
(
a00
==
0
&&
a01
==
-
fixedOne
&&
a10
==
fixedOne
&&
a11
==
0
)
{
rotationDegrees
=
270
;
}
else
if
(
a00
==
-
fixedOne
&&
a01
==
0
&&
a10
==
0
&&
a11
==
-
fixedOne
)
{
rotationDegrees
=
180
;
}
else
{
// Only 0, 90, 180 and 270 are supported. Treat anything else as 0.
rotationDegrees
=
0
;
}
return
new
TkhdData
(
trackId
,
duration
,
rotationDegrees
);
}
/**
...
...
@@ -333,11 +350,16 @@ import java.util.List;
return
Pair
.
create
(
timescale
,
language
);
}
private
static
StsdDataHolder
parseStsd
(
ParsableByteArray
stsd
,
long
durationUs
,
/**
* Parses a stsd atom (defined in 14496-12).
*
* @return An object containing the parsed data.
*/
private
static
StsdData
parseStsd
(
ParsableByteArray
stsd
,
long
durationUs
,
int
rotationDegrees
,
String
language
)
{
stsd
.
setPosition
(
Atom
.
FULL_HEADER_SIZE
);
int
numberOfEntries
=
stsd
.
readInt
();
StsdData
Holder
holder
=
new
StsdDataHolder
(
numberOfEntries
);
StsdData
out
=
new
StsdData
(
numberOfEntries
);
for
(
int
i
=
0
;
i
<
numberOfEntries
;
i
++)
{
int
childStartPosition
=
stsd
.
getPosition
();
int
childAtomSize
=
stsd
.
readInt
();
...
...
@@ -347,25 +369,26 @@ import java.util.List;
||
childAtomType
==
Atom
.
TYPE_encv
||
childAtomType
==
Atom
.
TYPE_mp4v
||
childAtomType
==
Atom
.
TYPE_hvc1
||
childAtomType
==
Atom
.
TYPE_hev1
||
childAtomType
==
Atom
.
TYPE_s263
)
{
parseVideoSampleEntry
(
stsd
,
childStartPosition
,
childAtomSize
,
durationUs
,
holder
,
i
);
parseVideoSampleEntry
(
stsd
,
childStartPosition
,
childAtomSize
,
durationUs
,
rotationDegrees
,
out
,
i
);
}
else
if
(
childAtomType
==
Atom
.
TYPE_mp4a
||
childAtomType
==
Atom
.
TYPE_enca
||
childAtomType
==
Atom
.
TYPE_ac_3
)
{
parseAudioSampleEntry
(
stsd
,
childAtomType
,
childStartPosition
,
childAtomSize
,
durationUs
,
holder
,
i
);
out
,
i
);
}
else
if
(
childAtomType
==
Atom
.
TYPE_TTML
)
{
holder
.
mediaFormat
=
MediaFormat
.
createTextFormat
(
MimeTypes
.
APPLICATION_TTML
,
language
,
out
.
mediaFormat
=
MediaFormat
.
createTextFormat
(
MimeTypes
.
APPLICATION_TTML
,
language
,
durationUs
);
}
else
if
(
childAtomType
==
Atom
.
TYPE_tx3g
)
{
holder
.
mediaFormat
=
MediaFormat
.
createTextFormat
(
MimeTypes
.
APPLICATION_TX3G
,
language
,
out
.
mediaFormat
=
MediaFormat
.
createTextFormat
(
MimeTypes
.
APPLICATION_TX3G
,
language
,
durationUs
);
}
stsd
.
setPosition
(
childStartPosition
+
childAtomSize
);
}
return
holder
;
return
out
;
}
private
static
void
parseVideoSampleEntry
(
ParsableByteArray
parent
,
int
position
,
int
size
,
long
durationUs
,
StsdDataHolder
out
,
int
entryIndex
)
{
long
durationUs
,
int
rotationDegrees
,
StsdData
out
,
int
entryIndex
)
{
parent
.
setPosition
(
position
+
Atom
.
HEADER_SIZE
);
parent
.
skipBytes
(
24
);
...
...
@@ -428,7 +451,7 @@ import java.util.List;
}
out
.
mediaFormat
=
MediaFormat
.
createVideoFormat
(
mimeType
,
MediaFormat
.
NO_VALUE
,
durationUs
,
width
,
height
,
pixelWidthHeightRatio
,
initializationData
);
width
,
height
,
rotationDegrees
,
pixelWidthHeightRatio
,
initializationData
);
}
private
static
AvcCData
parseAvcCFromParent
(
ParsableByteArray
parent
,
int
position
)
{
...
...
@@ -556,7 +579,7 @@ import java.util.List;
}
private
static
void
parseAudioSampleEntry
(
ParsableByteArray
parent
,
int
atomType
,
int
position
,
int
size
,
long
durationUs
,
StsdData
Holder
out
,
int
entryIndex
)
{
int
size
,
long
durationUs
,
StsdData
out
,
int
entryIndex
)
{
parent
.
setPosition
(
position
+
Atom
.
HEADER_SIZE
);
parent
.
skipBytes
(
16
);
int
channelCount
=
parent
.
readUnsignedShort
();
...
...
@@ -703,22 +726,42 @@ import java.util.List;
}
/**
* Holds data parsed from a tkhd atom.
*/
private
static
final
class
TkhdData
{
private
final
int
id
;
private
final
long
duration
;
private
final
int
rotationDegrees
;
public
TkhdData
(
int
id
,
long
duration
,
int
rotationDegrees
)
{
this
.
id
=
id
;
this
.
duration
=
duration
;
this
.
rotationDegrees
=
rotationDegrees
;
}
}
/**
* Holds data parsed from an stsd atom and its children.
*/
private
static
final
class
StsdData
Holder
{
private
static
final
class
StsdData
{
public
final
TrackEncryptionBox
[]
trackEncryptionBoxes
;
public
MediaFormat
mediaFormat
;
public
int
nalUnitLengthFieldLength
;
public
StsdData
Holder
(
int
numberOfEntries
)
{
public
StsdData
(
int
numberOfEntries
)
{
trackEncryptionBoxes
=
new
TrackEncryptionBox
[
numberOfEntries
];
nalUnitLengthFieldLength
=
-
1
;
}
}
/**
* Holds data parsed from an AvcC atom.
*/
private
static
final
class
AvcCData
{
public
final
List
<
byte
[]>
initializationData
;
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java
View file @
d3995eaa
...
...
@@ -211,7 +211,7 @@ import java.util.List;
// Construct and output the format.
output
.
format
(
MediaFormat
.
createVideoFormat
(
MimeTypes
.
VIDEO_H264
,
MediaFormat
.
NO_VALUE
,
C
.
UNKNOWN_TIME_US
,
parsedSpsData
.
width
,
parsedSpsData
.
height
,
C
.
UNKNOWN_TIME_US
,
parsedSpsData
.
width
,
parsedSpsData
.
height
,
0
,
parsedSpsData
.
pixelWidthAspectRatio
,
initializationData
));
hasOutputFormat
=
true
;
}
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java
View file @
d3995eaa
...
...
@@ -306,7 +306,7 @@ import java.util.Collections;
}
output
.
format
(
MediaFormat
.
createVideoFormat
(
MimeTypes
.
VIDEO_H265
,
MediaFormat
.
NO_VALUE
,
C
.
UNKNOWN_TIME_US
,
picWidthInLumaSamples
,
picHeightInLumaSamples
,
pixelWidthHeightRatio
,
C
.
UNKNOWN_TIME_US
,
picWidthInLumaSamples
,
picHeightInLumaSamples
,
0
,
pixelWidthHeightRatio
,
Collections
.
singletonList
(
csd
)));
hasOutputFormat
=
true
;
}
...
...
library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java
View file @
d3995eaa
...
...
@@ -1127,7 +1127,7 @@ public final class WebmExtractor implements Extractor {
sampleRate
,
initializationData
);
}
else
if
(
MimeTypes
.
isVideo
(
mimeType
))
{
return
MediaFormat
.
createVideoFormat
(
mimeType
,
maxInputSize
,
durationUs
,
pixelWidth
,
pixelHeight
,
initializationData
);
pixelHeight
,
0
,
initializationData
);
}
else
{
throw
new
ParserException
(
"Unexpected MIME type."
);
}
...
...
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