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
aed45bb2
authored
May 01, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add support for reading AC-3 audio in HLS.
parent
9b6c3052
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
309 additions
and
2 deletions
library/src/main/java/com/google/android/exoplayer/extractor/ts/Ac3Reader.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
library/src/main/java/com/google/android/exoplayer/util/Ac3Util.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/Ac3Reader.java
0 → 100644
View file @
aed45bb2
/*
* Copyright (C) 2014 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
.
exoplayer
.
extractor
.
ts
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.util.Ac3Util
;
import
com.google.android.exoplayer.util.ParsableBitArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
/**
* Parses a continuous AC-3 byte stream and extracts individual samples.
*/
/* package */
final
class
Ac3Reader
extends
ElementaryStreamReader
{
private
static
final
int
STATE_FINDING_SYNC
=
0
;
private
static
final
int
STATE_READING_HEADER
=
1
;
private
static
final
int
STATE_READING_SAMPLE
=
2
;
private
static
final
int
HEADER_SIZE
=
8
;
private
final
ParsableBitArray
headerScratchBits
;
private
final
ParsableByteArray
headerScratchBytes
;
private
int
state
;
private
int
bytesRead
;
// Used to find the header.
private
boolean
lastByteWas0B
;
// Used when parsing the header.
private
long
frameDurationUs
;
private
MediaFormat
mediaFormat
;
private
int
sampleSize
;
// Used when reading the samples.
private
long
timeUs
;
public
Ac3Reader
(
TrackOutput
output
)
{
super
(
output
);
headerScratchBits
=
new
ParsableBitArray
(
new
byte
[
HEADER_SIZE
]);
headerScratchBytes
=
new
ParsableByteArray
(
headerScratchBits
.
getData
());
state
=
STATE_FINDING_SYNC
;
}
public
void
seek
()
{
state
=
STATE_FINDING_SYNC
;
bytesRead
=
0
;
lastByteWas0B
=
false
;
}
@Override
public
void
consume
(
ParsableByteArray
data
,
long
pesTimeUs
,
boolean
startOfPacket
)
{
if
(
startOfPacket
)
{
timeUs
=
pesTimeUs
;
}
while
(
data
.
bytesLeft
()
>
0
)
{
switch
(
state
)
{
case
STATE_FINDING_SYNC:
if
(
skipToNextSync
(
data
))
{
state
=
STATE_READING_HEADER
;
headerScratchBytes
.
data
[
0
]
=
0x0B
;
headerScratchBytes
.
data
[
1
]
=
0x77
;
bytesRead
=
2
;
}
break
;
case
STATE_READING_HEADER:
if
(
continueRead
(
data
,
headerScratchBytes
.
data
,
HEADER_SIZE
))
{
parseHeader
();
headerScratchBytes
.
setPosition
(
0
);
output
.
sampleData
(
headerScratchBytes
,
HEADER_SIZE
);
state
=
STATE_READING_SAMPLE
;
}
break
;
case
STATE_READING_SAMPLE:
int
bytesToRead
=
Math
.
min
(
data
.
bytesLeft
(),
sampleSize
-
bytesRead
);
output
.
sampleData
(
data
,
bytesToRead
);
bytesRead
+=
bytesToRead
;
if
(
bytesRead
==
sampleSize
)
{
output
.
sampleMetadata
(
timeUs
,
C
.
SAMPLE_FLAG_SYNC
,
sampleSize
,
0
,
null
);
timeUs
+=
frameDurationUs
;
state
=
STATE_FINDING_SYNC
;
}
break
;
}
}
}
@Override
public
void
packetFinished
()
{
// Do nothing.
}
/**
* Continues a read from the provided {@code source} into a given {@code target}. It's assumed
* that the data should be written into {@code target} starting from an offset of zero.
*
* @param source The source from which to read.
* @param target The target into which data is to be read.
* @param targetLength The target length of the read.
* @return Whether the target length was reached.
*/
private
boolean
continueRead
(
ParsableByteArray
source
,
byte
[]
target
,
int
targetLength
)
{
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
targetLength
-
bytesRead
);
source
.
readBytes
(
target
,
bytesRead
,
bytesToRead
);
bytesRead
+=
bytesToRead
;
return
bytesRead
==
targetLength
;
}
/**
* Locates the next sync word, advancing the position to the byte that immediately follows it.
* If a sync word was not located, the position is advanced to the limit.
*
* @param pesBuffer The buffer whose position should be advanced.
* @return True if a sync word position was found. False otherwise.
*/
private
boolean
skipToNextSync
(
ParsableByteArray
pesBuffer
)
{
while
(
pesBuffer
.
bytesLeft
()
>
0
)
{
if
(!
lastByteWas0B
)
{
lastByteWas0B
=
pesBuffer
.
readUnsignedByte
()
==
0x0B
;
continue
;
}
int
secondByte
=
pesBuffer
.
readUnsignedByte
();
if
(
secondByte
==
0x77
)
{
lastByteWas0B
=
false
;
return
true
;
}
else
{
lastByteWas0B
=
secondByte
==
0x0B
;
}
}
return
false
;
}
/**
* Parses the sample header.
*/
private
void
parseHeader
()
{
if
(
mediaFormat
==
null
)
{
headerScratchBits
.
setPosition
(
0
);
mediaFormat
=
Ac3Util
.
parseFrameAc3Format
(
headerScratchBits
);
output
.
format
(
mediaFormat
);
}
headerScratchBits
.
setPosition
(
0
);
sampleSize
=
Ac3Util
.
parseFrameSize
(
headerScratchBits
);
frameDurationUs
=
(
int
)
(
1000000L
*
8
*
sampleSize
/
mediaFormat
.
bitrate
);
}
}
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
View file @
aed45bb2
...
@@ -40,6 +40,8 @@ public final class TsExtractor implements Extractor {
...
@@ -40,6 +40,8 @@ public final class TsExtractor implements Extractor {
private
static
final
int
TS_PAT_PID
=
0
;
private
static
final
int
TS_PAT_PID
=
0
;
private
static
final
int
TS_STREAM_TYPE_AAC
=
0x0F
;
private
static
final
int
TS_STREAM_TYPE_AAC
=
0x0F
;
private
static
final
int
TS_STREAM_TYPE_ATSC_AC3
=
0x81
;
private
static
final
int
TS_STREAM_TYPE_DVB_AC3
=
0x06
;
private
static
final
int
TS_STREAM_TYPE_H264
=
0x1B
;
private
static
final
int
TS_STREAM_TYPE_H264
=
0x1B
;
private
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
private
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
private
static
final
int
TS_STREAM_TYPE_EIA608
=
0x100
;
// 0xFF + 1
private
static
final
int
TS_STREAM_TYPE_EIA608
=
0x100
;
// 0xFF + 1
...
@@ -262,11 +264,14 @@ public final class TsExtractor implements Extractor {
...
@@ -262,11 +264,14 @@ public final class TsExtractor implements Extractor {
case
TS_STREAM_TYPE_AAC:
case
TS_STREAM_TYPE_AAC:
pesPayloadReader
=
new
AdtsReader
(
output
.
track
(
TS_STREAM_TYPE_AAC
));
pesPayloadReader
=
new
AdtsReader
(
output
.
track
(
TS_STREAM_TYPE_AAC
));
break
;
break
;
case
TS_STREAM_TYPE_ATSC_AC3:
case
TS_STREAM_TYPE_DVB_AC3:
pesPayloadReader
=
new
Ac3Reader
(
output
.
track
(
streamType
));
break
;
case
TS_STREAM_TYPE_H264:
case
TS_STREAM_TYPE_H264:
SeiReader
seiReader
=
new
SeiReader
(
output
.
track
(
TS_STREAM_TYPE_EIA608
));
SeiReader
seiReader
=
new
SeiReader
(
output
.
track
(
TS_STREAM_TYPE_EIA608
));
streamReaders
.
put
(
TS_STREAM_TYPE_EIA608
,
seiReader
);
streamReaders
.
put
(
TS_STREAM_TYPE_EIA608
,
seiReader
);
pesPayloadReader
=
new
H264Reader
(
output
.
track
(
TS_STREAM_TYPE_H264
),
pesPayloadReader
=
new
H264Reader
(
output
.
track
(
TS_STREAM_TYPE_H264
),
seiReader
);
seiReader
);
break
;
break
;
case
TS_STREAM_TYPE_ID3:
case
TS_STREAM_TYPE_ID3:
pesPayloadReader
=
id3Reader
;
pesPayloadReader
=
id3Reader
;
...
...
library/src/main/java/com/google/android/exoplayer/util/Ac3Util.java
0 → 100644
View file @
aed45bb2
/*
* Copyright (C) 2014 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
.
exoplayer
.
util
;
import
com.google.android.exoplayer.MediaFormat
;
import
java.util.Collections
;
/**
* Utility methods for parsing AC-3 headers.
*/
public
final
class
Ac3Util
{
/** Sample rates, indexed by fscod. */
private
static
final
int
[]
SAMPLE_RATES
=
new
int
[]
{
48000
,
44100
,
32000
};
/** Channel counts, indexed by acmod. */
private
static
final
int
[]
CHANNEL_COUNTS
=
new
int
[]
{
2
,
1
,
2
,
3
,
3
,
4
,
4
,
5
};
/** Nominal bitrates in kbps, indexed by bit_rate_code. */
private
static
final
int
[]
BITRATES
=
new
int
[]
{
32
,
40
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
160
,
192
,
224
,
256
,
320
,
384
,
448
,
512
,
576
,
640
};
/** 16-bit words per sync frame, indexed by frmsizecod / 2. (See ETSI TS 102 366 table 4.13.) */
private
static
final
int
[]
FRMSIZECOD_TO_FRAME_SIZE_44_1
=
new
int
[]
{
69
,
87
,
104
,
121
,
139
,
174
,
208
,
243
,
278
,
348
,
417
,
487
,
557
,
696
,
835
,
975
,
1114
,
1253
,
1393
};
/**
* Returns the AC-3 format given {@code data} containing the AC3SpecificBox according to
* ETSI TS 102 366 Annex F.
*/
public
static
MediaFormat
parseAnnexFAc3Format
(
ParsableByteArray
data
)
{
// fscod (sample rate code)
int
fscod
=
(
data
.
readUnsignedByte
()
&
0xC0
)
>>
6
;
int
sampleRate
=
SAMPLE_RATES
[
fscod
];
int
nextByte
=
data
.
readUnsignedByte
();
// Map acmod (audio coding mode) onto a channel count.
int
channelCount
=
CHANNEL_COUNTS
[(
nextByte
&
0x38
)
>>
3
];
// lfeon (low frequency effects on)
if
((
nextByte
&
0x04
)
!=
0
)
{
channelCount
++;
}
// Map bit_rate_code onto a bitrate in bit/s.
int
bitrate
=
BITRATES
[((
nextByte
&
0x03
)
<<
3
)
+
(
data
.
readUnsignedByte
()
>>
5
)]
*
1000
;
return
MediaFormat
.
createAudioFormat
(
MimeTypes
.
AUDIO_AC3
,
MediaFormat
.
NO_VALUE
,
MediaFormat
.
NO_VALUE
,
channelCount
,
sampleRate
,
bitrate
,
Collections
.<
byte
[]>
emptyList
());
}
/**
* Returns the AC-3 format given {@code data} containing the EC3SpecificBox according to
* ETSI TS 102 366 Annex F.
*/
public
static
MediaFormat
parseAnnexFEAc3Format
(
ParsableByteArray
data
)
{
data
.
skipBytes
(
2
);
// Skip data_rate and num_ind_sub.
// Read only the first substream.
// TODO: Read later substreams?
// fscod (sample rate code)
int
fscod
=
(
data
.
readUnsignedByte
()
&
0xC0
)
>>
6
;
int
sampleRate
=
SAMPLE_RATES
[
fscod
];
int
nextByte
=
data
.
readUnsignedByte
();
// Map acmod (audio coding mode) onto a channel count.
int
channelCount
=
CHANNEL_COUNTS
[(
nextByte
&
0x0E
)
>>
1
];
// lfeon (low frequency effects on)
if
((
nextByte
&
0x01
)
!=
0
)
{
channelCount
++;
}
return
MediaFormat
.
createAudioFormat
(
MimeTypes
.
AUDIO_EC3
,
MediaFormat
.
NO_VALUE
,
channelCount
,
sampleRate
,
Collections
.<
byte
[]>
emptyList
());
}
/**
* Returns the AC-3 format given {@code data} containing the frame header starting from the sync
* word.
*
* @param data Data to parse, positioned at the start of the syncword.
* @return AC-3 format parsed from data in the header.
*/
public
static
MediaFormat
parseFrameAc3Format
(
ParsableBitArray
data
)
{
// Skip syncword and crc1.
data
.
skipBits
(
4
*
8
);
int
fscod
=
data
.
readBits
(
2
);
int
frmsizecod
=
data
.
readBits
(
6
);
data
.
skipBits
(
8
);
// bsid (5 bits) + bsmod (3 bits)
int
acmod
=
data
.
readBits
(
3
);
if
((
acmod
&
0x01
)
!=
0
&&
acmod
!=
1
)
{
data
.
skipBits
(
2
);
// cmixlev
}
if
((
acmod
&
0x04
)
!=
0
)
{
data
.
skipBits
(
2
);
// surmixlev
}
if
(
acmod
==
0x02
)
{
data
.
skipBits
(
2
);
// dsurmod
}
boolean
lfeon
=
data
.
readBit
();
return
MediaFormat
.
createAudioFormat
(
MimeTypes
.
AUDIO_AC3
,
MediaFormat
.
NO_VALUE
,
MediaFormat
.
NO_VALUE
,
CHANNEL_COUNTS
[
acmod
]
+
(
lfeon
?
1
:
0
),
SAMPLE_RATES
[
fscod
],
BITRATES
[
frmsizecod
/
2
]
*
1000
,
Collections
.<
byte
[]>
emptyList
());
}
/**
* Returns the AC-3 frame size in bytes given {@code data} containing the frame header starting
* from the sync word.
*
* @param data Data to parse, positioned at the start of the syncword.
* @return The frame size parsed from data in the header.
*/
public
static
int
parseFrameSize
(
ParsableBitArray
data
)
{
// Skip syncword and crc1.
data
.
skipBits
(
4
*
8
);
int
fscod
=
data
.
readBits
(
2
);
int
frmsizecod
=
data
.
readBits
(
6
);
int
sampleRate
=
SAMPLE_RATES
[
fscod
];
int
bitrate
=
BITRATES
[
frmsizecod
/
2
];
if
(
sampleRate
==
32000
)
{
return
6
*
bitrate
;
}
else
if
(
sampleRate
==
44100
)
{
return
2
*
(
FRMSIZECOD_TO_FRAME_SIZE_44_1
[
frmsizecod
/
2
]
+
(
frmsizecod
%
2
));
}
else
{
// sampleRate == 48000
return
4
*
bitrate
;
}
}
private
Ac3Util
()
{
// Prevent instantiation.
}
}
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