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
5de56cd6
authored
Aug 06, 2020
by
olly
Committed by
kim-vde
Aug 07, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Opus: Add utility for handling header and initialization data
PiperOrigin-RevId: 325202386
parent
94fb9ade
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
248 additions
and
69 deletions
extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java
extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java
library/common/src/main/java/com/google/android/exoplayer2/audio/OpusUtil.java
library/common/src/test/java/com/google/android/exoplayer2/audio/OpusUtilTest.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java
extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java
View file @
5de56cd6
...
@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.audio.AudioRendererEventListener;
...
@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import
com.google.android.exoplayer2.audio.AudioSink
;
import
com.google.android.exoplayer2.audio.AudioSink
;
import
com.google.android.exoplayer2.audio.AudioSink.SinkFormatSupport
;
import
com.google.android.exoplayer2.audio.AudioSink.SinkFormatSupport
;
import
com.google.android.exoplayer2.audio.DecoderAudioRenderer
;
import
com.google.android.exoplayer2.audio.DecoderAudioRenderer
;
import
com.google.android.exoplayer2.audio.OpusUtil
;
import
com.google.android.exoplayer2.drm.ExoMediaCrypto
;
import
com.google.android.exoplayer2.drm.ExoMediaCrypto
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.TraceUtil
;
import
com.google.android.exoplayer2.util.TraceUtil
;
...
@@ -125,6 +126,6 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer<OpusDecoder> {
...
@@ -125,6 +126,6 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer<OpusDecoder> {
protected
Format
getOutputFormat
(
OpusDecoder
decoder
)
{
protected
Format
getOutputFormat
(
OpusDecoder
decoder
)
{
@C
.
PcmEncoding
@C
.
PcmEncoding
int
pcmEncoding
=
decoder
.
outputFloat
?
C
.
ENCODING_PCM_FLOAT
:
C
.
ENCODING_PCM_16BIT
;
int
pcmEncoding
=
decoder
.
outputFloat
?
C
.
ENCODING_PCM_FLOAT
:
C
.
ENCODING_PCM_16BIT
;
return
Util
.
getPcmFormat
(
pcmEncoding
,
decoder
.
channelCount
,
Opus
Decoder
.
SAMPLE_RATE
);
return
Util
.
getPcmFormat
(
pcmEncoding
,
decoder
.
channelCount
,
Opus
Util
.
SAMPLE_RATE
);
}
}
}
}
extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java
View file @
5de56cd6
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.opus;
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.opus;
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.audio.OpusUtil
;
import
com.google.android.exoplayer2.decoder.CryptoInfo
;
import
com.google.android.exoplayer2.decoder.CryptoInfo
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.decoder.SimpleDecoder
;
import
com.google.android.exoplayer2.decoder.SimpleDecoder
;
...
@@ -26,18 +27,12 @@ import com.google.android.exoplayer2.drm.ExoMediaCrypto;
...
@@ -26,18 +27,12 @@ import com.google.android.exoplayer2.drm.ExoMediaCrypto;
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
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.util.List
;
import
java.util.List
;
/** Opus decoder. */
/** Opus decoder. */
/* package */
final
class
OpusDecoder
/* package */
final
class
OpusDecoder
extends
SimpleDecoder
<
DecoderInputBuffer
,
SimpleOutputBuffer
,
OpusDecoderException
>
{
extends
SimpleDecoder
<
DecoderInputBuffer
,
SimpleOutputBuffer
,
OpusDecoderException
>
{
private
static
final
int
DEFAULT_SEEK_PRE_ROLL_SAMPLES
=
3840
;
/** Opus streams are always decoded at 48000 Hz. */
public
static
final
int
SAMPLE_RATE
=
48_000
;
private
static
final
int
NO_ERROR
=
0
;
private
static
final
int
NO_ERROR
=
0
;
private
static
final
int
DECODE_ERROR
=
-
1
;
private
static
final
int
DECODE_ERROR
=
-
1
;
private
static
final
int
DRM_ERROR
=
-
2
;
private
static
final
int
DRM_ERROR
=
-
2
;
...
@@ -46,9 +41,8 @@ import java.util.List;
...
@@ -46,9 +41,8 @@ import java.util.List;
public
final
int
channelCount
;
public
final
int
channelCount
;
@Nullable
private
final
ExoMediaCrypto
exoMediaCrypto
;
@Nullable
private
final
ExoMediaCrypto
exoMediaCrypto
;
private
final
int
preSkipSamples
;
private
final
int
headerSkipSamples
;
private
final
int
seekPreRollSamples
;
private
final
int
headerSeekPreRollSamples
;
private
final
long
nativeDecoderContext
;
private
final
long
nativeDecoderContext
;
private
int
skipSamples
;
private
int
skipSamples
;
...
@@ -77,21 +71,31 @@ import java.util.List;
...
@@ -77,21 +71,31 @@ import java.util.List;
throws
OpusDecoderException
{
throws
OpusDecoderException
{
super
(
new
DecoderInputBuffer
[
numInputBuffers
],
new
SimpleOutputBuffer
[
numOutputBuffers
]);
super
(
new
DecoderInputBuffer
[
numInputBuffers
],
new
SimpleOutputBuffer
[
numOutputBuffers
]);
if
(!
OpusLibrary
.
isAvailable
())
{
if
(!
OpusLibrary
.
isAvailable
())
{
throw
new
OpusDecoderException
(
"Failed to load decoder native libraries
.
"
);
throw
new
OpusDecoderException
(
"Failed to load decoder native libraries"
);
}
}
this
.
exoMediaCrypto
=
exoMediaCrypto
;
this
.
exoMediaCrypto
=
exoMediaCrypto
;
if
(
exoMediaCrypto
!=
null
&&
!
OpusLibrary
.
opusIsSecureDecodeSupported
())
{
if
(
exoMediaCrypto
!=
null
&&
!
OpusLibrary
.
opusIsSecureDecodeSupported
())
{
throw
new
OpusDecoderException
(
"Opus decoder does not support secure decode."
);
throw
new
OpusDecoderException
(
"Opus decoder does not support secure decode"
);
}
int
initializationDataSize
=
initializationData
.
size
();
if
(
initializationDataSize
!=
1
&&
initializationDataSize
!=
3
)
{
throw
new
OpusDecoderException
(
"Invalid initialization data size"
);
}
}
if
(
initializationDataSize
==
3
&&
(
initializationData
.
get
(
1
).
length
!=
8
||
initializationData
.
get
(
2
).
length
!=
8
))
{
throw
new
OpusDecoderException
(
"Invalid pre-skip or seek pre-roll"
);
}
preSkipSamples
=
OpusUtil
.
getPreSkipSamples
(
initializationData
);
seekPreRollSamples
=
OpusUtil
.
getSeekPreRollSamples
(
initializationData
);
byte
[]
headerBytes
=
initializationData
.
get
(
0
);
byte
[]
headerBytes
=
initializationData
.
get
(
0
);
if
(
headerBytes
.
length
<
19
)
{
if
(
headerBytes
.
length
<
19
)
{
throw
new
OpusDecoderException
(
"
Header size is too small.
"
);
throw
new
OpusDecoderException
(
"
Invalid header length
"
);
}
}
channelCount
=
headerBytes
[
9
]
&
0xFF
;
channelCount
=
OpusUtil
.
getChannelCount
(
headerBytes
)
;
if
(
channelCount
>
8
)
{
if
(
channelCount
>
8
)
{
throw
new
OpusDecoderException
(
"Invalid channel count: "
+
channelCount
);
throw
new
OpusDecoderException
(
"Invalid channel count: "
+
channelCount
);
}
}
int
preskip
=
readUnsignedLittleEndian16
(
headerBytes
,
10
);
int
gain
=
readSignedLittleEndian16
(
headerBytes
,
16
);
int
gain
=
readSignedLittleEndian16
(
headerBytes
,
16
);
byte
[]
streamMap
=
new
byte
[
8
];
byte
[]
streamMap
=
new
byte
[
8
];
...
@@ -100,7 +104,7 @@ import java.util.List;
...
@@ -100,7 +104,7 @@ import java.util.List;
if
(
headerBytes
[
18
]
==
0
)
{
// Channel mapping
if
(
headerBytes
[
18
]
==
0
)
{
// Channel mapping
// If there is no channel mapping, use the defaults.
// If there is no channel mapping, use the defaults.
if
(
channelCount
>
2
)
{
// Maximum channel count with default layout.
if
(
channelCount
>
2
)
{
// Maximum channel count with default layout.
throw
new
OpusDecoderException
(
"Invalid
Header, missing stream map.
"
);
throw
new
OpusDecoderException
(
"Invalid
header, missing stream map
"
);
}
}
numStreams
=
1
;
numStreams
=
1
;
numCoupled
=
(
channelCount
==
2
)
?
1
:
0
;
numCoupled
=
(
channelCount
==
2
)
?
1
:
0
;
...
@@ -108,29 +112,15 @@ import java.util.List;
...
@@ -108,29 +112,15 @@ import java.util.List;
streamMap
[
1
]
=
1
;
streamMap
[
1
]
=
1
;
}
else
{
}
else
{
if
(
headerBytes
.
length
<
21
+
channelCount
)
{
if
(
headerBytes
.
length
<
21
+
channelCount
)
{
throw
new
OpusDecoderException
(
"
Header size is too small.
"
);
throw
new
OpusDecoderException
(
"
Invalid header length
"
);
}
}
// Read the channel mapping.
// Read the channel mapping.
numStreams
=
headerBytes
[
19
]
&
0xFF
;
numStreams
=
headerBytes
[
19
]
&
0xFF
;
numCoupled
=
headerBytes
[
20
]
&
0xFF
;
numCoupled
=
headerBytes
[
20
]
&
0xFF
;
System
.
arraycopy
(
headerBytes
,
21
,
streamMap
,
0
,
channelCount
);
System
.
arraycopy
(
headerBytes
,
21
,
streamMap
,
0
,
channelCount
);
}
}
if
(
initializationData
.
size
()
==
3
)
{
if
(
initializationData
.
get
(
1
).
length
!=
8
||
initializationData
.
get
(
2
).
length
!=
8
)
{
throw
new
OpusDecoderException
(
"Invalid Codec Delay or Seek Preroll"
);
}
long
codecDelayNs
=
ByteBuffer
.
wrap
(
initializationData
.
get
(
1
)).
order
(
ByteOrder
.
nativeOrder
()).
getLong
();
long
seekPreRollNs
=
ByteBuffer
.
wrap
(
initializationData
.
get
(
2
)).
order
(
ByteOrder
.
nativeOrder
()).
getLong
();
headerSkipSamples
=
nsToSamples
(
codecDelayNs
);
headerSeekPreRollSamples
=
nsToSamples
(
seekPreRollNs
);
}
else
{
headerSkipSamples
=
preskip
;
headerSeekPreRollSamples
=
DEFAULT_SEEK_PRE_ROLL_SAMPLES
;
}
nativeDecoderContext
=
nativeDecoderContext
=
opusInit
(
SAMPLE_RATE
,
channelCount
,
numStreams
,
numCoupled
,
gain
,
streamMap
);
opusInit
(
OpusUtil
.
SAMPLE_RATE
,
channelCount
,
numStreams
,
numCoupled
,
gain
,
streamMap
);
if
(
nativeDecoderContext
==
0
)
{
if
(
nativeDecoderContext
==
0
)
{
throw
new
OpusDecoderException
(
"Failed to initialize decoder"
);
throw
new
OpusDecoderException
(
"Failed to initialize decoder"
);
}
}
...
@@ -170,7 +160,7 @@ import java.util.List;
...
@@ -170,7 +160,7 @@ import java.util.List;
opusReset
(
nativeDecoderContext
);
opusReset
(
nativeDecoderContext
);
// When seeking to 0, skip number of samples as specified in opus header. When seeking to
// When seeking to 0, skip number of samples as specified in opus header. When seeking to
// any other time, skip number of samples as specified by seek preroll.
// any other time, skip number of samples as specified by seek preroll.
skipSamples
=
(
inputBuffer
.
timeUs
==
0
)
?
headerSkipSamples
:
headerS
eekPreRollSamples
;
skipSamples
=
(
inputBuffer
.
timeUs
==
0
)
?
preSkipSamples
:
s
eekPreRollSamples
;
}
}
ByteBuffer
inputData
=
Util
.
castNonNull
(
inputBuffer
.
data
);
ByteBuffer
inputData
=
Util
.
castNonNull
(
inputBuffer
.
data
);
CryptoInfo
cryptoInfo
=
inputBuffer
.
cryptoInfo
;
CryptoInfo
cryptoInfo
=
inputBuffer
.
cryptoInfo
;
...
@@ -182,7 +172,7 @@ import java.util.List;
...
@@ -182,7 +172,7 @@ import java.util.List;
inputData
,
inputData
,
inputData
.
limit
(),
inputData
.
limit
(),
outputBuffer
,
outputBuffer
,
SAMPLE_RATE
,
OpusUtil
.
SAMPLE_RATE
,
exoMediaCrypto
,
exoMediaCrypto
,
cryptoInfo
.
mode
,
cryptoInfo
.
mode
,
Assertions
.
checkNotNull
(
cryptoInfo
.
key
),
Assertions
.
checkNotNull
(
cryptoInfo
.
key
),
...
@@ -231,18 +221,10 @@ import java.util.List;
...
@@ -231,18 +221,10 @@ import java.util.List;
opusClose
(
nativeDecoderContext
);
opusClose
(
nativeDecoderContext
);
}
}
private
static
int
nsToSamples
(
long
ns
)
{
private
static
int
readSignedLittleEndian16
(
byte
[]
input
,
int
offset
)
{
return
(
int
)
(
ns
*
SAMPLE_RATE
/
1000000000
);
}
private
static
int
readUnsignedLittleEndian16
(
byte
[]
input
,
int
offset
)
{
int
value
=
input
[
offset
]
&
0xFF
;
int
value
=
input
[
offset
]
&
0xFF
;
value
|=
(
input
[
offset
+
1
]
&
0xFF
)
<<
8
;
value
|=
(
input
[
offset
+
1
]
&
0xFF
)
<<
8
;
return
value
;
return
(
short
)
value
;
}
private
static
int
readSignedLittleEndian16
(
byte
[]
input
,
int
offset
)
{
return
(
short
)
readUnsignedLittleEndian16
(
input
,
offset
);
}
}
private
native
long
opusInit
(
private
native
long
opusInit
(
...
...
library/common/src/main/java/com/google/android/exoplayer2/audio/OpusUtil.java
0 → 100644
View file @
5de56cd6
/*
* Copyright (C) 2020 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
.
audio
;
import
com.google.android.exoplayer2.C
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.util.ArrayList
;
import
java.util.List
;
/** Utility methods for handling Opus audio streams. */
public
class
OpusUtil
{
/** Opus streams are always 48000 Hz. */
public
static
final
int
SAMPLE_RATE
=
48_000
;
private
static
final
int
DEFAULT_SEEK_PRE_ROLL_SAMPLES
=
3840
;
private
static
final
int
FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT
=
3
;
private
OpusUtil
()
{}
// Prevents instantiation.
/**
* Parses the channel count from an Opus Identification Header.
*
* @param header An Opus Identification Header, as defined by RFC 7845.
* @return The parsed channel count.
*/
public
static
int
getChannelCount
(
byte
[]
header
)
{
return
header
[
9
]
&
0xFF
;
}
/**
* Builds codec initialization data from an Opus Identification Header.
*
* @param header An Opus Identification Header, as defined by RFC 7845.
* @return Codec initialization data suitable for an Opus <a
* href="https://developer.android.com/reference/android/media/MediaCodec#initialization">MediaCodec</a>.
*/
public
static
List
<
byte
[]>
buildInitializationData
(
byte
[]
header
)
{
int
preSkipSamples
=
getPreSkipSamples
(
header
);
long
preSkipNanos
=
sampleCountToNanoseconds
(
preSkipSamples
);
long
seekPreRollNanos
=
sampleCountToNanoseconds
(
DEFAULT_SEEK_PRE_ROLL_SAMPLES
);
List
<
byte
[]>
initializationData
=
new
ArrayList
<>(
FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT
);
initializationData
.
add
(
header
);
initializationData
.
add
(
buildNativeOrderByteArray
(
preSkipNanos
));
initializationData
.
add
(
buildNativeOrderByteArray
(
seekPreRollNanos
));
return
initializationData
;
}
/**
* Returns the number of pre-skip samples specified by the given Opus codec initialization data.
*
* @param initializationData The codec initialization data.
* @return The number of pre-skip samples.
*/
public
static
int
getPreSkipSamples
(
List
<
byte
[]>
initializationData
)
{
if
(
initializationData
.
size
()
==
FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT
)
{
long
codecDelayNs
=
ByteBuffer
.
wrap
(
initializationData
.
get
(
1
)).
order
(
ByteOrder
.
nativeOrder
()).
getLong
();
return
(
int
)
nanosecondsToSampleCount
(
codecDelayNs
);
}
// Fall back to parsing directly from the Opus Identification header.
return
getPreSkipSamples
(
initializationData
.
get
(
0
));
}
/**
* Returns the number of seek per-roll samples specified by the given Opus codec initialization
* data.
*
* @param initializationData The codec initialization data.
* @return The number of seek pre-roll samples.
*/
public
static
int
getSeekPreRollSamples
(
List
<
byte
[]>
initializationData
)
{
if
(
initializationData
.
size
()
==
FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT
)
{
long
seekPreRollNs
=
ByteBuffer
.
wrap
(
initializationData
.
get
(
2
)).
order
(
ByteOrder
.
nativeOrder
()).
getLong
();
return
(
int
)
nanosecondsToSampleCount
(
seekPreRollNs
);
}
// Fall back to returning the default seek pre-roll.
return
DEFAULT_SEEK_PRE_ROLL_SAMPLES
;
}
private
static
int
getPreSkipSamples
(
byte
[]
header
)
{
return
((
header
[
11
]
&
0xFF
)
<<
8
)
|
(
header
[
10
]
&
0xFF
);
}
private
static
byte
[]
buildNativeOrderByteArray
(
long
value
)
{
return
ByteBuffer
.
allocate
(
8
).
order
(
ByteOrder
.
nativeOrder
()).
putLong
(
value
).
array
();
}
private
static
long
sampleCountToNanoseconds
(
long
sampleCount
)
{
return
(
sampleCount
*
C
.
NANOS_PER_SECOND
)
/
SAMPLE_RATE
;
}
private
static
long
nanosecondsToSampleCount
(
long
nanoseconds
)
{
return
(
nanoseconds
*
SAMPLE_RATE
)
/
C
.
NANOS_PER_SECOND
;
}
}
library/common/src/test/java/com/google/android/exoplayer2/audio/OpusUtilTest.java
0 → 100644
View file @
5de56cd6
/*
* Copyright (C) 2020 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
.
audio
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.C
;
import
com.google.common.collect.ImmutableList
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.util.List
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
/** Unit tests for {@link OpusUtil}. */
@RunWith
(
AndroidJUnit4
.
class
)
public
final
class
OpusUtilTest
{
private
static
final
byte
[]
HEADER
=
new
byte
[]
{
79
,
112
,
117
,
115
,
72
,
101
,
97
,
100
,
0
,
2
,
1
,
56
,
0
,
0
,
-
69
,
-
128
,
0
,
0
,
0
};
private
static
final
int
HEADER_PRE_SKIP_SAMPLES
=
14337
;
private
static
final
byte
[]
HEADER_PRE_SKIP_BYTES
=
buildNativeOrderByteArray
(
sampleCountToNanoseconds
(
HEADER_PRE_SKIP_SAMPLES
));
private
static
final
int
DEFAULT_SEEK_PRE_ROLL_SAMPLES
=
3840
;
private
static
final
byte
[]
DEFAULT_SEEK_PRE_ROLL_BYTES
=
buildNativeOrderByteArray
(
sampleCountToNanoseconds
(
DEFAULT_SEEK_PRE_ROLL_SAMPLES
));
private
static
final
ImmutableList
<
byte
[]>
HEADER_ONLY_INITIALIZATION_DATA
=
ImmutableList
.
of
(
HEADER
);
private
static
final
long
CUSTOM_PRE_SKIP_SAMPLES
=
28674
;
private
static
final
byte
[]
CUSTOM_PRE_SKIP_BYTES
=
buildNativeOrderByteArray
(
sampleCountToNanoseconds
(
CUSTOM_PRE_SKIP_SAMPLES
));
private
static
final
long
CUSTOM_SEEK_PRE_ROLL_SAMPLES
=
7680
;
private
static
final
byte
[]
CUSTOM_SEEK_PRE_ROLL_BYTES
=
buildNativeOrderByteArray
(
sampleCountToNanoseconds
(
CUSTOM_SEEK_PRE_ROLL_SAMPLES
));
private
static
final
ImmutableList
<
byte
[]>
FULL_INITIALIZATION_DATA
=
ImmutableList
.
of
(
HEADER
,
CUSTOM_PRE_SKIP_BYTES
,
CUSTOM_SEEK_PRE_ROLL_BYTES
);
@Test
public
void
buildInitializationData
()
{
List
<
byte
[]>
initializationData
=
OpusUtil
.
buildInitializationData
(
HEADER
);
assertThat
(
initializationData
).
hasSize
(
3
);
assertThat
(
initializationData
.
get
(
0
)).
isEqualTo
(
HEADER
);
assertThat
(
initializationData
.
get
(
1
)).
isEqualTo
(
HEADER_PRE_SKIP_BYTES
);
assertThat
(
initializationData
.
get
(
2
)).
isEqualTo
(
DEFAULT_SEEK_PRE_ROLL_BYTES
);
}
@Test
public
void
getChannelCount
()
{
int
channelCount
=
OpusUtil
.
getChannelCount
(
HEADER
);
assertThat
(
channelCount
).
isEqualTo
(
2
);
}
@Test
public
void
getPreSkipSamples_fullInitializationData_returnsOverrideValue
()
{
int
preSkipSamples
=
OpusUtil
.
getPreSkipSamples
(
FULL_INITIALIZATION_DATA
);
assertThat
(
preSkipSamples
).
isEqualTo
(
CUSTOM_PRE_SKIP_SAMPLES
);
}
@Test
public
void
getPreSkipSamples_headerOnlyInitializationData_returnsHeaderValue
()
{
int
preSkipSamples
=
OpusUtil
.
getPreSkipSamples
(
HEADER_ONLY_INITIALIZATION_DATA
);
assertThat
(
preSkipSamples
).
isEqualTo
(
HEADER_PRE_SKIP_SAMPLES
);
}
@Test
public
void
getSeekPreRollSamples_fullInitializationData_returnsInitializationDataValue
()
{
int
seekPreRollSamples
=
OpusUtil
.
getSeekPreRollSamples
(
FULL_INITIALIZATION_DATA
);
assertThat
(
seekPreRollSamples
).
isEqualTo
(
CUSTOM_SEEK_PRE_ROLL_SAMPLES
);
}
@Test
public
void
getSeekPreRollSamples_headerOnlyInitializationData_returnsDefaultValue
()
{
int
seekPreRollSamples
=
OpusUtil
.
getSeekPreRollSamples
(
HEADER_ONLY_INITIALIZATION_DATA
);
assertThat
(
seekPreRollSamples
).
isEqualTo
(
DEFAULT_SEEK_PRE_ROLL_SAMPLES
);
}
private
static
long
sampleCountToNanoseconds
(
long
sampleCount
)
{
return
(
sampleCount
*
C
.
NANOS_PER_SECOND
)
/
OpusUtil
.
SAMPLE_RATE
;
}
private
static
byte
[]
buildNativeOrderByteArray
(
long
value
)
{
return
ByteBuffer
.
allocate
(
8
).
order
(
ByteOrder
.
nativeOrder
()).
putLong
(
value
).
array
();
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java
View file @
5de56cd6
...
@@ -15,13 +15,10 @@
...
@@ -15,13 +15,10 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
ogg
;
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
ogg
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.audio.OpusUtil
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.List
;
...
@@ -30,11 +27,6 @@ import java.util.List;
...
@@ -30,11 +27,6 @@ import java.util.List;
*/
*/
/* package */
final
class
OpusReader
extends
StreamReader
{
/* package */
final
class
OpusReader
extends
StreamReader
{
private
static
final
int
DEFAULT_SEEK_PRE_ROLL_SAMPLES
=
3840
;
/** Opus streams are always decoded at 48000 Hz. */
private
static
final
int
SAMPLE_RATE
=
48_000
;
private
static
final
int
OPUS_CODE
=
0x4f707573
;
private
static
final
int
OPUS_CODE
=
0x4f707573
;
private
static
final
byte
[]
OPUS_SIGNATURE
=
{
'O'
,
'p'
,
'u'
,
's'
,
'H'
,
'e'
,
'a'
,
'd'
};
private
static
final
byte
[]
OPUS_SIGNATURE
=
{
'O'
,
'p'
,
'u'
,
's'
,
'H'
,
'e'
,
'a'
,
'd'
};
...
@@ -65,20 +57,14 @@ import java.util.List;
...
@@ -65,20 +57,14 @@ import java.util.List;
@Override
@Override
protected
boolean
readHeaders
(
ParsableByteArray
packet
,
long
position
,
SetupData
setupData
)
{
protected
boolean
readHeaders
(
ParsableByteArray
packet
,
long
position
,
SetupData
setupData
)
{
if
(!
headerRead
)
{
if
(!
headerRead
)
{
byte
[]
metadata
=
Arrays
.
copyOf
(
packet
.
getData
(),
packet
.
limit
());
byte
[]
headerBytes
=
Arrays
.
copyOf
(
packet
.
getData
(),
packet
.
limit
());
int
channelCount
=
metadata
[
9
]
&
0xFF
;
int
channelCount
=
OpusUtil
.
getChannelCount
(
headerBytes
);
int
preskip
=
((
metadata
[
11
]
&
0xFF
)
<<
8
)
|
(
metadata
[
10
]
&
0xFF
);
List
<
byte
[]>
initializationData
=
OpusUtil
.
buildInitializationData
(
headerBytes
);
List
<
byte
[]>
initializationData
=
new
ArrayList
<>(
3
);
initializationData
.
add
(
metadata
);
putNativeOrderLong
(
initializationData
,
preskip
);
putNativeOrderLong
(
initializationData
,
DEFAULT_SEEK_PRE_ROLL_SAMPLES
);
setupData
.
format
=
setupData
.
format
=
new
Format
.
Builder
()
new
Format
.
Builder
()
.
setSampleMimeType
(
MimeTypes
.
AUDIO_OPUS
)
.
setSampleMimeType
(
MimeTypes
.
AUDIO_OPUS
)
.
setChannelCount
(
channelCount
)
.
setChannelCount
(
channelCount
)
.
setSampleRate
(
SAMPLE_RATE
)
.
setSampleRate
(
OpusUtil
.
SAMPLE_RATE
)
.
setInitializationData
(
initializationData
)
.
setInitializationData
(
initializationData
)
.
build
();
.
build
();
headerRead
=
true
;
headerRead
=
true
;
...
@@ -90,12 +76,6 @@ import java.util.List;
...
@@ -90,12 +76,6 @@ import java.util.List;
return
true
;
return
true
;
}
}
private
void
putNativeOrderLong
(
List
<
byte
[]>
initializationData
,
int
samples
)
{
long
ns
=
(
samples
*
C
.
NANOS_PER_SECOND
)
/
SAMPLE_RATE
;
byte
[]
array
=
ByteBuffer
.
allocate
(
8
).
order
(
ByteOrder
.
nativeOrder
()).
putLong
(
ns
).
array
();
initializationData
.
add
(
array
);
}
/**
/**
* Returns the duration of the given audio packet.
* Returns the duration of the given audio packet.
*
*
...
...
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