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
9927883b
authored
Feb 03, 2022
by
kimvde
Committed by
Ian Baker
Feb 04, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Use SpeedChangingAudioProcessor in Transformer
PiperOrigin-RevId: 426113559
parent
564c3bcb
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
117 additions
and
141 deletions
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AudioTranscodingSamplePipeline.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SpeedChangingAudioProcessor.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java
testdata/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AudioTranscodingSamplePipeline.java
View file @
9927883b
...
...
@@ -20,17 +20,16 @@ import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
static
java
.
lang
.
Math
.
min
;
import
android.media.MediaCodec.BufferInfo
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.audio.AudioProcessor
;
import
com.google.android.exoplayer2.audio.AudioProcessor.AudioFormat
;
import
com.google.android.exoplayer2.audio.SonicAudioProcessor
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
import
java.util.List
;
import
org.checkerframework.checker.nullness.qual.RequiresNonNull
;
import
org.checkerframework.dataflow.qual.Pure
;
/**
...
...
@@ -43,22 +42,18 @@ import org.checkerframework.dataflow.qual.Pure;
private
final
Codec
decoder
;
private
final
DecoderInputBuffer
decoderInputBuffer
;
private
final
SonicAudioProcessor
sonicAudioProcessor
;
private
final
SpeedProvider
speedProvider
;
private
final
boolean
flattenForSlowMotion
;
@Nullable
private
final
SpeedChangingAudioProcessor
speedChangingAudioProcessor
;
private
final
Codec
encoder
;
private
final
AudioFormat
encoderInputAudioFormat
;
private
final
DecoderInputBuffer
encoderInputBuffer
;
private
final
DecoderInputBuffer
encoderOutputBuffer
;
private
ByteBuffer
processorOutputBuffer
;
private
long
nextEncoderInputBufferTimeUs
;
private
long
encoderBufferDurationRemainder
;
private
ByteBuffer
sonicOutputBuffer
;
private
boolean
drainingSonicForSpeedChange
;
private
float
currentSpeed
;
public
AudioTranscodingSamplePipeline
(
Format
inputFormat
,
TransformationRequest
transformationRequest
,
...
...
@@ -74,13 +69,8 @@ import org.checkerframework.dataflow.qual.Pure;
encoderOutputBuffer
=
new
DecoderInputBuffer
(
DecoderInputBuffer
.
BUFFER_REPLACEMENT_MODE_DISABLED
);
this
.
decoder
=
decoderFactory
.
createForAudioDecoding
(
inputFormat
);
decoder
=
decoderFactory
.
createForAudioDecoding
(
inputFormat
);
this
.
flattenForSlowMotion
=
transformationRequest
.
flattenForSlowMotion
;
sonicAudioProcessor
=
new
SonicAudioProcessor
();
sonicOutputBuffer
=
AudioProcessor
.
EMPTY_BUFFER
;
speedProvider
=
new
SegmentSpeedProvider
(
inputFormat
);
currentSpeed
=
speedProvider
.
getSpeed
(
0
);
AudioFormat
encoderInputAudioFormat
=
new
AudioFormat
(
inputFormat
.
sampleRate
,
...
...
@@ -88,18 +78,21 @@ import org.checkerframework.dataflow.qual.Pure;
// The decoder uses ENCODING_PCM_16BIT by default.
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers
C
.
ENCODING_PCM_16BIT
);
if
(
flattenForSlowMotion
)
{
if
(
transformationRequest
.
flattenForSlowMotion
)
{
speedChangingAudioProcessor
=
new
SpeedChangingAudioProcessor
(
new
SegmentSpeedProvider
(
inputFormat
));
try
{
encoderInputAudioFormat
=
s
onic
AudioProcessor
.
configure
(
encoderInputAudioFormat
);
encoderInputAudioFormat
=
s
peedChanging
AudioProcessor
.
configure
(
encoderInputAudioFormat
);
}
catch
(
AudioProcessor
.
UnhandledAudioFormatException
impossible
)
{
throw
new
IllegalStateException
(
impossible
);
}
s
onicAudioProcessor
.
setSpeed
(
currentSpeed
);
sonicAudioProcessor
.
setPitch
(
currentSpeed
);
s
onicAudioProcessor
.
flush
()
;
s
peedChangingAudioProcessor
.
flush
(
);
}
else
{
s
peedChangingAudioProcessor
=
null
;
}
this
.
encoderInputAudioFormat
=
encoderInputAudioFormat
;
processorOutputBuffer
=
AudioProcessor
.
EMPTY_BUFFER
;
this
.
encoderInputAudioFormat
=
encoderInputAudioFormat
;
Format
requestedOutputFormat
=
new
Format
.
Builder
()
.
setSampleMimeType
(
...
...
@@ -130,8 +123,8 @@ import org.checkerframework.dataflow.qual.Pure;
@Override
public
boolean
processData
()
throws
TransformationException
{
if
(
s
onicAudioProcessor
.
isActive
()
)
{
return
feedEncoderFrom
Sonic
()
||
feedSonic
FromDecoder
();
if
(
s
peedChangingAudioProcessor
!=
null
)
{
return
feedEncoderFrom
Processor
()
||
feedProcessor
FromDecoder
();
}
else
{
return
feedEncoderFromDecoder
();
}
...
...
@@ -167,7 +160,9 @@ import org.checkerframework.dataflow.qual.Pure;
@Override
public
void
release
()
{
sonicAudioProcessor
.
reset
();
if
(
speedChangingAudioProcessor
!=
null
)
{
speedChangingAudioProcessor
.
reset
();
}
decoder
.
release
();
encoder
.
release
();
}
...
...
@@ -190,10 +185,7 @@ import org.checkerframework.dataflow.qual.Pure;
if
(
decoderOutputBuffer
==
null
)
{
return
false
;
}
if
(
isSpeedChanging
(
checkNotNull
(
decoder
.
getOutputBufferInfo
())))
{
flushSonicAndSetSpeed
(
currentSpeed
);
return
false
;
}
feedEncoder
(
decoderOutputBuffer
);
if
(!
decoderOutputBuffer
.
hasRemaining
())
{
decoder
.
releaseOutputBuffer
();
...
...
@@ -205,22 +197,23 @@ import org.checkerframework.dataflow.qual.Pure;
* Attempts to pass audio processor output data to the encoder, and returns whether it may be
* possible to pass more data immediately by calling this method again.
*/
private
boolean
feedEncoderFromSonic
()
throws
TransformationException
{
@RequiresNonNull
(
"speedChangingAudioProcessor"
)
private
boolean
feedEncoderFromProcessor
()
throws
TransformationException
{
if
(!
encoder
.
maybeDequeueInputBuffer
(
encoderInputBuffer
))
{
return
false
;
}
if
(!
sonic
OutputBuffer
.
hasRemaining
())
{
sonicOutputBuffer
=
sonic
AudioProcessor
.
getOutput
();
if
(!
sonic
OutputBuffer
.
hasRemaining
())
{
if
(
decoder
.
isEnded
()
&&
s
onic
AudioProcessor
.
isEnded
())
{
if
(!
processor
OutputBuffer
.
hasRemaining
())
{
processorOutputBuffer
=
speedChanging
AudioProcessor
.
getOutput
();
if
(!
processor
OutputBuffer
.
hasRemaining
())
{
if
(
decoder
.
isEnded
()
&&
s
peedChanging
AudioProcessor
.
isEnded
())
{
queueEndOfStreamToEncoder
();
}
return
false
;
}
}
feedEncoder
(
sonic
OutputBuffer
);
feedEncoder
(
processor
OutputBuffer
);
return
true
;
}
...
...
@@ -228,37 +221,27 @@ import org.checkerframework.dataflow.qual.Pure;
* Attempts to process decoder output data, and returns whether it may be possible to process more
* data immediately by calling this method again.
*/
private
boolean
feedSonicFromDecoder
()
throws
TransformationException
{
if
(
drainingSonicForSpeedChange
)
{
if
(
sonicAudioProcessor
.
isEnded
()
&&
!
sonicOutputBuffer
.
hasRemaining
())
{
flushSonicAndSetSpeed
(
currentSpeed
);
drainingSonicForSpeedChange
=
false
;
}
return
false
;
}
// Sonic invalidates any previous output buffer when more input is queued, so we don't queue if
// there is output still to be processed.
if
(
sonicOutputBuffer
.
hasRemaining
())
{
@RequiresNonNull
(
"speedChangingAudioProcessor"
)
private
boolean
feedProcessorFromDecoder
()
throws
TransformationException
{
// Audio processors invalidate any previous output buffer when more input is queued, so we don't
// queue if there is output still to be processed.
if
(
processorOutputBuffer
.
hasRemaining
()
||
speedChangingAudioProcessor
.
getOutput
().
hasRemaining
())
{
return
false
;
}
if
(
decoder
.
isEnded
())
{
s
onic
AudioProcessor
.
queueEndOfStream
();
s
peedChanging
AudioProcessor
.
queueEndOfStream
();
return
false
;
}
checkState
(!
s
onic
AudioProcessor
.
isEnded
());
checkState
(!
s
peedChanging
AudioProcessor
.
isEnded
());
@Nullable
ByteBuffer
decoderOutputBuffer
=
decoder
.
getOutputBuffer
();
if
(
decoderOutputBuffer
==
null
)
{
return
false
;
}
if
(
isSpeedChanging
(
checkNotNull
(
decoder
.
getOutputBufferInfo
())))
{
sonicAudioProcessor
.
queueEndOfStream
();
drainingSonicForSpeedChange
=
true
;
return
false
;
}
sonicAudioProcessor
.
queueInput
(
decoderOutputBuffer
);
speedChangingAudioProcessor
.
queueInput
(
decoderOutputBuffer
);
if
(!
decoderOutputBuffer
.
hasRemaining
())
{
decoder
.
releaseOutputBuffer
();
}
...
...
@@ -294,22 +277,6 @@ import org.checkerframework.dataflow.qual.Pure;
encoder
.
queueInputBuffer
(
encoderInputBuffer
);
}
private
boolean
isSpeedChanging
(
BufferInfo
bufferInfo
)
{
if
(!
flattenForSlowMotion
)
{
return
false
;
}
float
newSpeed
=
speedProvider
.
getSpeed
(
bufferInfo
.
presentationTimeUs
);
boolean
speedChanging
=
newSpeed
!=
currentSpeed
;
currentSpeed
=
newSpeed
;
return
speedChanging
;
}
private
void
flushSonicAndSetSpeed
(
float
speed
)
{
sonicAudioProcessor
.
setSpeed
(
speed
);
sonicAudioProcessor
.
setPitch
(
speed
);
sonicAudioProcessor
.
flush
();
}
private
void
computeNextEncoderInputBufferTimeUs
(
long
bytesWritten
,
int
bytesPerFrame
,
int
sampleRate
)
{
// The calculation below accounts for remainders and rounding. Without that it corresponds to
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SpeedChangingAudioProcessor.java
View file @
9927883b
...
...
@@ -28,6 +28,8 @@ import java.nio.ByteBuffer;
/**
* An {@link AudioProcessor} that changes the speed of audio samples depending on their timestamp.
*/
// TODO(b/198772621): Consider making the processor inactive and skipping it in the processor chain
// when speed is 1.
/* package */
final
class
SpeedChangingAudioProcessor
extends
BaseAudioProcessor
{
/** The speed provider that provides the speed for each timestamp. */
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java
View file @
9927883b
...
...
@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.Format;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.List
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
import
org.checkerframework.dataflow.qual.Pure
;
/**
...
...
@@ -42,11 +41,11 @@ import org.checkerframework.dataflow.qual.Pure;
private
final
DecoderInputBuffer
decoderInputBuffer
;
private
final
Codec
decoder
;
@Nullable
private
final
FrameEditor
frameEditor
;
private
final
Codec
encoder
;
private
final
DecoderInputBuffer
encoderOutputBuffer
;
private
@MonotonicNonNull
FrameEditor
frameEditor
;
private
boolean
waitingForFrameEditorInput
;
public
VideoTranscodingSamplePipeline
(
...
...
@@ -139,6 +138,8 @@ import org.checkerframework.dataflow.qual.Pure;
/* outputSurface= */
checkNotNull
(
encoder
.
getInputSurface
()),
transformationRequest
.
enableHdrEditing
,
debugViewProvider
);
}
else
{
frameEditor
=
null
;
}
decoder
=
...
...
testdata/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump
View file @
9927883b
...
...
@@ -132,148 +132,154 @@ sample:
presentationTimeUs = 0
sample:
trackIndex = 0
dataHashCode =
1000136444
size =
140
dataHashCode =
-1948569090
size =
72
isKeyFrame = true
presentationTimeUs = 417
sample:
trackIndex = 0
dataHashCode =
217961709
size =
172
dataHashCode =
-1316750072
size =
84
isKeyFrame = true
presentationTimeUs =
3334
presentationTimeUs =
1917
sample:
trackIndex = 0
dataHashCode =
-879376936
size =
176
dataHashCode =
1016428949
size =
88
isKeyFrame = true
presentationTimeUs =
691
7
presentationTimeUs =
366
7
sample:
trackIndex = 0
dataHashCode =
1259979587
size =
192
dataHashCode =
-1127325245
size =
96
isKeyFrame = true
presentationTimeUs =
10584
presentationTimeUs =
5500
sample:
trackIndex = 0
dataHashCode =
907407225
size =
188
dataHashCode =
1148147726
size =
92
isKeyFrame = true
presentationTimeUs =
14584
presentationTimeUs =
7500
sample:
trackIndex = 0
dataHashCode = -
904354707
size =
1
76
dataHashCode = -
2125685540
size = 76
isKeyFrame = true
presentationTimeUs =
18500
presentationTimeUs =
9417
sample:
trackIndex = 0
dataHashCode =
1001385853
size =
172
dataHashCode =
473329679
size =
24
isKeyFrame = true
presentationTimeUs =
22167
presentationTimeUs =
11000
sample:
trackIndex = 0
dataHashCode = 1545716086
dataHashCode = 240990900
size = 176
isKeyFrame = true
presentationTimeUs = 11500
sample:
trackIndex = 0
dataHashCode = 777637182
size = 196
isKeyFrame = true
presentationTimeUs =
25750
presentationTimeUs =
15167
sample:
trackIndex = 0
dataHashCode =
358710839
dataHashCode =
1872106264
size = 180
isKeyFrame = true
presentationTimeUs =
29834
presentationTimeUs =
19250
sample:
trackIndex = 0
dataHashCode = -
671124798
dataHashCode = -
1520711499
size = 140
isKeyFrame = true
presentationTimeUs =
33584
presentationTimeUs =
23000
sample:
trackIndex = 0
dataHashCode =
-945404910
size =
120
dataHashCode =
1580199067
size =
232
isKeyFrame = true
presentationTimeUs =
36500
presentationTimeUs =
25917
sample:
trackIndex = 0
dataHashCode =
1881048379
size =
88
dataHashCode =
475464086
size =
184
isKeyFrame = true
presentationTimeUs = 3
900
0
presentationTimeUs = 3
075
0
sample:
trackIndex = 0
dataHashCode =
1059579897
size =
88
dataHashCode =
-211754132
size =
172
isKeyFrame = true
presentationTimeUs =
4083
4
presentationTimeUs =
3458
4
sample:
trackIndex = 0
dataHashCode = 1
496098648
size =
84
dataHashCode = 1
236547164
size =
172
isKeyFrame = true
presentationTimeUs =
426
67
presentationTimeUs =
381
67
sample:
trackIndex = 0
dataHashCode =
250093960
size =
751
dataHashCode =
-2064216186
size =
188
isKeyFrame = true
presentationTimeUs = 4
4417
presentationTimeUs = 4
1750
sample:
trackIndex = 0
dataHashCode =
1895536226
size =
1045
dataHashCode =
-682950885
size =
260
isKeyFrame = true
presentationTimeUs =
60063
presentationTimeUs =
45667
sample:
trackIndex = 0
dataHashCode = 1
723596464
size =
947
dataHashCode = 1
301206627
size =
236
isKeyFrame = true
presentationTimeUs =
8183
4
presentationTimeUs =
5108
4
sample:
trackIndex = 0
dataHashCode =
-978803114
size =
94
6
dataHashCode =
256580525
size =
23
6
isKeyFrame = true
presentationTimeUs =
101563
presentationTimeUs =
56000
sample:
trackIndex = 0
dataHashCode =
387377078
size =
94
6
dataHashCode =
-1086601304
size =
23
6
isKeyFrame = true
presentationTimeUs =
121271
presentationTimeUs =
60917
sample:
trackIndex = 0
dataHashCode = -
13265869
8
size =
901
dataHashCode = -
204613158
8
size =
224
isKeyFrame = true
presentationTimeUs =
140980
presentationTimeUs =
65834
sample:
trackIndex = 0
dataHashCode = 1
495036471
size =
899
dataHashCode = 1
550955865
size =
224
isKeyFrame = true
presentationTimeUs =
15975
0
presentationTimeUs =
7050
0
sample:
trackIndex = 0
dataHashCode =
304440590
size =
878
dataHashCode =
-274800552
size =
220
isKeyFrame = true
presentationTimeUs =
178480
presentationTimeUs =
75167
sample:
trackIndex = 0
dataHashCode =
-1955900344
size =
112
dataHashCode =
382420909
size =
224
isKeyFrame = true
presentationTimeUs =
196771
presentationTimeUs =
79750
sample:
trackIndex = 0
dataHashCode =
88896626
size =
116
dataHashCode =
-1431575865
size =
232
isKeyFrame = true
presentationTimeUs =
199105
presentationTimeUs =
84417
sample:
trackIndex = 1
dataHashCode = -968901399
...
...
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