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
eec8d315
authored
Nov 11, 2021
by
tonihei
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Merge pull request #9543 from KasemJaffer:rf64
PiperOrigin-RevId: 408816643
parent
334fe47b
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
237 additions
and
23 deletions
RELEASENOTES.md
library/extractor/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java
library/extractor/src/test/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.0.dump
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.1.dump
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.2.dump
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.3.dump
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.unknown_length.dump
testdata/src/test/assets/media/wav/sample_rf64.wav
RELEASENOTES.md
View file @
eec8d315
...
...
@@ -11,6 +11,9 @@
`buildVideoRenderers()`
or
`buildAudioRenderers()`
can access the codec
adapter factory and pass it to
`MediaCodecRenderer`
instances they
create.
*
Extractors:
*
WAV: Add support for RF64 streams
(
[
#9543
](
https://github.com/google/ExoPlayer/issues/9543
)
.
### 2.16.0 (2021-11-04)
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java
View file @
eec8d315
...
...
@@ -30,6 +30,10 @@ public final class WavUtil {
public
static
final
int
FMT_FOURCC
=
0x666d7420
;
/** Four character code for "data". */
public
static
final
int
DATA_FOURCC
=
0x64617461
;
/** Four character code for "RF64". */
public
static
final
int
RF64_FOURCC
=
0x52463634
;
/** Four character code for "ds64". */
public
static
final
int
DS64_FOURCC
=
0x64733634
;
/** WAVE type value for integer PCM audio data. */
public
static
final
int
TYPE_PCM
=
0x0001
;
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java
View file @
eec8d315
...
...
@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
...
...
@@ -47,6 +48,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** Extracts data from WAV byte streams. */
public
final
class
WavExtractor
implements
Extractor
{
private
static
final
String
TAG
=
"WavExtractor"
;
/**
* When outputting PCM data to a {@link TrackOutput}, we can choose how many frames are grouped
* into each sample, and hence each sample's duration. This is the target number of samples to
...
...
@@ -63,6 +66,7 @@ public final class WavExtractor implements Extractor {
@Target
({
ElementType
.
TYPE_USE
})
@IntDef
({
STATE_READING_FILE_TYPE
,
STATE_READING_RF64_SAMPLE_DATA_SIZE
,
STATE_READING_FORMAT
,
STATE_SKIPPING_TO_SAMPLE_DATA
,
STATE_READING_SAMPLE_DATA
...
...
@@ -70,19 +74,22 @@ public final class WavExtractor implements Extractor {
private
@interface
State
{}
private
static
final
int
STATE_READING_FILE_TYPE
=
0
;
private
static
final
int
STATE_READING_FORMAT
=
1
;
private
static
final
int
STATE_SKIPPING_TO_SAMPLE_DATA
=
2
;
private
static
final
int
STATE_READING_SAMPLE_DATA
=
3
;
private
static
final
int
STATE_READING_RF64_SAMPLE_DATA_SIZE
=
1
;
private
static
final
int
STATE_READING_FORMAT
=
2
;
private
static
final
int
STATE_SKIPPING_TO_SAMPLE_DATA
=
3
;
private
static
final
int
STATE_READING_SAMPLE_DATA
=
4
;
private
@MonotonicNonNull
ExtractorOutput
extractorOutput
;
private
@MonotonicNonNull
TrackOutput
trackOutput
;
private
@State
int
state
;
private
long
rf64SampleDataSize
;
private
@MonotonicNonNull
OutputWriter
outputWriter
;
private
int
dataStartPosition
;
private
long
dataEndPosition
;
public
WavExtractor
()
{
state
=
STATE_READING_FILE_TYPE
;
rf64SampleDataSize
=
C
.
LENGTH_UNSET
;
dataStartPosition
=
C
.
POSITION_UNSET
;
dataEndPosition
=
C
.
POSITION_UNSET
;
}
...
...
@@ -120,6 +127,9 @@ public final class WavExtractor implements Extractor {
case
STATE_READING_FILE_TYPE:
readFileType
(
input
);
return
Extractor
.
RESULT_CONTINUE
;
case
STATE_READING_RF64_SAMPLE_DATA_SIZE:
readRf64SampleDataSize
(
input
);
return
Extractor
.
RESULT_CONTINUE
;
case
STATE_READING_FORMAT:
readFormat
(
input
);
return
Extractor
.
RESULT_CONTINUE
;
...
...
@@ -152,6 +162,11 @@ public final class WavExtractor implements Extractor {
"Unsupported or unrecognized wav file type."
,
/* cause= */
null
);
}
input
.
skipFully
((
int
)
(
input
.
getPeekPosition
()
-
input
.
getPosition
()));
state
=
STATE_READING_RF64_SAMPLE_DATA_SIZE
;
}
private
void
readRf64SampleDataSize
(
ExtractorInput
input
)
throws
IOException
{
rf64SampleDataSize
=
WavHeaderReader
.
readRf64SampleDataSize
(
input
);
state
=
STATE_READING_FORMAT
;
}
...
...
@@ -194,7 +209,18 @@ public final class WavExtractor implements Extractor {
private
void
skipToSampleData
(
ExtractorInput
input
)
throws
IOException
{
Pair
<
Long
,
Long
>
dataBounds
=
WavHeaderReader
.
skipToSampleData
(
input
);
dataStartPosition
=
dataBounds
.
first
.
intValue
();
dataEndPosition
=
dataBounds
.
second
;
long
dataSize
=
dataBounds
.
second
;
if
(
rf64SampleDataSize
!=
C
.
LENGTH_UNSET
&&
dataSize
==
0xFFFFFFFF
L
)
{
// Following EBU - Tech 3306-2007, the data size indicated in the ds64 chunk should only be
// used if the size of the data chunk is unset.
dataSize
=
rf64SampleDataSize
;
}
dataEndPosition
=
dataStartPosition
+
dataSize
;
long
inputLength
=
input
.
getLength
();
if
(
inputLength
!=
C
.
LENGTH_UNSET
&&
dataEndPosition
>
inputLength
)
{
Log
.
w
(
TAG
,
"Data exceeds input length: "
+
dataEndPosition
+
", "
+
inputLength
);
dataEndPosition
=
inputLength
;
}
Assertions
.
checkNotNull
(
outputWriter
).
init
(
dataStartPosition
,
dataEndPosition
);
state
=
STATE_READING_SAMPLE_DATA
;
}
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java
View file @
eec8d315
...
...
@@ -32,20 +32,19 @@ import java.io.IOException;
private
static
final
String
TAG
=
"WavHeaderReader"
;
/**
* Returns whether the given {@code input} starts with a RIFF
chunk header, followed by a WAVE
* tag.
* Returns whether the given {@code input} starts with a RIFF
or RF64 chunk header, followed by a
*
WAVE
tag.
*
* @param input The input stream to peek from. The position should point to the start of the
* stream.
* @return Whether the given {@code input} starts with a RIFF
chunk header, followed by a WAVE
* tag.
* @return Whether the given {@code input} starts with a RIFF
or RF64 chunk header, followed by a
*
WAVE
tag.
* @throws IOException If peeking from the input fails.
*/
public
static
boolean
checkFileType
(
ExtractorInput
input
)
throws
IOException
{
ParsableByteArray
scratch
=
new
ParsableByteArray
(
ChunkHeader
.
SIZE_IN_BYTES
);
// Attempt to read the RIFF chunk.
ChunkHeader
chunkHeader
=
ChunkHeader
.
peek
(
input
,
scratch
);
if
(
chunkHeader
.
id
!=
WavUtil
.
RIFF_FOURCC
)
{
if
(
chunkHeader
.
id
!=
WavUtil
.
RIFF_FOURCC
&&
chunkHeader
.
id
!=
WavUtil
.
RF64_FOURCC
)
{
return
false
;
}
...
...
@@ -61,10 +60,35 @@ import java.io.IOException;
}
/**
* Reads the ds64 chunk defined in EBU - TECH 3306-2007, if present. If there is no such chunk,
* the input's position is left unchanged.
*
* @param input Input stream to read from. The position should point to the byte following the
* WAVE tag.
* @throws IOException If reading from the input fails.
* @return The value of the data size field in the ds64 chunk, or {@link C#LENGTH_UNSET} if there
* is no such chunk.
*/
public
static
long
readRf64SampleDataSize
(
ExtractorInput
input
)
throws
IOException
{
ParsableByteArray
scratch
=
new
ParsableByteArray
(
ChunkHeader
.
SIZE_IN_BYTES
);
ChunkHeader
chunkHeader
=
ChunkHeader
.
peek
(
input
,
scratch
);
if
(
chunkHeader
.
id
!=
WavUtil
.
DS64_FOURCC
)
{
input
.
resetPeekPosition
();
return
C
.
LENGTH_UNSET
;
}
input
.
advancePeekPosition
(
8
);
// RIFF size
scratch
.
setPosition
(
0
);
input
.
peekFully
(
scratch
.
getData
(),
0
,
8
);
long
sampleDataSize
=
scratch
.
readLittleEndianLong
();
input
.
skipFully
(
ChunkHeader
.
SIZE_IN_BYTES
+
(
int
)
chunkHeader
.
size
);
return
sampleDataSize
;
}
/**
* Reads and returns a {@code WavFormat}.
*
* @param input Input stream to read the WAV format from. The position should point to the byte
* following the
WAVE tag
.
* following the
ds64 chunk if present, or to the byte following the WAVE tag otherwise
.
* @throws IOException If reading from the input fails.
* @return A new {@code WavFormat} read from {@code input}.
*/
...
...
@@ -104,13 +128,14 @@ import java.io.IOException;
}
/**
* Skips to the data in the given WAV input stream, and returns its
bounds. After calling, the
*
input stream's position will point to the start of sample data in the WAV. If an exception is
*
thrown, the input position will be left pointing to a chunk header (that may not be the data
* chunk header).
* Skips to the data in the given WAV input stream, and returns its
start position and size. After
*
calling, the input stream's position will point to the start of sample data in the WAV. If an
*
exception is thrown, the input position will be left pointing to a chunk header (that may not
*
be the data
chunk header).
*
* @param input The input stream, whose read position must be pointing to a valid chunk header.
* @return The byte positions at which the data starts (inclusive) and ends (exclusive).
* @return The byte positions at which the data starts (inclusive) and the size of the data, in
* bytes.
* @throws ParserException If an error occurs parsing chunks.
* @throws IOException If reading from the input fails.
*/
...
...
@@ -125,13 +150,7 @@ import java.io.IOException;
input
.
skipFully
(
ChunkHeader
.
SIZE_IN_BYTES
);
long
dataStartPosition
=
input
.
getPosition
();
long
dataEndPosition
=
dataStartPosition
+
chunkHeader
.
size
;
long
inputLength
=
input
.
getLength
();
if
(
inputLength
!=
C
.
LENGTH_UNSET
&&
dataEndPosition
>
inputLength
)
{
Log
.
w
(
TAG
,
"Data exceeds input length: "
+
dataEndPosition
+
", "
+
inputLength
);
dataEndPosition
=
inputLength
;
}
return
Pair
.
create
(
dataStartPosition
,
dataEndPosition
);
return
Pair
.
create
(
dataStartPosition
,
chunkHeader
.
size
);
}
/**
...
...
library/extractor/src/test/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java
View file @
eec8d315
...
...
@@ -53,4 +53,10 @@ public final class WavExtractorTest {
ExtractorAsserts
.
assertBehavior
(
WavExtractor:
:
new
,
"media/wav/sample_ima_adpcm.wav"
,
simulationConfig
);
}
@Test
public
void
sample_rf64
()
throws
Exception
{
ExtractorAsserts
.
assertBehavior
(
WavExtractor:
:
new
,
"media/wav/sample_rf64.wav"
,
simulationConfig
);
}
}
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.0.dump
0 → 100644
View file @
eec8d315
seekMap:
isSeekable = true
duration = 348625
getPosition(0) = [[timeUs=0, position=80]]
getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]]
getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]]
getPosition(348625) = [[timeUs=348604, position=67012]]
numberOfTracks = 1
track 0:
total output bytes = 66936
sample count = 4
format 0:
averageBitrate = 1536000
peakBitrate = 1536000
sampleMimeType = audio/raw
maxInputSize = 19200
channelCount = 2
sampleRate = 48000
pcmEncoding = 2
sample 0:
time = 0
flags = 1
data = length 19200, hash EF6C7C27
sample 1:
time = 100000
flags = 1
data = length 19200, hash 5AB97AFC
sample 2:
time = 200000
flags = 1
data = length 19200, hash 37920F33
sample 3:
time = 300000
flags = 1
data = length 9336, hash 135F1C30
tracksEnded = true
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.1.dump
0 → 100644
View file @
eec8d315
seekMap:
isSeekable = true
duration = 348625
getPosition(0) = [[timeUs=0, position=80]]
getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]]
getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]]
getPosition(348625) = [[timeUs=348604, position=67012]]
numberOfTracks = 1
track 0:
total output bytes = 44628
sample count = 3
format 0:
averageBitrate = 1536000
peakBitrate = 1536000
sampleMimeType = audio/raw
maxInputSize = 19200
channelCount = 2
sampleRate = 48000
pcmEncoding = 2
sample 0:
time = 116208
flags = 1
data = length 19200, hash E4B962ED
sample 1:
time = 216208
flags = 1
data = length 19200, hash 4F13D6CF
sample 2:
time = 316208
flags = 1
data = length 6228, hash 3FB5F446
tracksEnded = true
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.2.dump
0 → 100644
View file @
eec8d315
seekMap:
isSeekable = true
duration = 348625
getPosition(0) = [[timeUs=0, position=80]]
getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]]
getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]]
getPosition(348625) = [[timeUs=348604, position=67012]]
numberOfTracks = 1
track 0:
total output bytes = 22316
sample count = 2
format 0:
averageBitrate = 1536000
peakBitrate = 1536000
sampleMimeType = audio/raw
maxInputSize = 19200
channelCount = 2
sampleRate = 48000
pcmEncoding = 2
sample 0:
time = 232416
flags = 1
data = length 19200, hash F82E494B
sample 1:
time = 332416
flags = 1
data = length 3116, hash 93C99CFD
tracksEnded = true
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.3.dump
0 → 100644
View file @
eec8d315
seekMap:
isSeekable = true
duration = 348625
getPosition(0) = [[timeUs=0, position=80]]
getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]]
getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]]
getPosition(348625) = [[timeUs=348604, position=67012]]
numberOfTracks = 1
track 0:
total output bytes = 4
sample count = 1
format 0:
averageBitrate = 1536000
peakBitrate = 1536000
sampleMimeType = audio/raw
maxInputSize = 19200
channelCount = 2
sampleRate = 48000
pcmEncoding = 2
sample 0:
time = 348625
flags = 1
data = length 4, hash FFD4C53F
tracksEnded = true
testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.unknown_length.dump
0 → 100644
View file @
eec8d315
seekMap:
isSeekable = true
duration = 348625
getPosition(0) = [[timeUs=0, position=80]]
getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]]
getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]]
getPosition(348625) = [[timeUs=348604, position=67012]]
numberOfTracks = 1
track 0:
total output bytes = 66936
sample count = 4
format 0:
averageBitrate = 1536000
peakBitrate = 1536000
sampleMimeType = audio/raw
maxInputSize = 19200
channelCount = 2
sampleRate = 48000
pcmEncoding = 2
sample 0:
time = 0
flags = 1
data = length 19200, hash EF6C7C27
sample 1:
time = 100000
flags = 1
data = length 19200, hash 5AB97AFC
sample 2:
time = 200000
flags = 1
data = length 19200, hash 37920F33
sample 3:
time = 300000
flags = 1
data = length 9336, hash 135F1C30
tracksEnded = true
testdata/src/test/assets/media/wav/sample_rf64.wav
0 → 100644
View file @
eec8d315
No preview for this file type
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment