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
327df95f
authored
Feb 01, 2022
by
kimvde
Committed by
Ian Baker
Feb 02, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add SpeedChangingAudioProcessor
PiperOrigin-RevId: 425562875
parent
4020f695
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
169 additions
and
0 deletions
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SegmentSpeedProvider.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SpeedChangingAudioProcessor.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SpeedProvider.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SegmentSpeedProvider.java
View file @
327df95f
...
...
@@ -59,6 +59,13 @@ import java.util.TreeMap;
return
entry
!=
null
?
entry
.
getValue
()
:
baseSpeedMultiplier
;
}
@Override
public
long
getNextSpeedChangeTimeUs
(
long
timeUs
)
{
checkArgument
(
timeUs
>=
0
);
@Nullable
Long
nextTimeUs
=
speedsByStartTimeUs
.
higherKey
(
timeUs
);
return
nextTimeUs
!=
null
?
nextTimeUs
:
C
.
TIME_UNSET
;
}
private
static
ImmutableSortedMap
<
Long
,
Float
>
buildSpeedByStartTimeUsMap
(
Format
format
,
float
baseSpeed
)
{
List
<
Segment
>
segments
=
extractSlowMotionSegments
(
format
);
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SpeedChangingAudioProcessor.java
0 → 100644
View file @
327df95f
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
static
java
.
lang
.
Math
.
min
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.audio.AudioProcessor
;
import
com.google.android.exoplayer2.audio.BaseAudioProcessor
;
import
com.google.android.exoplayer2.audio.SonicAudioProcessor
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
/**
* An {@link AudioProcessor} that changes the speed of audio samples depending on their timestamp.
*/
/* package */
final
class
SpeedChangingAudioProcessor
extends
BaseAudioProcessor
{
/** The speed provider that provides the speed for each timestamp. */
private
final
SpeedProvider
speedProvider
;
/**
* The {@link SonicAudioProcessor} used to change the speed, when needed. If there is no speed
* change required, the input buffer is copied to the output buffer and this processor is not
* used.
*/
private
final
SonicAudioProcessor
sonicAudioProcessor
;
private
float
currentSpeed
;
private
long
bytesRead
;
private
boolean
endOfStreamQueuedToSonic
;
public
SpeedChangingAudioProcessor
(
SpeedProvider
speedProvider
)
{
this
.
speedProvider
=
speedProvider
;
sonicAudioProcessor
=
new
SonicAudioProcessor
();
currentSpeed
=
1
f
;
}
@Override
public
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledAudioFormatException
{
return
sonicAudioProcessor
.
configure
(
inputAudioFormat
);
}
@Override
public
void
queueInput
(
ByteBuffer
inputBuffer
)
{
long
timeUs
=
Util
.
scaleLargeTimestamp
(
/* timestamp= */
bytesRead
,
/* multiplier= */
C
.
MICROS_PER_SECOND
,
/* divisor= */
(
long
)
inputAudioFormat
.
sampleRate
*
inputAudioFormat
.
bytesPerFrame
);
float
newSpeed
=
speedProvider
.
getSpeed
(
timeUs
);
if
(
newSpeed
!=
currentSpeed
)
{
currentSpeed
=
newSpeed
;
if
(
isUsingSonic
())
{
sonicAudioProcessor
.
setSpeed
(
newSpeed
);
sonicAudioProcessor
.
setPitch
(
newSpeed
);
sonicAudioProcessor
.
flush
();
endOfStreamQueuedToSonic
=
false
;
}
}
int
inputBufferLimit
=
inputBuffer
.
limit
();
long
nextSpeedChangeTimeUs
=
speedProvider
.
getNextSpeedChangeTimeUs
(
timeUs
);
int
bytesToNextSpeedChange
;
if
(
nextSpeedChangeTimeUs
!=
C
.
TIME_UNSET
)
{
bytesToNextSpeedChange
=
(
int
)
Util
.
scaleLargeTimestamp
(
/* timestamp= */
nextSpeedChangeTimeUs
-
timeUs
,
/* multiplier= */
(
long
)
inputAudioFormat
.
sampleRate
*
inputAudioFormat
.
bytesPerFrame
,
/* divisor= */
C
.
MICROS_PER_SECOND
);
int
bytesToNextFrame
=
inputAudioFormat
.
bytesPerFrame
-
bytesToNextSpeedChange
%
inputAudioFormat
.
bytesPerFrame
;
if
(
bytesToNextFrame
!=
inputAudioFormat
.
bytesPerFrame
)
{
bytesToNextSpeedChange
+=
bytesToNextFrame
;
}
// Update the input buffer limit to make sure that all samples processed have the same speed.
inputBuffer
.
limit
(
min
(
inputBufferLimit
,
inputBuffer
.
position
()
+
bytesToNextSpeedChange
));
}
else
{
bytesToNextSpeedChange
=
C
.
LENGTH_UNSET
;
}
long
startPosition
=
inputBuffer
.
position
();
if
(
isUsingSonic
())
{
sonicAudioProcessor
.
queueInput
(
inputBuffer
);
if
(
bytesToNextSpeedChange
!=
C
.
LENGTH_UNSET
&&
(
inputBuffer
.
position
()
-
startPosition
)
==
bytesToNextSpeedChange
)
{
sonicAudioProcessor
.
queueEndOfStream
();
endOfStreamQueuedToSonic
=
true
;
}
}
else
{
ByteBuffer
buffer
=
replaceOutputBuffer
(
/* count= */
inputBuffer
.
remaining
());
buffer
.
put
(
inputBuffer
);
buffer
.
flip
();
}
bytesRead
+=
inputBuffer
.
position
()
-
startPosition
;
inputBuffer
.
limit
(
inputBufferLimit
);
}
@Override
protected
void
onQueueEndOfStream
()
{
if
(!
endOfStreamQueuedToSonic
)
{
sonicAudioProcessor
.
queueEndOfStream
();
endOfStreamQueuedToSonic
=
true
;
}
}
@Override
public
ByteBuffer
getOutput
()
{
return
isUsingSonic
()
?
sonicAudioProcessor
.
getOutput
()
:
super
.
getOutput
();
}
@Override
public
boolean
isEnded
()
{
return
super
.
isEnded
()
&&
sonicAudioProcessor
.
isEnded
();
}
@Override
protected
void
onFlush
()
{
sonicAudioProcessor
.
flush
();
endOfStreamQueuedToSonic
=
false
;
}
@Override
protected
void
onReset
()
{
currentSpeed
=
1
f
;
bytesRead
=
0
;
sonicAudioProcessor
.
reset
();
endOfStreamQueuedToSonic
=
false
;
}
private
boolean
isUsingSonic
()
{
return
currentSpeed
!=
1
f
;
}
}
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/SpeedProvider.java
View file @
327df95f
...
...
@@ -15,6 +15,8 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
com.google.android.exoplayer2.C
;
/** A custom interface that determines the speed for media at specific timestamps. */
/* package */
interface
SpeedProvider
{
...
...
@@ -25,4 +27,14 @@ package com.google.android.exoplayer2.transformer;
* @return The speed that the media should be played at, based on the timeUs.
*/
float
getSpeed
(
long
timeUs
);
/**
* Returns the timestamp of the next speed change, if there is any.
*
* @param timeUs A timestamp, in microseconds.
* @return The timestamp of the next speed change, in microseconds, or {@link C#TIME_UNSET} if
* there is no next speed change. If {@code timeUs} corresponds to a speed change, the
* returned value corresponds to the following speed change.
*/
long
getNextSpeedChangeTimeUs
(
long
timeUs
);
}
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