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
10f142b3
authored
Nov 26, 2019
by
andrewlewis
Committed by
Oliver Woodman
Nov 27, 2019
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add AudioProcessor.AudioFormat
Issue: #6601 PiperOrigin-RevId: 282515179
parent
b7000e64
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
259 additions
and
329 deletions
extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/BaseAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/ChannelMappingAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/audio/FloatResamplingAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/ResamplingAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/TeeAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/audio/TrimmingAudioProcessor.java
library/core/src/test/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessorTest.java
library/core/src/test/java/com/google/android/exoplayer2/audio/SonicAudioProcessorTest.java
extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java
View file @
10f142b3
...
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.ext.gvr;
...
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.ext.gvr;
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.ExoPlayerLibraryInfo
;
import
com.google.android.exoplayer2.ExoPlayerLibraryInfo
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.audio.AudioProcessor
;
import
com.google.android.exoplayer2.audio.AudioProcessor
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.vr.sdk.audio.GvrAudioSurround
;
import
com.google.vr.sdk.audio.GvrAudioSurround
;
...
@@ -44,8 +43,7 @@ public final class GvrAudioProcessor implements AudioProcessor {
...
@@ -44,8 +43,7 @@ public final class GvrAudioProcessor implements AudioProcessor {
private
static
final
int
OUTPUT_FRAME_SIZE
=
OUTPUT_CHANNEL_COUNT
*
2
;
// 16-bit stereo output.
private
static
final
int
OUTPUT_FRAME_SIZE
=
OUTPUT_CHANNEL_COUNT
*
2
;
// 16-bit stereo output.
private
static
final
int
NO_SURROUND_FORMAT
=
GvrAudioSurround
.
SurroundFormat
.
INVALID
;
private
static
final
int
NO_SURROUND_FORMAT
=
GvrAudioSurround
.
SurroundFormat
.
INVALID
;
private
int
sampleRateHz
;
private
AudioFormat
inputAudioFormat
;
private
int
channelCount
;
private
int
pendingGvrAudioSurroundFormat
;
private
int
pendingGvrAudioSurroundFormat
;
@Nullable
private
GvrAudioSurround
gvrAudioSurround
;
@Nullable
private
GvrAudioSurround
gvrAudioSurround
;
private
ByteBuffer
buffer
;
private
ByteBuffer
buffer
;
...
@@ -60,8 +58,7 @@ public final class GvrAudioProcessor implements AudioProcessor {
...
@@ -60,8 +58,7 @@ public final class GvrAudioProcessor implements AudioProcessor {
public
GvrAudioProcessor
()
{
public
GvrAudioProcessor
()
{
// Use the identity for the initial orientation.
// Use the identity for the initial orientation.
w
=
1
f
;
w
=
1
f
;
sampleRateHz
=
Format
.
NO_VALUE
;
inputAudioFormat
=
AudioFormat
.
NOT_SET
;
channelCount
=
Format
.
NO_VALUE
;
buffer
=
EMPTY_BUFFER
;
buffer
=
EMPTY_BUFFER
;
pendingGvrAudioSurroundFormat
=
NO_SURROUND_FORMAT
;
pendingGvrAudioSurroundFormat
=
NO_SURROUND_FORMAT
;
}
}
...
@@ -87,15 +84,13 @@ public final class GvrAudioProcessor implements AudioProcessor {
...
@@ -87,15 +84,13 @@ public final class GvrAudioProcessor implements AudioProcessor {
@SuppressWarnings
(
"ReferenceEquality"
)
@SuppressWarnings
(
"ReferenceEquality"
)
@Override
@Override
public
synchronized
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
Encoding
int
encoding
)
public
synchronized
AudioFormat
configure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledFormatException
{
throws
Unhandled
Audio
FormatException
{
if
(
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
if
(
inputAudioFormat
.
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
maybeReleaseGvrAudioSurround
();
maybeReleaseGvrAudioSurround
();
throw
new
Unhandled
FormatException
(
sampleRateHz
,
channelCount
,
encoding
);
throw
new
Unhandled
AudioFormatException
(
inputAudioFormat
);
}
}
this
.
sampleRateHz
=
sampleRateHz
;
switch
(
inputAudioFormat
.
channelCount
)
{
this
.
channelCount
=
channelCount
;
switch
(
channelCount
)
{
case
1
:
case
1
:
pendingGvrAudioSurroundFormat
=
GvrAudioSurround
.
SurroundFormat
.
SURROUND_MONO
;
pendingGvrAudioSurroundFormat
=
GvrAudioSurround
.
SurroundFormat
.
SURROUND_MONO
;
break
;
break
;
...
@@ -115,12 +110,14 @@ public final class GvrAudioProcessor implements AudioProcessor {
...
@@ -115,12 +110,14 @@ public final class GvrAudioProcessor implements AudioProcessor {
pendingGvrAudioSurroundFormat
=
GvrAudioSurround
.
SurroundFormat
.
THIRD_ORDER_AMBISONICS
;
pendingGvrAudioSurroundFormat
=
GvrAudioSurround
.
SurroundFormat
.
THIRD_ORDER_AMBISONICS
;
break
;
break
;
default
:
default
:
throw
new
Unhandled
FormatException
(
sampleRateHz
,
channelCount
,
encoding
);
throw
new
Unhandled
AudioFormatException
(
inputAudioFormat
);
}
}
if
(
buffer
==
EMPTY_BUFFER
)
{
if
(
buffer
==
EMPTY_BUFFER
)
{
buffer
=
ByteBuffer
.
allocateDirect
(
FRAMES_PER_OUTPUT_BUFFER
*
OUTPUT_FRAME_SIZE
)
buffer
=
ByteBuffer
.
allocateDirect
(
FRAMES_PER_OUTPUT_BUFFER
*
OUTPUT_FRAME_SIZE
)
.
order
(
ByteOrder
.
nativeOrder
());
.
order
(
ByteOrder
.
nativeOrder
());
}
}
this
.
inputAudioFormat
=
inputAudioFormat
;
return
new
AudioFormat
(
inputAudioFormat
.
sampleRate
,
OUTPUT_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
}
}
@Override
@Override
...
@@ -129,21 +126,6 @@ public final class GvrAudioProcessor implements AudioProcessor {
...
@@ -129,21 +126,6 @@ public final class GvrAudioProcessor implements AudioProcessor {
}
}
@Override
@Override
public
int
getOutputChannelCount
()
{
return
OUTPUT_CHANNEL_COUNT
;
}
@Override
public
int
getOutputEncoding
()
{
return
C
.
ENCODING_PCM_16BIT
;
}
@Override
public
int
getOutputSampleRateHz
()
{
return
sampleRateHz
;
}
@Override
public
void
queueInput
(
ByteBuffer
input
)
{
public
void
queueInput
(
ByteBuffer
input
)
{
int
position
=
input
.
position
();
int
position
=
input
.
position
();
Assertions
.
checkNotNull
(
gvrAudioSurround
);
Assertions
.
checkNotNull
(
gvrAudioSurround
);
...
@@ -181,7 +163,10 @@ public final class GvrAudioProcessor implements AudioProcessor {
...
@@ -181,7 +163,10 @@ public final class GvrAudioProcessor implements AudioProcessor {
maybeReleaseGvrAudioSurround
();
maybeReleaseGvrAudioSurround
();
gvrAudioSurround
=
gvrAudioSurround
=
new
GvrAudioSurround
(
new
GvrAudioSurround
(
pendingGvrAudioSurroundFormat
,
sampleRateHz
,
channelCount
,
FRAMES_PER_OUTPUT_BUFFER
);
pendingGvrAudioSurroundFormat
,
inputAudioFormat
.
sampleRate
,
inputAudioFormat
.
channelCount
,
FRAMES_PER_OUTPUT_BUFFER
);
gvrAudioSurround
.
updateNativeOrientation
(
w
,
x
,
y
,
z
);
gvrAudioSurround
.
updateNativeOrientation
(
w
,
x
,
y
,
z
);
pendingGvrAudioSurroundFormat
=
NO_SURROUND_FORMAT
;
pendingGvrAudioSurroundFormat
=
NO_SURROUND_FORMAT
;
}
else
if
(
gvrAudioSurround
!=
null
)
{
}
else
if
(
gvrAudioSurround
!=
null
)
{
...
@@ -195,8 +180,7 @@ public final class GvrAudioProcessor implements AudioProcessor {
...
@@ -195,8 +180,7 @@ public final class GvrAudioProcessor implements AudioProcessor {
maybeReleaseGvrAudioSurround
();
maybeReleaseGvrAudioSurround
();
updateOrientation
(
/* w= */
1
f
,
/* x= */
0
f
,
/* y= */
0
f
,
/* z= */
0
f
);
updateOrientation
(
/* w= */
1
f
,
/* x= */
0
f
,
/* y= */
0
f
,
/* z= */
0
f
);
inputEnded
=
false
;
inputEnded
=
false
;
sampleRateHz
=
Format
.
NO_VALUE
;
inputAudioFormat
=
AudioFormat
.
NOT_SET
;
channelCount
=
Format
.
NO_VALUE
;
buffer
=
EMPTY_BUFFER
;
buffer
=
EMPTY_BUFFER
;
pendingGvrAudioSurroundFormat
=
NO_SURROUND_FORMAT
;
pendingGvrAudioSurroundFormat
=
NO_SURROUND_FORMAT
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioProcessor.java
View file @
10f142b3
...
@@ -16,6 +16,8 @@
...
@@ -16,6 +16,8 @@
package
com
.
google
.
android
.
exoplayer2
.
audio
;
package
com
.
google
.
android
.
exoplayer2
.
audio
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.nio.ByteOrder
;
...
@@ -23,24 +25,56 @@ import java.nio.ByteOrder;
...
@@ -23,24 +25,56 @@ import java.nio.ByteOrder;
* Interface for audio processors, which take audio data as input and transform it, potentially
* Interface for audio processors, which take audio data as input and transform it, potentially
* modifying its channel count, encoding and/or sample rate.
* modifying its channel count, encoding and/or sample rate.
*
*
* <p>Call {@link #configure(int, int, int)} to configure the processor to receive input audio, then
* call {@link #isActive()} to determine whether the processor is active in the new configuration.
* {@link #queueInput(ByteBuffer)}, {@link #getOutputChannelCount()}, {@link #getOutputEncoding()}
* and {@link #getOutputSampleRateHz()} may only be called if the processor is active. Call {@link
* #reset()} to reset the processor to its unconfigured state and release any resources.
*
* <p>In addition to being able to modify the format of audio, implementations may allow parameters
* <p>In addition to being able to modify the format of audio, implementations may allow parameters
* to be set that affect the output audio and whether the processor is active/inactive.
* to be set that affect the output audio and whether the processor is active/inactive.
*/
*/
public
interface
AudioProcessor
{
public
interface
AudioProcessor
{
/** PCM audio format that may be handled by an audio processor. */
final
class
AudioFormat
{
public
static
final
AudioFormat
NOT_SET
=
new
AudioFormat
(
/* sampleRate= */
Format
.
NO_VALUE
,
/* channelCount= */
Format
.
NO_VALUE
,
/* encoding= */
Format
.
NO_VALUE
);
/** The sample rate in Hertz. */
public
final
int
sampleRate
;
/** The number of interleaved channels. */
public
final
int
channelCount
;
/** The type of linear PCM encoding. */
@C
.
PcmEncoding
public
final
int
encoding
;
/** The number of bytes used to represent one audio frame. */
public
final
int
bytesPerFrame
;
public
AudioFormat
(
int
sampleRate
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
{
this
.
sampleRate
=
sampleRate
;
this
.
channelCount
=
channelCount
;
this
.
encoding
=
encoding
;
bytesPerFrame
=
Util
.
isEncodingLinearPcm
(
encoding
)
?
Util
.
getPcmFrameSize
(
encoding
,
channelCount
)
:
Format
.
NO_VALUE
;
}
@Override
public
String
toString
()
{
return
"AudioFormat["
+
"sampleRate="
+
sampleRate
+
", channelCount="
+
channelCount
+
", encoding="
+
encoding
+
']'
;
}
}
/** Exception thrown when a processor can't be configured for a given input audio format. */
/** Exception thrown when a processor can't be configured for a given input audio format. */
final
class
UnhandledFormatException
extends
Exception
{
final
class
Unhandled
Audio
FormatException
extends
Exception
{
public
UnhandledFormatException
(
public
UnhandledAudioFormatException
(
AudioFormat
inputAudioFormat
)
{
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
{
super
(
"Unhandled format: "
+
inputAudioFormat
);
super
(
"Unhandled format: "
+
sampleRateHz
+
" Hz, "
+
channelCount
+
" channels in encoding "
+
encoding
);
}
}
}
}
...
@@ -50,46 +84,24 @@ public interface AudioProcessor {
...
@@ -50,46 +84,24 @@ public interface AudioProcessor {
/**
/**
* Configures the processor to process input audio with the specified format. After calling this
* Configures the processor to process input audio with the specified format. After calling this
* method, call {@link #isActive()} to determine whether the audio processor is active.
* method, call {@link #isActive()} to determine whether the audio processor is active. Returns
*
* the configured output audio format if this instance is active.
* <p>If the audio processor is active after configuration, call {@link #getOutputSampleRateHz()},
* {@link #getOutputChannelCount()} and {@link #getOutputEncoding()} to get its new output format.
*
*
* <p>After calling this method, it is necessary to {@link #flush()} the processor to apply the
* <p>After calling this method, it is necessary to {@link #flush()} the processor to apply the
* new configuration before queueing more data. You can (optionally) first drain output in the
* new configuration before queueing more data. You can (optionally) first drain output in the
* previous configuration by calling {@link #queueEndOfStream()} and {@link #getOutput()}.
* previous configuration by calling {@link #queueEndOfStream()} and {@link #getOutput()}.
*
*
* @param
sampleRateHz The sample rate of input audio in Hz.
* @param
inputAudioFormat The format of audio that will be queued after the next call to {@link
*
@param channelCount The number of interleaved channels in input audio
.
*
#flush()}
.
* @
param encoding The encoding of input audio
.
* @
return The configured output audio format if this instance is {@link #isActive() active}
.
* @throws UnhandledFormatException Thrown if the specified format can't be handled as input.
* @throws Unhandled
Audio
FormatException Thrown if the specified format can't be handled as input.
*/
*/
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
AudioFormat
configure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledAudioFormatException
;
throws
UnhandledFormatException
;
/** Returns whether the processor is configured and will process input buffers. */
/** Returns whether the processor is configured and will process input buffers. */
boolean
isActive
();
boolean
isActive
();
/**
/**
* Returns the number of audio channels in the data output by the processor. The value may change
* as a result of calling {@link #configure(int, int, int)}.
*/
int
getOutputChannelCount
();
/**
* Returns the audio encoding used in the data output by the processor. The value may change as a
* result of calling {@link #configure(int, int, int)}.
*/
@C
.
PcmEncoding
int
getOutputEncoding
();
/**
* Returns the sample rate of audio output by the processor, in hertz. The value may change as a
* result of calling {@link #configure(int, int, int)}.
*/
int
getOutputSampleRateHz
();
/**
* Queues audio data between the position and limit of the input {@code buffer} for processing.
* Queues audio data between the position and limit of the input {@code buffer} for processing.
* {@code buffer} must be a direct byte buffer with native byte order. Its contents are treated as
* {@code buffer} must be a direct byte buffer with native byte order. Its contents are treated as
* read-only. Its position will be advanced by the number of bytes consumed (which may be zero).
* read-only. Its position will be advanced by the number of bytes consumed (which may be zero).
...
@@ -130,6 +142,6 @@ public interface AudioProcessor {
...
@@ -130,6 +142,6 @@ public interface AudioProcessor {
*/
*/
void
flush
();
void
flush
();
/** Resets the processor to its unconfigured state. */
/** Resets the processor to its unconfigured state
, releasing any resources
. */
void
reset
();
void
reset
();
}
}
library/core/src/main/java/com/google/android/exoplayer2/audio/BaseAudioProcessor.java
View file @
10f142b3
...
@@ -16,24 +16,20 @@
...
@@ -16,24 +16,20 @@
package
com
.
google
.
android
.
exoplayer2
.
audio
;
package
com
.
google
.
android
.
exoplayer2
.
audio
;
import
androidx.annotation.CallSuper
;
import
androidx.annotation.CallSuper
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.nio.ByteOrder
;
/**
/**
* Base class for audio processors that keep an output buffer and an internal buffer that is reused
* Base class for audio processors that keep an output buffer and an internal buffer that is reused
* whenever input is queued.
* whenever input is queued. Subclasses should override {@link #onConfigure(AudioFormat)} to return
* the output audio format for the processor if it's active.
*/
*/
public
abstract
class
BaseAudioProcessor
implements
AudioProcessor
{
public
abstract
class
BaseAudioProcessor
implements
AudioProcessor
{
/** The configured input sample rate, in Hertz, or {@link Format#NO_VALUE} if not configured. */
/** The configured input audio format. */
protected
int
sampleRateHz
;
protected
AudioFormat
inputAudioFormat
;
/** The configured input channel count, or {@link Format#NO_VALUE} if not configured. */
protected
int
channelCount
;
/** The configured input encoding, or {@link Format#NO_VALUE} if not configured. */
@C
.
PcmEncoding
protected
int
encoding
;
private
AudioFormat
outputAudioFormat
;
private
ByteBuffer
buffer
;
private
ByteBuffer
buffer
;
private
ByteBuffer
outputBuffer
;
private
ByteBuffer
outputBuffer
;
private
boolean
inputEnded
;
private
boolean
inputEnded
;
...
@@ -41,29 +37,21 @@ public abstract class BaseAudioProcessor implements AudioProcessor {
...
@@ -41,29 +37,21 @@ public abstract class BaseAudioProcessor implements AudioProcessor {
public
BaseAudioProcessor
()
{
public
BaseAudioProcessor
()
{
buffer
=
EMPTY_BUFFER
;
buffer
=
EMPTY_BUFFER
;
outputBuffer
=
EMPTY_BUFFER
;
outputBuffer
=
EMPTY_BUFFER
;
channelCount
=
Format
.
NO_VALUE
;
inputAudioFormat
=
AudioFormat
.
NOT_SET
;
sampleRateHz
=
Format
.
NO_VALUE
;
outputAudioFormat
=
AudioFormat
.
NOT_SET
;
encoding
=
Format
.
NO_VALUE
;
}
}
@Override
@Override
public
boolean
isActive
()
{
public
final
AudioFormat
configure
(
AudioFormat
inputAudioFormat
)
return
sampleRateHz
!=
Format
.
NO_VALUE
;
throws
UnhandledAudioFormatException
{
}
this
.
inputAudioFormat
=
inputAudioFormat
;
outputAudioFormat
=
onConfigure
(
inputAudioFormat
);
@Override
return
isActive
()
?
outputAudioFormat
:
AudioFormat
.
NOT_SET
;
public
int
getOutputChannelCount
()
{
return
channelCount
;
}
@Override
public
int
getOutputEncoding
()
{
return
encoding
;
}
}
@Override
@Override
public
int
getOutputSampleRateHz
()
{
public
boolean
isActive
()
{
return
sampleRateHz
;
return
outputAudioFormat
!=
AudioFormat
.
NOT_SET
;
}
}
@Override
@Override
...
@@ -98,20 +86,11 @@ public abstract class BaseAudioProcessor implements AudioProcessor {
...
@@ -98,20 +86,11 @@ public abstract class BaseAudioProcessor implements AudioProcessor {
public
final
void
reset
()
{
public
final
void
reset
()
{
flush
();
flush
();
buffer
=
EMPTY_BUFFER
;
buffer
=
EMPTY_BUFFER
;
sampleRateHz
=
Format
.
NO_VALUE
;
inputAudioFormat
=
AudioFormat
.
NOT_SET
;
channelCount
=
Format
.
NO_VALUE
;
outputAudioFormat
=
AudioFormat
.
NOT_SET
;
encoding
=
Format
.
NO_VALUE
;
onReset
();
onReset
();
}
}
/** Sets the input format of this processor. */
protected
final
void
setInputFormat
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
{
this
.
sampleRateHz
=
sampleRateHz
;
this
.
channelCount
=
channelCount
;
this
.
encoding
=
encoding
;
}
/**
/**
* Replaces the current output buffer with a buffer of at least {@code count} bytes and returns
* Replaces the current output buffer with a buffer of at least {@code count} bytes and returns
* it. Callers should write to the returned buffer then {@link ByteBuffer#flip()} it so it can be
* it. Callers should write to the returned buffer then {@link ByteBuffer#flip()} it so it can be
...
@@ -132,6 +111,12 @@ public abstract class BaseAudioProcessor implements AudioProcessor {
...
@@ -132,6 +111,12 @@ public abstract class BaseAudioProcessor implements AudioProcessor {
return
outputBuffer
.
hasRemaining
();
return
outputBuffer
.
hasRemaining
();
}
}
/** Called when the processor is configured for a new input format. */
protected
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledAudioFormatException
{
return
AudioFormat
.
NOT_SET
;
}
/** Called when the end-of-stream is queued to the processor. */
/** Called when the end-of-stream is queued to the processor. */
protected
void
onQueueEndOfStream
()
{
protected
void
onQueueEndOfStream
()
{
// Do nothing.
// Do nothing.
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/ChannelMappingAudioProcessor.java
View file @
10f142b3
...
@@ -24,19 +24,17 @@ import java.nio.ByteBuffer;
...
@@ -24,19 +24,17 @@ import java.nio.ByteBuffer;
* An {@link AudioProcessor} that applies a mapping from input channels onto specified output
* An {@link AudioProcessor} that applies a mapping from input channels onto specified output
* channels. This can be used to reorder, duplicate or discard channels.
* channels. This can be used to reorder, duplicate or discard channels.
*/
*/
/* package */
// the constructor does not initialize fields: pendingOutputChannels, outputChannels
// the constructor does not initialize fields: pendingOutputChannels, outputChannels
@SuppressWarnings
(
"nullness:initialization.fields.uninitialized"
)
@SuppressWarnings
(
"nullness:initialization.fields.uninitialized"
)
final
class
ChannelMappingAudioProcessor
extends
BaseAudioProcessor
{
/* package */
final
class
ChannelMappingAudioProcessor
extends
BaseAudioProcessor
{
@Nullable
private
int
[]
pendingOutputChannels
;
@Nullable
private
int
[]
pendingOutputChannels
;
private
boolean
active
;
@Nullable
private
int
[]
outputChannels
;
@Nullable
private
int
[]
outputChannels
;
/**
/**
* Resets the channel mapping. After calling this method, call {@link #configure(
int, int, int)}
* Resets the channel mapping. After calling this method, call {@link #configure(
AudioFormat)} to
*
to
start using the new channel map.
* start using the new channel map.
*
*
* @param outputChannels The mapping from input to output channel indices, or {@code null} to
* @param outputChannels The mapping from input to output channel indices, or {@code null} to
* leave the input unchanged.
* leave the input unchanged.
...
@@ -47,38 +45,30 @@ final class ChannelMappingAudioProcessor extends BaseAudioProcessor {
...
@@ -47,38 +45,30 @@ final class ChannelMappingAudioProcessor extends BaseAudioProcessor {
}
}
@Override
@Override
public
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
public
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledFormatException
{
throws
Unhandled
Audio
FormatException
{
outputChannels
=
pendingOutputChannels
;
outputChannels
=
pendingOutputChannels
;
int
[]
outputChannels
=
this
.
outputChannels
;
int
[]
outputChannels
=
this
.
outputChannels
;
if
(
outputChannels
==
null
)
{
if
(
outputChannels
==
null
)
{
active
=
false
;
return
AudioFormat
.
NOT_SET
;
return
;
}
}
if
(
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
throw
new
UnhandledFormatException
(
sampleRateHz
,
channelCount
,
encoding
);
if
(
inputAudioFormat
.
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
throw
new
UnhandledAudioFormatException
(
inputAudioFormat
);
}
}
setInputFormat
(
sampleRateHz
,
channelCount
,
encoding
);
boolean
active
=
inputAudioFormat
.
channelCount
!=
outputChannels
.
length
;
active
=
channelCount
!=
outputChannels
.
length
;
for
(
int
i
=
0
;
i
<
outputChannels
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
outputChannels
.
length
;
i
++)
{
int
channelIndex
=
outputChannels
[
i
];
int
channelIndex
=
outputChannels
[
i
];
if
(
channelIndex
>=
channelCount
)
{
if
(
channelIndex
>=
inputAudioFormat
.
channelCount
)
{
throw
new
Unhandled
FormatException
(
sampleRateHz
,
channelCount
,
encoding
);
throw
new
Unhandled
AudioFormatException
(
inputAudioFormat
);
}
}
active
|=
(
channelIndex
!=
i
);
active
|=
(
channelIndex
!=
i
);
}
}
}
return
active
?
new
AudioFormat
(
inputAudioFormat
.
sampleRate
,
outputChannels
.
length
,
C
.
ENCODING_PCM_16BIT
)
@Override
:
AudioFormat
.
NOT_SET
;
public
boolean
isActive
()
{
return
active
;
}
@Override
public
int
getOutputChannelCount
()
{
return
outputChannels
==
null
?
channelCount
:
outputChannels
.
length
;
}
}
@Override
@Override
...
@@ -86,14 +76,14 @@ final class ChannelMappingAudioProcessor extends BaseAudioProcessor {
...
@@ -86,14 +76,14 @@ final class ChannelMappingAudioProcessor extends BaseAudioProcessor {
int
[]
outputChannels
=
Assertions
.
checkNotNull
(
this
.
outputChannels
);
int
[]
outputChannels
=
Assertions
.
checkNotNull
(
this
.
outputChannels
);
int
position
=
inputBuffer
.
position
();
int
position
=
inputBuffer
.
position
();
int
limit
=
inputBuffer
.
limit
();
int
limit
=
inputBuffer
.
limit
();
int
frameCount
=
(
limit
-
position
)
/
(
2
*
channelCount
);
int
frameCount
=
(
limit
-
position
)
/
(
2
*
inputAudioFormat
.
channelCount
);
int
outputSize
=
frameCount
*
outputChannels
.
length
*
2
;
int
outputSize
=
frameCount
*
outputChannels
.
length
*
2
;
ByteBuffer
buffer
=
replaceOutputBuffer
(
outputSize
);
ByteBuffer
buffer
=
replaceOutputBuffer
(
outputSize
);
while
(
position
<
limit
)
{
while
(
position
<
limit
)
{
for
(
int
channelIndex
:
outputChannels
)
{
for
(
int
channelIndex
:
outputChannels
)
{
buffer
.
putShort
(
inputBuffer
.
getShort
(
position
+
2
*
channelIndex
));
buffer
.
putShort
(
inputBuffer
.
getShort
(
position
+
2
*
channelIndex
));
}
}
position
+=
channelCount
*
2
;
position
+=
inputAudioFormat
.
channelCount
*
2
;
}
}
inputBuffer
.
position
(
limit
);
inputBuffer
.
position
(
limit
);
buffer
.
flip
();
buffer
.
flip
();
...
@@ -103,7 +93,6 @@ final class ChannelMappingAudioProcessor extends BaseAudioProcessor {
...
@@ -103,7 +93,6 @@ final class ChannelMappingAudioProcessor extends BaseAudioProcessor {
protected
void
onReset
()
{
protected
void
onReset
()
{
outputChannels
=
null
;
outputChannels
=
null
;
pendingOutputChannels
=
null
;
pendingOutputChannels
=
null
;
active
=
false
;
}
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
View file @
10f142b3
...
@@ -27,6 +27,7 @@ import androidx.annotation.Nullable;
...
@@ -27,6 +27,7 @@ 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
;
import
com.google.android.exoplayer2.PlaybackParameters
;
import
com.google.android.exoplayer2.PlaybackParameters
;
import
com.google.android.exoplayer2.audio.AudioProcessor.UnhandledAudioFormatException
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
...
@@ -435,18 +436,22 @@ public final class DefaultAudioSink implements AudioSink {
...
@@ -435,18 +436,22 @@ public final class DefaultAudioSink implements AudioSink {
if
(
processingEnabled
)
{
if
(
processingEnabled
)
{
trimmingAudioProcessor
.
setTrimFrameCount
(
trimStartFrames
,
trimEndFrames
);
trimmingAudioProcessor
.
setTrimFrameCount
(
trimStartFrames
,
trimEndFrames
);
channelMappingAudioProcessor
.
setChannelMap
(
outputChannels
);
channelMappingAudioProcessor
.
setChannelMap
(
outputChannels
);
AudioProcessor
.
AudioFormat
inputAudioFormat
=
new
AudioProcessor
.
AudioFormat
(
sampleRate
,
channelCount
,
encoding
);
AudioProcessor
.
AudioFormat
outputAudioFormat
=
inputAudioFormat
;
for
(
AudioProcessor
audioProcessor
:
availableAudioProcessors
)
{
for
(
AudioProcessor
audioProcessor
:
availableAudioProcessors
)
{
try
{
try
{
audioProcessor
.
configure
(
sampleRate
,
channelCount
,
encoding
);
outputAudioFormat
=
audioProcessor
.
configure
(
inputAudioFormat
);
}
catch
(
AudioProcessor
.
Unhandled
FormatException
e
)
{
}
catch
(
UnhandledAudio
FormatException
e
)
{
throw
new
ConfigurationException
(
e
);
throw
new
ConfigurationException
(
e
);
}
}
if
(
audioProcessor
.
isActive
())
{
if
(
audioProcessor
.
isActive
())
{
channelCount
=
audioProcessor
.
getOutputChannelCount
();
inputAudioFormat
=
outputAudioFormat
;
sampleRate
=
audioProcessor
.
getOutputSampleRateHz
();
encoding
=
audioProcessor
.
getOutputEncoding
();
}
}
}
}
sampleRate
=
outputAudioFormat
.
sampleRate
;
channelCount
=
outputAudioFormat
.
channelCount
;
encoding
=
outputAudioFormat
.
encoding
;
}
}
int
outputChannelConfig
=
getChannelConfig
(
channelCount
,
isInputPcm
);
int
outputChannelConfig
=
getChannelConfig
(
channelCount
,
isInputPcm
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/FloatResamplingAudioProcessor.java
View file @
10f142b3
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
audio
;
package
com
.
google
.
android
.
exoplayer2
.
audio
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
...
@@ -29,27 +30,21 @@ import java.nio.ByteBuffer;
...
@@ -29,27 +30,21 @@ import java.nio.ByteBuffer;
private
static
final
double
PCM_32_BIT_INT_TO_PCM_32_BIT_FLOAT_FACTOR
=
1.0
/
0x7FFFFFFF
;
private
static
final
double
PCM_32_BIT_INT_TO_PCM_32_BIT_FLOAT_FACTOR
=
1.0
/
0x7FFFFFFF
;
@Override
@Override
public
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
public
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledFormatException
{
throws
Unhandled
Audio
FormatException
{
if
(!
Util
.
isEncodingHighResolutionIntegerPcm
(
encoding
))
{
if
(!
Util
.
isEncodingHighResolutionIntegerPcm
(
inputAudioFormat
.
encoding
))
{
throw
new
Unhandled
FormatException
(
sampleRateHz
,
channelCount
,
encoding
);
throw
new
Unhandled
AudioFormatException
(
inputAudioFormat
);
}
}
setInputFormat
(
sampleRateHz
,
channelCount
,
encoding
);
return
Util
.
isEncodingHighResolutionIntegerPcm
(
inputAudioFormat
.
encoding
)
}
?
new
AudioFormat
(
inputAudioFormat
.
sampleRate
,
inputAudioFormat
.
channelCount
,
C
.
ENCODING_PCM_FLOAT
)
@Override
:
AudioFormat
.
NOT_SET
;
public
boolean
isActive
()
{
return
Util
.
isEncodingHighResolutionIntegerPcm
(
encoding
);
}
@Override
public
int
getOutputEncoding
()
{
return
C
.
ENCODING_PCM_FLOAT
;
}
}
@Override
@Override
public
void
queueInput
(
ByteBuffer
inputBuffer
)
{
public
void
queueInput
(
ByteBuffer
inputBuffer
)
{
boolean
isInput32Bit
=
encoding
==
C
.
ENCODING_PCM_32BIT
;
Assertions
.
checkState
(
Util
.
isEncodingHighResolutionIntegerPcm
(
inputAudioFormat
.
encoding
));
boolean
isInput32Bit
=
inputAudioFormat
.
encoding
==
C
.
ENCODING_PCM_32BIT
;
int
position
=
inputBuffer
.
position
();
int
position
=
inputBuffer
.
position
();
int
limit
=
inputBuffer
.
limit
();
int
limit
=
inputBuffer
.
limit
();
int
size
=
limit
-
position
;
int
size
=
limit
-
position
;
...
@@ -65,7 +60,7 @@ import java.nio.ByteBuffer;
...
@@ -65,7 +60,7 @@ import java.nio.ByteBuffer;
|
((
inputBuffer
.
get
(
i
+
3
)
&
0xFF
)
<<
24
);
|
((
inputBuffer
.
get
(
i
+
3
)
&
0xFF
)
<<
24
);
writePcm32BitFloat
(
pcm32BitInteger
,
buffer
);
writePcm32BitFloat
(
pcm32BitInteger
,
buffer
);
}
}
}
else
{
}
else
{
// Input is 24-bit PCM.
for
(
int
i
=
position
;
i
<
limit
;
i
+=
3
)
{
for
(
int
i
=
position
;
i
<
limit
;
i
+=
3
)
{
int
pcm32BitInteger
=
int
pcm32BitInteger
=
((
inputBuffer
.
get
(
i
)
&
0xFF
)
<<
8
)
((
inputBuffer
.
get
(
i
)
&
0xFF
)
<<
8
)
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/ResamplingAudioProcessor.java
View file @
10f142b3
...
@@ -26,23 +26,17 @@ import java.nio.ByteBuffer;
...
@@ -26,23 +26,17 @@ import java.nio.ByteBuffer;
/* package */
final
class
ResamplingAudioProcessor
extends
BaseAudioProcessor
{
/* package */
final
class
ResamplingAudioProcessor
extends
BaseAudioProcessor
{
@Override
@Override
public
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
public
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledFormatException
{
throws
UnhandledAudioFormatException
{
@C
.
PcmEncoding
int
encoding
=
inputAudioFormat
.
encoding
;
if
(
encoding
!=
C
.
ENCODING_PCM_8BIT
&&
encoding
!=
C
.
ENCODING_PCM_16BIT
if
(
encoding
!=
C
.
ENCODING_PCM_8BIT
&&
encoding
!=
C
.
ENCODING_PCM_16BIT
&&
encoding
!=
C
.
ENCODING_PCM_24BIT
&&
encoding
!=
C
.
ENCODING_PCM_32BIT
)
{
&&
encoding
!=
C
.
ENCODING_PCM_24BIT
&&
encoding
!=
C
.
ENCODING_PCM_32BIT
)
{
throw
new
Unhandled
FormatException
(
sampleRateHz
,
channelCount
,
encoding
);
throw
new
Unhandled
AudioFormatException
(
inputAudioFormat
);
}
}
setInputFormat
(
sampleRateHz
,
channelCount
,
encoding
);
return
encoding
!=
C
.
ENCODING_PCM_16BIT
}
?
new
AudioFormat
(
inputAudioFormat
.
sampleRate
,
inputAudioFormat
.
channelCount
,
C
.
ENCODING_PCM_16BIT
)
@Override
:
AudioFormat
.
NOT_SET
;
public
boolean
isActive
()
{
return
encoding
!=
C
.
ENCODING_INVALID
&&
encoding
!=
C
.
ENCODING_PCM_16BIT
;
}
@Override
public
int
getOutputEncoding
()
{
return
C
.
ENCODING_PCM_16BIT
;
}
}
@Override
@Override
...
@@ -52,7 +46,7 @@ import java.nio.ByteBuffer;
...
@@ -52,7 +46,7 @@ import java.nio.ByteBuffer;
int
limit
=
inputBuffer
.
limit
();
int
limit
=
inputBuffer
.
limit
();
int
size
=
limit
-
position
;
int
size
=
limit
-
position
;
int
resampledSize
;
int
resampledSize
;
switch
(
encoding
)
{
switch
(
inputAudioFormat
.
encoding
)
{
case
C
.
ENCODING_PCM_8BIT
:
case
C
.
ENCODING_PCM_8BIT
:
resampledSize
=
size
*
2
;
resampledSize
=
size
*
2
;
break
;
break
;
...
@@ -74,7 +68,7 @@ import java.nio.ByteBuffer;
...
@@ -74,7 +68,7 @@ import java.nio.ByteBuffer;
// Resample the little endian input and update the input/output buffers.
// Resample the little endian input and update the input/output buffers.
ByteBuffer
buffer
=
replaceOutputBuffer
(
resampledSize
);
ByteBuffer
buffer
=
replaceOutputBuffer
(
resampledSize
);
switch
(
encoding
)
{
switch
(
inputAudioFormat
.
encoding
)
{
case
C
.
ENCODING_PCM_8BIT
:
case
C
.
ENCODING_PCM_8BIT
:
// 8->16 bit resampling. Shift each byte from [0, 256) to [-128, 128) and scale up.
// 8->16 bit resampling. Shift each byte from [0, 256) to [-128, 128) and scale up.
for
(
int
i
=
position
;
i
<
limit
;
i
++)
{
for
(
int
i
=
position
;
i
<
limit
;
i
++)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java
View file @
10f142b3
...
@@ -119,18 +119,17 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor {
...
@@ -119,18 +119,17 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor {
// AudioProcessor implementation.
// AudioProcessor implementation.
@Override
@Override
public
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
public
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledFormatException
{
throws
Unhandled
Audio
FormatException
{
if
(
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
if
(
inputAudioFormat
.
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
throw
new
Unhandled
FormatException
(
sampleRateHz
,
channelCount
,
encoding
);
throw
new
Unhandled
AudioFormatException
(
inputAudioFormat
);
}
}
bytesPerFrame
=
channelCount
*
2
;
return
enabled
?
inputAudioFormat
:
AudioFormat
.
NOT_SET
;
setInputFormat
(
sampleRateHz
,
channelCount
,
encoding
);
}
}
@Override
@Override
public
boolean
isActive
()
{
public
boolean
isActive
()
{
return
super
.
isActive
()
&&
enabled
;
return
enabled
;
}
}
@Override
@Override
...
@@ -165,7 +164,8 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor {
...
@@ -165,7 +164,8 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor {
@Override
@Override
protected
void
onFlush
()
{
protected
void
onFlush
()
{
if
(
isActive
())
{
if
(
enabled
)
{
bytesPerFrame
=
inputAudioFormat
.
bytesPerFrame
;
int
maybeSilenceBufferSize
=
durationUsToFrames
(
MINIMUM_SILENCE_DURATION_US
)
*
bytesPerFrame
;
int
maybeSilenceBufferSize
=
durationUsToFrames
(
MINIMUM_SILENCE_DURATION_US
)
*
bytesPerFrame
;
if
(
maybeSilenceBuffer
.
length
!=
maybeSilenceBufferSize
)
{
if
(
maybeSilenceBuffer
.
length
!=
maybeSilenceBufferSize
)
{
maybeSilenceBuffer
=
new
byte
[
maybeSilenceBufferSize
];
maybeSilenceBuffer
=
new
byte
[
maybeSilenceBufferSize
];
...
@@ -317,7 +317,7 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor {
...
@@ -317,7 +317,7 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor {
* Returns the number of input frames corresponding to {@code durationUs} microseconds of audio.
* Returns the number of input frames corresponding to {@code durationUs} microseconds of audio.
*/
*/
private
int
durationUsToFrames
(
long
durationUs
)
{
private
int
durationUsToFrames
(
long
durationUs
)
{
return
(
int
)
((
durationUs
*
sampleRateHz
)
/
C
.
MICROS_PER_SECOND
);
return
(
int
)
((
durationUs
*
inputAudioFormat
.
sampleRate
)
/
C
.
MICROS_PER_SECOND
);
}
}
/**
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java
View file @
10f142b3
...
@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.audio;
...
@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.audio;
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.C.Encoding
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
...
@@ -62,12 +61,12 @@ public final class SonicAudioProcessor implements AudioProcessor {
...
@@ -62,12 +61,12 @@ public final class SonicAudioProcessor implements AudioProcessor {
*/
*/
private
static
final
int
MIN_BYTES_FOR_SPEEDUP_CALCULATION
=
1024
;
private
static
final
int
MIN_BYTES_FOR_SPEEDUP_CALCULATION
=
1024
;
private
int
channelCount
;
private
int
pendingOutputSampleRate
;
private
int
sampleRateHz
;
private
float
speed
;
private
float
speed
;
private
float
pitch
;
private
float
pitch
;
private
int
outputSampleRateHz
;
private
int
pendingOutputSampleRateHz
;
private
AudioFormat
inputAudioFormat
;
private
AudioFormat
outputAudioFormat
;
private
boolean
pendingSonicRecreation
;
private
boolean
pendingSonicRecreation
;
@Nullable
private
Sonic
sonic
;
@Nullable
private
Sonic
sonic
;
...
@@ -84,13 +83,12 @@ public final class SonicAudioProcessor implements AudioProcessor {
...
@@ -84,13 +83,12 @@ public final class SonicAudioProcessor implements AudioProcessor {
public
SonicAudioProcessor
()
{
public
SonicAudioProcessor
()
{
speed
=
1
f
;
speed
=
1
f
;
pitch
=
1
f
;
pitch
=
1
f
;
channelCount
=
Format
.
NO_VALUE
;
inputAudioFormat
=
AudioFormat
.
NOT_SET
;
sampleRateHz
=
Format
.
NO_VALUE
;
outputAudioFormat
=
AudioFormat
.
NOT_SET
;
outputSampleRateHz
=
Format
.
NO_VALUE
;
buffer
=
EMPTY_BUFFER
;
buffer
=
EMPTY_BUFFER
;
shortBuffer
=
buffer
.
asShortBuffer
();
shortBuffer
=
buffer
.
asShortBuffer
();
outputBuffer
=
EMPTY_BUFFER
;
outputBuffer
=
EMPTY_BUFFER
;
pendingOutputSampleRate
Hz
=
SAMPLE_RATE_NO_CHANGE
;
pendingOutputSampleRate
=
SAMPLE_RATE_NO_CHANGE
;
}
}
/**
/**
...
@@ -129,14 +127,14 @@ public final class SonicAudioProcessor implements AudioProcessor {
...
@@ -129,14 +127,14 @@ public final class SonicAudioProcessor implements AudioProcessor {
/**
/**
* Sets the sample rate for output audio, in hertz. Pass {@link #SAMPLE_RATE_NO_CHANGE} to output
* Sets the sample rate for output audio, in hertz. Pass {@link #SAMPLE_RATE_NO_CHANGE} to output
* audio at the same sample rate as the input. After calling this method, call
* audio at the same sample rate as the input. After calling this method, call
{@link
*
{@link #configure(int, int, in
t)} to start using the new sample rate.
*
#configure(AudioForma
t)} to start using the new sample rate.
*
*
* @param sampleRateHz The sample rate for output audio, in hertz.
* @param sampleRateHz The sample rate for output audio, in hertz.
* @see #configure(
int, int, in
t)
* @see #configure(
AudioForma
t)
*/
*/
public
void
setOutputSampleRateHz
(
int
sampleRateHz
)
{
public
void
setOutputSampleRateHz
(
int
sampleRateHz
)
{
pendingOutputSampleRate
Hz
=
sampleRateHz
;
pendingOutputSampleRate
=
sampleRateHz
;
}
}
/**
/**
...
@@ -149,50 +147,39 @@ public final class SonicAudioProcessor implements AudioProcessor {
...
@@ -149,50 +147,39 @@ public final class SonicAudioProcessor implements AudioProcessor {
*/
*/
public
long
scaleDurationForSpeedup
(
long
duration
)
{
public
long
scaleDurationForSpeedup
(
long
duration
)
{
if
(
outputBytes
>=
MIN_BYTES_FOR_SPEEDUP_CALCULATION
)
{
if
(
outputBytes
>=
MIN_BYTES_FOR_SPEEDUP_CALCULATION
)
{
return
output
SampleRateHz
==
sampleRateHz
return
output
AudioFormat
.
sampleRate
==
inputAudioFormat
.
sampleRate
?
Util
.
scaleLargeTimestamp
(
duration
,
inputBytes
,
outputBytes
)
?
Util
.
scaleLargeTimestamp
(
duration
,
inputBytes
,
outputBytes
)
:
Util
.
scaleLargeTimestamp
(
duration
,
inputBytes
*
outputSampleRateHz
,
:
Util
.
scaleLargeTimestamp
(
outputBytes
*
sampleRateHz
);
duration
,
inputBytes
*
outputAudioFormat
.
sampleRate
,
outputBytes
*
inputAudioFormat
.
sampleRate
);
}
else
{
}
else
{
return
(
long
)
((
double
)
speed
*
duration
);
return
(
long
)
((
double
)
speed
*
duration
);
}
}
}
}
@Override
@Override
public
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@Encoding
int
encoding
)
public
AudioFormat
configure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledAudioFormatException
{
throws
UnhandledFormatException
{
if
(
inputAudioFormat
.
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
if
(
encoding
!=
C
.
ENCODING_PCM_16BIT
)
{
throw
new
UnhandledAudioFormatException
(
inputAudioFormat
);
throw
new
UnhandledFormatException
(
sampleRateHz
,
channelCount
,
encoding
);
}
}
int
outputSampleRateHz
=
pendingOutputSampleRateHz
==
SAMPLE_RATE_NO_CHANGE
int
outputSampleRateHz
=
?
sampleRateHz
:
pendingOutputSampleRateHz
;
pendingOutputSampleRate
==
SAMPLE_RATE_NO_CHANGE
this
.
sampleRateHz
=
sampleRateHz
;
?
inputAudioFormat
.
sampleRate
this
.
channelCount
=
channelCount
;
:
pendingOutputSampleRate
;
this
.
outputSampleRateHz
=
outputSampleRateHz
;
this
.
inputAudioFormat
=
inputAudioFormat
;
this
.
outputAudioFormat
=
new
AudioFormat
(
outputSampleRateHz
,
inputAudioFormat
.
channelCount
,
C
.
ENCODING_PCM_16BIT
);
pendingSonicRecreation
=
true
;
pendingSonicRecreation
=
true
;
return
outputAudioFormat
;
}
}
@Override
@Override
public
boolean
isActive
()
{
public
boolean
isActive
()
{
return
sampleRateHz
!=
Format
.
NO_VALUE
return
outputAudioFormat
.
sampleRate
!=
Format
.
NO_VALUE
&&
(
Math
.
abs
(
speed
-
1
f
)
>=
CLOSE_THRESHOLD
&&
(
Math
.
abs
(
speed
-
1
f
)
>=
CLOSE_THRESHOLD
||
Math
.
abs
(
pitch
-
1
f
)
>=
CLOSE_THRESHOLD
||
Math
.
abs
(
pitch
-
1
f
)
>=
CLOSE_THRESHOLD
||
outputSampleRateHz
!=
sampleRateHz
);
||
outputAudioFormat
.
sampleRate
!=
inputAudioFormat
.
sampleRate
);
}
@Override
public
int
getOutputChannelCount
()
{
return
channelCount
;
}
@Override
public
int
getOutputEncoding
()
{
return
C
.
ENCODING_PCM_16BIT
;
}
@Override
public
int
getOutputSampleRateHz
()
{
return
outputSampleRateHz
;
}
}
@Override
@Override
...
@@ -245,7 +232,13 @@ public final class SonicAudioProcessor implements AudioProcessor {
...
@@ -245,7 +232,13 @@ public final class SonicAudioProcessor implements AudioProcessor {
public
void
flush
()
{
public
void
flush
()
{
if
(
isActive
())
{
if
(
isActive
())
{
if
(
pendingSonicRecreation
)
{
if
(
pendingSonicRecreation
)
{
sonic
=
new
Sonic
(
sampleRateHz
,
channelCount
,
speed
,
pitch
,
outputSampleRateHz
);
sonic
=
new
Sonic
(
inputAudioFormat
.
sampleRate
,
inputAudioFormat
.
channelCount
,
speed
,
pitch
,
outputAudioFormat
.
sampleRate
);
}
else
if
(
sonic
!=
null
)
{
}
else
if
(
sonic
!=
null
)
{
sonic
.
flush
();
sonic
.
flush
();
}
}
...
@@ -260,13 +253,12 @@ public final class SonicAudioProcessor implements AudioProcessor {
...
@@ -260,13 +253,12 @@ public final class SonicAudioProcessor implements AudioProcessor {
public
void
reset
()
{
public
void
reset
()
{
speed
=
1
f
;
speed
=
1
f
;
pitch
=
1
f
;
pitch
=
1
f
;
channelCount
=
Format
.
NO_VALUE
;
inputAudioFormat
=
AudioFormat
.
NOT_SET
;
sampleRateHz
=
Format
.
NO_VALUE
;
outputAudioFormat
=
AudioFormat
.
NOT_SET
;
outputSampleRateHz
=
Format
.
NO_VALUE
;
buffer
=
EMPTY_BUFFER
;
buffer
=
EMPTY_BUFFER
;
shortBuffer
=
buffer
.
asShortBuffer
();
shortBuffer
=
buffer
.
asShortBuffer
();
outputBuffer
=
EMPTY_BUFFER
;
outputBuffer
=
EMPTY_BUFFER
;
pendingOutputSampleRate
Hz
=
SAMPLE_RATE_NO_CHANGE
;
pendingOutputSampleRate
=
SAMPLE_RATE_NO_CHANGE
;
pendingSonicRecreation
=
false
;
pendingSonicRecreation
=
false
;
sonic
=
null
;
sonic
=
null
;
inputBytes
=
0
;
inputBytes
=
0
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/TeeAudioProcessor.java
View file @
10f142b3
...
@@ -64,8 +64,9 @@ public final class TeeAudioProcessor extends BaseAudioProcessor {
...
@@ -64,8 +64,9 @@ public final class TeeAudioProcessor extends BaseAudioProcessor {
}
}
@Override
@Override
public
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
{
public
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
{
setInputFormat
(
sampleRateHz
,
channelCount
,
encoding
);
// This processor is always active (if passed to the sink) and outputs its input.
return
inputAudioFormat
;
}
}
@Override
@Override
...
@@ -81,7 +82,8 @@ public final class TeeAudioProcessor extends BaseAudioProcessor {
...
@@ -81,7 +82,8 @@ public final class TeeAudioProcessor extends BaseAudioProcessor {
@Override
@Override
protected
void
onFlush
()
{
protected
void
onFlush
()
{
if
(
isActive
())
{
if
(
isActive
())
{
audioBufferSink
.
flush
(
sampleRateHz
,
channelCount
,
encoding
);
audioBufferSink
.
flush
(
inputAudioFormat
.
sampleRate
,
inputAudioFormat
.
channelCount
,
inputAudioFormat
.
encoding
);
}
}
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/TrimmingAudioProcessor.java
View file @
10f142b3
...
@@ -24,7 +24,6 @@ import java.nio.ByteBuffer;
...
@@ -24,7 +24,6 @@ import java.nio.ByteBuffer;
@C
.
PcmEncoding
private
static
final
int
OUTPUT_ENCODING
=
C
.
ENCODING_PCM_16BIT
;
@C
.
PcmEncoding
private
static
final
int
OUTPUT_ENCODING
=
C
.
ENCODING_PCM_16BIT
;
private
boolean
isActive
;
private
int
trimStartFrames
;
private
int
trimStartFrames
;
private
int
trimEndFrames
;
private
int
trimEndFrames
;
private
int
bytesPerFrame
;
private
int
bytesPerFrame
;
...
@@ -42,7 +41,7 @@ import java.nio.ByteBuffer;
...
@@ -42,7 +41,7 @@ import java.nio.ByteBuffer;
/**
/**
* Sets the number of audio frames to trim from the start and end of audio passed to this
* Sets the number of audio frames to trim from the start and end of audio passed to this
* processor. After calling this method, call {@link #configure(
int, int, in
t)} to apply the new
* processor. After calling this method, call {@link #configure(
AudioForma
t)} to apply the new
* trimming frame counts.
* trimming frame counts.
*
*
* @param trimStartFrames The number of audio frames to trim from the start of audio.
* @param trimStartFrames The number of audio frames to trim from the start of audio.
...
@@ -68,26 +67,20 @@ import java.nio.ByteBuffer;
...
@@ -68,26 +67,20 @@ import java.nio.ByteBuffer;
}
}
@Override
@Override
public
void
configure
(
int
sampleRateHz
,
int
channelCount
,
@C
.
PcmEncoding
int
encoding
)
public
AudioFormat
onConfigure
(
AudioFormat
inputAudioFormat
)
throws
UnhandledFormatException
{
throws
Unhandled
Audio
FormatException
{
if
(
encoding
!=
OUTPUT_ENCODING
)
{
if
(
inputAudioFormat
.
encoding
!=
OUTPUT_ENCODING
)
{
throw
new
Unhandled
FormatException
(
sampleRateHz
,
channelCount
,
encoding
);
throw
new
Unhandled
AudioFormatException
(
inputAudioFormat
);
}
}
if
(
endBufferSize
>
0
)
{
if
(
endBufferSize
>
0
)
{
trimmedFrameCount
+=
endBufferSize
/
bytesPerFrame
;
trimmedFrameCount
+=
endBufferSize
/
bytesPerFrame
;
}
}
bytesPerFrame
=
Util
.
getPcmFrameSize
(
OUTPUT_ENCODING
,
channelCount
)
;
bytesPerFrame
=
inputAudioFormat
.
bytesPerFrame
;
endBuffer
=
new
byte
[
trimEndFrames
*
bytesPerFrame
];
endBuffer
=
new
byte
[
trimEndFrames
*
bytesPerFrame
];
endBufferSize
=
0
;
endBufferSize
=
0
;
pendingTrimStartBytes
=
trimStartFrames
*
bytesPerFrame
;
pendingTrimStartBytes
=
trimStartFrames
*
bytesPerFrame
;
isActive
=
trimStartFrames
!=
0
||
trimEndFrames
!=
0
;
receivedInputSinceConfigure
=
false
;
receivedInputSinceConfigure
=
false
;
setInputFormat
(
sampleRateHz
,
channelCount
,
encoding
);
return
trimStartFrames
!=
0
||
trimEndFrames
!=
0
?
inputAudioFormat
:
AudioFormat
.
NOT_SET
;
}
@Override
public
boolean
isActive
()
{
return
isActive
;
}
}
@Override
@Override
...
@@ -140,7 +133,6 @@ import java.nio.ByteBuffer;
...
@@ -140,7 +133,6 @@ import java.nio.ByteBuffer;
buffer
.
flip
();
buffer
.
flip
();
}
}
@SuppressWarnings
(
"ReferenceEquality"
)
@Override
@Override
public
ByteBuffer
getOutput
()
{
public
ByteBuffer
getOutput
()
{
if
(
super
.
isEnded
()
&&
endBufferSize
>
0
)
{
if
(
super
.
isEnded
()
&&
endBufferSize
>
0
)
{
...
@@ -155,7 +147,6 @@ import java.nio.ByteBuffer;
...
@@ -155,7 +147,6 @@ import java.nio.ByteBuffer;
return
super
.
getOutput
();
return
super
.
getOutput
();
}
}
@SuppressWarnings
(
"ReferenceEquality"
)
@Override
@Override
public
boolean
isEnded
()
{
public
boolean
isEnded
()
{
return
super
.
isEnded
()
&&
endBufferSize
==
0
;
return
super
.
isEnded
()
&&
endBufferSize
==
0
;
...
...
library/core/src/test/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessorTest.java
View file @
10f142b3
...
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
...
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.audio.AudioProcessor.AudioFormat
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.nio.ByteOrder
;
...
@@ -31,8 +32,9 @@ import org.junit.runner.RunWith;
...
@@ -31,8 +32,9 @@ import org.junit.runner.RunWith;
@RunWith
(
AndroidJUnit4
.
class
)
@RunWith
(
AndroidJUnit4
.
class
)
public
final
class
SilenceSkippingAudioProcessorTest
{
public
final
class
SilenceSkippingAudioProcessorTest
{
private
static
final
int
TEST_SIGNAL_SAMPLE_RATE_HZ
=
1000
;
private
static
final
AudioFormat
AUDIO_FORMAT
=
private
static
final
int
TEST_SIGNAL_CHANNEL_COUNT
=
2
;
new
AudioFormat
(
/* sampleRate= */
1000
,
/* channelCount= */
2
,
/* encoding= */
C
.
ENCODING_PCM_16BIT
);
private
static
final
int
TEST_SIGNAL_SILENCE_DURATION_MS
=
1000
;
private
static
final
int
TEST_SIGNAL_SILENCE_DURATION_MS
=
1000
;
private
static
final
int
TEST_SIGNAL_NOISE_DURATION_MS
=
1000
;
private
static
final
int
TEST_SIGNAL_NOISE_DURATION_MS
=
1000
;
private
static
final
int
TEST_SIGNAL_FRAME_COUNT
=
100000
;
private
static
final
int
TEST_SIGNAL_FRAME_COUNT
=
100000
;
...
@@ -52,8 +54,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -52,8 +54,7 @@ public final class SilenceSkippingAudioProcessorTest {
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
// When configuring it.
// When configuring it.
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
// It's active.
// It's active.
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
...
@@ -65,8 +66,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -65,8 +66,7 @@ public final class SilenceSkippingAudioProcessorTest {
silenceSkippingAudioProcessor
.
setEnabled
(
false
);
silenceSkippingAudioProcessor
.
setEnabled
(
false
);
// When configuring it.
// When configuring it.
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
// It's not active.
// It's not active.
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isFalse
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isFalse
();
...
@@ -76,8 +76,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -76,8 +76,7 @@ public final class SilenceSkippingAudioProcessorTest {
public
void
testDefaultProcessor_isNotEnabled
()
throws
Exception
{
public
void
testDefaultProcessor_isNotEnabled
()
throws
Exception
{
// Given a processor in its default state.
// Given a processor in its default state.
// When reconfigured.
// When reconfigured.
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
// It's not active.
// It's not active.
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isFalse
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isFalse
();
...
@@ -88,16 +87,13 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -88,16 +87,13 @@ public final class SilenceSkippingAudioProcessorTest {
// Given a signal with only noise.
// Given a signal with only noise.
InputBufferProvider
inputBufferProvider
=
InputBufferProvider
inputBufferProvider
=
getInputBufferProviderForAlternatingSilenceAndNoise
(
getInputBufferProviderForAlternatingSilenceAndNoise
(
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
/* noiseDurationMs= */
0
,
/* noiseDurationMs= */
0
,
TEST_SIGNAL_FRAME_COUNT
);
TEST_SIGNAL_FRAME_COUNT
);
// When processing the entire signal.
// When processing the entire signal.
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
silenceSkippingAudioProcessor
.
flush
();
silenceSkippingAudioProcessor
.
flush
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
long
totalOutputFrames
=
long
totalOutputFrames
=
...
@@ -113,8 +109,6 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -113,8 +109,6 @@ public final class SilenceSkippingAudioProcessorTest {
// Given a signal with only silence.
// Given a signal with only silence.
InputBufferProvider
inputBufferProvider
=
InputBufferProvider
inputBufferProvider
=
getInputBufferProviderForAlternatingSilenceAndNoise
(
getInputBufferProviderForAlternatingSilenceAndNoise
(
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
/* silenceDurationMs= */
0
,
/* silenceDurationMs= */
0
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_FRAME_COUNT
);
TEST_SIGNAL_FRAME_COUNT
);
...
@@ -123,8 +117,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -123,8 +117,7 @@ public final class SilenceSkippingAudioProcessorTest {
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
new
SilenceSkippingAudioProcessor
();
new
SilenceSkippingAudioProcessor
();
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
silenceSkippingAudioProcessor
.
flush
();
silenceSkippingAudioProcessor
.
flush
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
long
totalOutputFrames
=
long
totalOutputFrames
=
...
@@ -141,8 +134,6 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -141,8 +134,6 @@ public final class SilenceSkippingAudioProcessorTest {
// Given a signal that alternates between silence and noise.
// Given a signal that alternates between silence and noise.
InputBufferProvider
inputBufferProvider
=
InputBufferProvider
inputBufferProvider
=
getInputBufferProviderForAlternatingSilenceAndNoise
(
getInputBufferProviderForAlternatingSilenceAndNoise
(
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_FRAME_COUNT
);
TEST_SIGNAL_FRAME_COUNT
);
...
@@ -151,8 +142,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -151,8 +142,7 @@ public final class SilenceSkippingAudioProcessorTest {
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
new
SilenceSkippingAudioProcessor
();
new
SilenceSkippingAudioProcessor
();
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
silenceSkippingAudioProcessor
.
flush
();
silenceSkippingAudioProcessor
.
flush
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
long
totalOutputFrames
=
long
totalOutputFrames
=
...
@@ -169,8 +159,6 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -169,8 +159,6 @@ public final class SilenceSkippingAudioProcessorTest {
// Given a signal that alternates between silence and noise.
// Given a signal that alternates between silence and noise.
InputBufferProvider
inputBufferProvider
=
InputBufferProvider
inputBufferProvider
=
getInputBufferProviderForAlternatingSilenceAndNoise
(
getInputBufferProviderForAlternatingSilenceAndNoise
(
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_FRAME_COUNT
);
TEST_SIGNAL_FRAME_COUNT
);
...
@@ -179,8 +167,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -179,8 +167,7 @@ public final class SilenceSkippingAudioProcessorTest {
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
new
SilenceSkippingAudioProcessor
();
new
SilenceSkippingAudioProcessor
();
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
silenceSkippingAudioProcessor
.
flush
();
silenceSkippingAudioProcessor
.
flush
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
long
totalOutputFrames
=
long
totalOutputFrames
=
...
@@ -197,8 +184,6 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -197,8 +184,6 @@ public final class SilenceSkippingAudioProcessorTest {
// Given a signal that alternates between silence and noise.
// Given a signal that alternates between silence and noise.
InputBufferProvider
inputBufferProvider
=
InputBufferProvider
inputBufferProvider
=
getInputBufferProviderForAlternatingSilenceAndNoise
(
getInputBufferProviderForAlternatingSilenceAndNoise
(
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_FRAME_COUNT
);
TEST_SIGNAL_FRAME_COUNT
);
...
@@ -207,8 +192,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -207,8 +192,7 @@ public final class SilenceSkippingAudioProcessorTest {
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
new
SilenceSkippingAudioProcessor
();
new
SilenceSkippingAudioProcessor
();
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
silenceSkippingAudioProcessor
.
flush
();
silenceSkippingAudioProcessor
.
flush
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
long
totalOutputFrames
=
long
totalOutputFrames
=
...
@@ -224,8 +208,6 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -224,8 +208,6 @@ public final class SilenceSkippingAudioProcessorTest {
// Given a signal that alternates between silence and noise.
// Given a signal that alternates between silence and noise.
InputBufferProvider
inputBufferProvider
=
InputBufferProvider
inputBufferProvider
=
getInputBufferProviderForAlternatingSilenceAndNoise
(
getInputBufferProviderForAlternatingSilenceAndNoise
(
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_SILENCE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_NOISE_DURATION_MS
,
TEST_SIGNAL_FRAME_COUNT
);
TEST_SIGNAL_FRAME_COUNT
);
...
@@ -234,8 +216,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -234,8 +216,7 @@ public final class SilenceSkippingAudioProcessorTest {
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
SilenceSkippingAudioProcessor
silenceSkippingAudioProcessor
=
new
SilenceSkippingAudioProcessor
();
new
SilenceSkippingAudioProcessor
();
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
setEnabled
(
true
);
silenceSkippingAudioProcessor
.
configure
(
silenceSkippingAudioProcessor
.
configure
(
AUDIO_FORMAT
);
TEST_SIGNAL_SAMPLE_RATE_HZ
,
TEST_SIGNAL_CHANNEL_COUNT
,
C
.
ENCODING_PCM_16BIT
);
silenceSkippingAudioProcessor
.
flush
();
silenceSkippingAudioProcessor
.
flush
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
silenceSkippingAudioProcessor
.
isActive
()).
isTrue
();
process
(
silenceSkippingAudioProcessor
,
inputBufferProvider
,
INPUT_BUFFER_SIZE
);
process
(
silenceSkippingAudioProcessor
,
inputBufferProvider
,
INPUT_BUFFER_SIZE
);
...
@@ -253,6 +234,7 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -253,6 +234,7 @@ public final class SilenceSkippingAudioProcessorTest {
SilenceSkippingAudioProcessor
processor
,
SilenceSkippingAudioProcessor
processor
,
InputBufferProvider
inputBufferProvider
,
InputBufferProvider
inputBufferProvider
,
int
inputBufferSize
)
{
int
inputBufferSize
)
{
int
bytesPerFrame
=
AUDIO_FORMAT
.
bytesPerFrame
;
processor
.
flush
();
processor
.
flush
();
long
totalOutputFrames
=
0
;
long
totalOutputFrames
=
0
;
while
(
inputBufferProvider
.
hasRemaining
())
{
while
(
inputBufferProvider
.
hasRemaining
())
{
...
@@ -260,14 +242,14 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -260,14 +242,14 @@ public final class SilenceSkippingAudioProcessorTest {
while
(
inputBuffer
.
hasRemaining
())
{
while
(
inputBuffer
.
hasRemaining
())
{
processor
.
queueInput
(
inputBuffer
);
processor
.
queueInput
(
inputBuffer
);
ByteBuffer
outputBuffer
=
processor
.
getOutput
();
ByteBuffer
outputBuffer
=
processor
.
getOutput
();
totalOutputFrames
+=
outputBuffer
.
remaining
()
/
(
2
*
processor
.
getOutputChannelCount
())
;
totalOutputFrames
+=
outputBuffer
.
remaining
()
/
bytesPerFrame
;
outputBuffer
.
clear
();
outputBuffer
.
clear
();
}
}
}
}
processor
.
queueEndOfStream
();
processor
.
queueEndOfStream
();
while
(!
processor
.
isEnded
())
{
while
(!
processor
.
isEnded
())
{
ByteBuffer
outputBuffer
=
processor
.
getOutput
();
ByteBuffer
outputBuffer
=
processor
.
getOutput
();
totalOutputFrames
+=
outputBuffer
.
remaining
()
/
(
2
*
processor
.
getOutputChannelCount
())
;
totalOutputFrames
+=
outputBuffer
.
remaining
()
/
bytesPerFrame
;
outputBuffer
.
clear
();
outputBuffer
.
clear
();
}
}
return
totalOutputFrames
;
return
totalOutputFrames
;
...
@@ -278,16 +260,16 @@ public final class SilenceSkippingAudioProcessorTest {
...
@@ -278,16 +260,16 @@ public final class SilenceSkippingAudioProcessorTest {
* between silence/noise of the specified durations to fill {@code totalFrameCount}.
* between silence/noise of the specified durations to fill {@code totalFrameCount}.
*/
*/
private
static
InputBufferProvider
getInputBufferProviderForAlternatingSilenceAndNoise
(
private
static
InputBufferProvider
getInputBufferProviderForAlternatingSilenceAndNoise
(
int
sampleRateHz
,
int
channelCount
,
int
silenceDurationMs
,
int
silenceDurationMs
,
int
noiseDurationMs
,
int
noiseDurationMs
,
int
totalFrameCount
)
{
int
totalFrameCount
)
{
int
sampleRate
=
AUDIO_FORMAT
.
sampleRate
;
int
channelCount
=
AUDIO_FORMAT
.
channelCount
;
Pcm16BitAudioBuilder
audioBuilder
=
new
Pcm16BitAudioBuilder
(
channelCount
,
totalFrameCount
);
Pcm16BitAudioBuilder
audioBuilder
=
new
Pcm16BitAudioBuilder
(
channelCount
,
totalFrameCount
);
while
(!
audioBuilder
.
isFull
())
{
while
(!
audioBuilder
.
isFull
())
{
int
silenceDurationFrames
=
(
silenceDurationMs
*
sampleRate
Hz
)
/
1000
;
int
silenceDurationFrames
=
(
silenceDurationMs
*
sampleRate
)
/
1000
;
audioBuilder
.
appendFrames
(
/* count= */
silenceDurationFrames
,
/* channelLevels= */
(
short
)
0
);
audioBuilder
.
appendFrames
(
/* count= */
silenceDurationFrames
,
/* channelLevels= */
(
short
)
0
);
int
noiseDurationFrames
=
(
noiseDurationMs
*
sampleRate
Hz
)
/
1000
;
int
noiseDurationFrames
=
(
noiseDurationMs
*
sampleRate
)
/
1000
;
audioBuilder
.
appendFrames
(
audioBuilder
.
appendFrames
(
/* count= */
noiseDurationFrames
,
/* channelLevels= */
Short
.
MAX_VALUE
);
/* count= */
noiseDurationFrames
,
/* channelLevels= */
Short
.
MAX_VALUE
);
}
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/audio/SonicAudioProcessorTest.java
View file @
10f142b3
...
@@ -20,6 +20,8 @@ import static org.junit.Assert.fail;
...
@@ -20,6 +20,8 @@ import static org.junit.Assert.fail;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.audio.AudioProcessor.AudioFormat
;
import
com.google.android.exoplayer2.audio.AudioProcessor.UnhandledAudioFormatException
;
import
org.junit.Before
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.junit.runner.RunWith
;
...
@@ -28,6 +30,16 @@ import org.junit.runner.RunWith;
...
@@ -28,6 +30,16 @@ import org.junit.runner.RunWith;
@RunWith
(
AndroidJUnit4
.
class
)
@RunWith
(
AndroidJUnit4
.
class
)
public
final
class
SonicAudioProcessorTest
{
public
final
class
SonicAudioProcessorTest
{
private
static
final
AudioFormat
AUDIO_FORMAT_22050_HZ
=
new
AudioFormat
(
/* sampleRate= */
22050
,
/* channelCount= */
2
,
/* encoding= */
C
.
ENCODING_PCM_16BIT
);
private
static
final
AudioFormat
AUDIO_FORMAT_44100_HZ
=
new
AudioFormat
(
/* sampleRate= */
44100
,
/* channelCount= */
2
,
/* encoding= */
C
.
ENCODING_PCM_16BIT
);
private
static
final
AudioFormat
AUDIO_FORMAT_48000_HZ
=
new
AudioFormat
(
/* sampleRate= */
48000
,
/* channelCount= */
2
,
/* encoding= */
C
.
ENCODING_PCM_16BIT
);
private
SonicAudioProcessor
sonicAudioProcessor
;
private
SonicAudioProcessor
sonicAudioProcessor
;
@Before
@Before
...
@@ -39,59 +51,36 @@ public final class SonicAudioProcessorTest {
...
@@ -39,59 +51,36 @@ public final class SonicAudioProcessorTest {
public
void
testReconfigureWithSameSampleRate
()
throws
Exception
{
public
void
testReconfigureWithSameSampleRate
()
throws
Exception
{
// When configured for resampling from 44.1 kHz to 48 kHz, the output sample rate is correct.
// When configured for resampling from 44.1 kHz to 48 kHz, the output sample rate is correct.
sonicAudioProcessor
.
setOutputSampleRateHz
(
48000
);
sonicAudioProcessor
.
setOutputSampleRateHz
(
48000
);
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
AudioFormat
outputAudioFormat
=
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_44100_HZ
);
assertThat
(
sonicAudioProcessor
.
getOutputSampleRateHz
()).
isEqualTo
(
48000
);
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
outputAudioFormat
.
sampleRate
).
isEqualTo
(
48000
);
// When reconfigured with 48 kHz input, there is no resampling.
// When reconfigured with 48 kHz input, there is no resampling.
sonicAudioProcessor
.
configure
(
48000
,
2
,
C
.
ENCODING_PCM_16BIT
);
outputAudioFormat
=
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_48000_HZ
);
assertThat
(
sonicAudioProcessor
.
getOutputSampleRateHz
()).
isEqualTo
(
48000
);
assertThat
(
sonicAudioProcessor
.
isActive
()).
isFalse
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isFalse
();
assertThat
(
outputAudioFormat
.
sampleRate
).
isEqualTo
(
48000
);
// When reconfigure with 44.1 kHz input, resampling is enabled again.
// When reconfigure with 44.1 kHz input, resampling is enabled again.
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
outputAudioFormat
=
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_44100_HZ
);
assertThat
(
sonicAudioProcessor
.
getOutputSampleRateHz
()).
isEqualTo
(
48000
);
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
outputAudioFormat
.
sampleRate
).
isEqualTo
(
48000
);
}
}
@Test
@Test
public
void
testNoSampleRateChange
()
throws
Exception
{
public
void
testNoSampleRateChange
()
throws
Exception
{
// Configure for resampling 44.1 kHz to 48 kHz.
// Configure for resampling 44.1 kHz to 48 kHz.
sonicAudioProcessor
.
setOutputSampleRateHz
(
48000
);
sonicAudioProcessor
.
setOutputSampleRateHz
(
48000
);
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_44100_HZ
);
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
// Reconfigure to not modify the sample rate.
// Reconfigure to not modify the sample rate.
sonicAudioProcessor
.
setOutputSampleRateHz
(
SonicAudioProcessor
.
SAMPLE_RATE_NO_CHANGE
);
sonicAudioProcessor
.
setOutputSampleRateHz
(
SonicAudioProcessor
.
SAMPLE_RATE_NO_CHANGE
);
sonicAudioProcessor
.
configure
(
22050
,
2
,
C
.
ENCODING_PCM_16BIT
);
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_22050_HZ
);
// The sample rate is unmodified, and the audio processor is not active.
// The sample rate is unmodified, and the audio processor is not active.
assertThat
(
sonicAudioProcessor
.
getOutputSampleRateHz
()).
isEqualTo
(
22050
);
assertThat
(
sonicAudioProcessor
.
isActive
()).
isFalse
();
}
@Test
public
void
testBecomesActiveAfterConfigure
()
throws
Exception
{
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
// Set a new sample rate.
sonicAudioProcessor
.
setOutputSampleRateHz
(
22050
);
// The new sample rate is not active yet.
assertThat
(
sonicAudioProcessor
.
isActive
()).
isFalse
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isFalse
();
assertThat
(
sonicAudioProcessor
.
getOutputSampleRateHz
()).
isEqualTo
(
44100
);
}
@Test
public
void
testSampleRateChangeBecomesActiveAfterConfigure
()
throws
Exception
{
// Configure for resampling 44.1 kHz to 48 kHz.
sonicAudioProcessor
.
setOutputSampleRateHz
(
48000
);
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
// Set a new sample rate, which isn't active yet.
sonicAudioProcessor
.
setOutputSampleRateHz
(
22050
);
assertThat
(
sonicAudioProcessor
.
getOutputSampleRateHz
()).
isEqualTo
(
48000
);
// The new sample rate takes effect on reconfiguration.
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
assertThat
(
sonicAudioProcessor
.
getOutputSampleRateHz
()).
isEqualTo
(
22050
);
}
}
@Test
@Test
public
void
testIsActiveWithSpeedChange
()
throws
Exception
{
public
void
testIsActiveWithSpeedChange
()
throws
Exception
{
sonicAudioProcessor
.
setSpeed
(
1.5f
);
sonicAudioProcessor
.
setSpeed
(
1.5f
);
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_44100_HZ
);
sonicAudioProcessor
.
flush
();
sonicAudioProcessor
.
flush
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
}
}
...
@@ -99,35 +88,45 @@ public final class SonicAudioProcessorTest {
...
@@ -99,35 +88,45 @@ public final class SonicAudioProcessorTest {
@Test
@Test
public
void
testIsActiveWithPitchChange
()
throws
Exception
{
public
void
testIsActiveWithPitchChange
()
throws
Exception
{
sonicAudioProcessor
.
setPitch
(
1.5f
);
sonicAudioProcessor
.
setPitch
(
1.5f
);
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_44100_HZ
);
sonicAudioProcessor
.
flush
();
sonicAudioProcessor
.
flush
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isTrue
();
}
}
@Test
@Test
public
void
testIsNotActiveWithNoChange
()
throws
Exception
{
public
void
testIsNotActiveWithNoChange
()
throws
Exception
{
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_16BIT
);
sonicAudioProcessor
.
configure
(
AUDIO_FORMAT_44100_HZ
);
assertThat
(
sonicAudioProcessor
.
isActive
()).
isFalse
();
assertThat
(
sonicAudioProcessor
.
isActive
()).
isFalse
();
}
}
@Test
@Test
public
void
testDoesNotSupportNon16BitInput
()
throws
Exception
{
public
void
testDoesNotSupportNon16BitInput
()
throws
Exception
{
try
{
try
{
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_8BIT
);
sonicAudioProcessor
.
configure
(
new
AudioFormat
(
/* sampleRate= */
44100
,
/* channelCount= */
2
,
/* encoding= */
C
.
ENCODING_PCM_8BIT
));
fail
();
fail
();
}
catch
(
AudioProcessor
.
Unhandled
FormatException
e
)
{
}
catch
(
UnhandledAudio
FormatException
e
)
{
// Expected.
// Expected.
}
}
try
{
try
{
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_24BIT
);
sonicAudioProcessor
.
configure
(
new
AudioFormat
(
/* sampleRate= */
44100
,
/* channelCount= */
2
,
/* encoding= */
C
.
ENCODING_PCM_24BIT
));
fail
();
fail
();
}
catch
(
AudioProcessor
.
Unhandled
FormatException
e
)
{
}
catch
(
UnhandledAudio
FormatException
e
)
{
// Expected.
// Expected.
}
}
try
{
try
{
sonicAudioProcessor
.
configure
(
44100
,
2
,
C
.
ENCODING_PCM_32BIT
);
sonicAudioProcessor
.
configure
(
new
AudioFormat
(
/* sampleRate= */
44100
,
/* channelCount= */
2
,
/* encoding= */
C
.
ENCODING_PCM_32BIT
));
fail
();
fail
();
}
catch
(
AudioProcessor
.
Unhandled
FormatException
e
)
{
}
catch
(
UnhandledAudio
FormatException
e
)
{
// Expected.
// Expected.
}
}
}
}
...
...
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