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
2902452b
authored
Jan 10, 2020
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Merge pull request #6797 from DolbyLaboratories:dev-v2-ac4-drm
PiperOrigin-RevId: 289092332
parent
ca11e56f
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
431 additions
and
9 deletions
RELEASENOTES.md
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
library/core/src/test/assets/mp4/sample_ac4_protected.mp4
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.0.dump
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.1.dump
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.2.dump
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.3.dump
library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java
RELEASENOTES.md
View file @
2902452b
...
@@ -37,6 +37,7 @@
...
@@ -37,6 +37,7 @@
on earlier releases, but only when embedded in a non-FLAC container such as
on earlier releases, but only when embedded in a non-FLAC container such as
Matroska or MP4.
Matroska or MP4.
*
Javadocs: Add favicon for easier identification in browser tabs
*
Javadocs: Add favicon for easier identification in browser tabs
*
FMP4: Add support for encrypted AC-4 tracks.
### 2.11.1 (2019-12-20) ###
### 2.11.1 (2019-12-20) ###
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
View file @
2902452b
...
@@ -1257,14 +1257,19 @@ public class FragmentedMp4Extractor implements Extractor {
...
@@ -1257,14 +1257,19 @@ public class FragmentedMp4Extractor implements Extractor {
sampleSize
-=
Atom
.
HEADER_SIZE
;
sampleSize
-=
Atom
.
HEADER_SIZE
;
input
.
skipFully
(
Atom
.
HEADER_SIZE
);
input
.
skipFully
(
Atom
.
HEADER_SIZE
);
}
}
sampleBytesWritten
=
currentTrackBundle
.
outputSampleEncryptionData
();
sampleSize
+=
sampleBytesWritten
;
if
(
MimeTypes
.
AUDIO_AC4
.
equals
(
currentTrackBundle
.
track
.
format
.
sampleMimeType
))
{
if
(
MimeTypes
.
AUDIO_AC4
.
equals
(
currentTrackBundle
.
track
.
format
.
sampleMimeType
))
{
// AC4 samples need to be prefixed with a clear sample header.
sampleBytesWritten
=
currentTrackBundle
.
outputSampleEncryptionData
(
sampleSize
,
Ac4Util
.
SAMPLE_HEADER_SIZE
);
Ac4Util
.
getAc4SampleHeader
(
sampleSize
,
scratch
);
Ac4Util
.
getAc4SampleHeader
(
sampleSize
,
scratch
);
currentTrackBundle
.
output
.
sampleData
(
scratch
,
Ac4Util
.
SAMPLE_HEADER_SIZE
);
currentTrackBundle
.
output
.
sampleData
(
scratch
,
Ac4Util
.
SAMPLE_HEADER_SIZE
);
sampleBytesWritten
+=
Ac4Util
.
SAMPLE_HEADER_SIZE
;
sampleBytesWritten
+=
Ac4Util
.
SAMPLE_HEADER_SIZE
;
sampleSize
+=
Ac4Util
.
SAMPLE_HEADER_SIZE
;
}
else
{
sampleBytesWritten
=
currentTrackBundle
.
outputSampleEncryptionData
(
sampleSize
,
/* clearHeaderSize= */
0
);
}
}
sampleSize
+=
sampleBytesWritten
;
parserState
=
STATE_READING_SAMPLE_CONTINUE
;
parserState
=
STATE_READING_SAMPLE_CONTINUE
;
sampleCurrentNalBytesRemaining
=
0
;
sampleCurrentNalBytesRemaining
=
0
;
}
}
...
@@ -1462,8 +1467,11 @@ public class FragmentedMp4Extractor implements Extractor {
...
@@ -1462,8 +1467,11 @@ public class FragmentedMp4Extractor implements Extractor {
*/
*/
private
static
final
class
TrackBundle
{
private
static
final
class
TrackBundle
{
private
static
final
int
SINGLE_SUBSAMPLE_ENCRYPTION_DATA_LENGTH
=
8
;
public
final
TrackOutput
output
;
public
final
TrackOutput
output
;
public
final
TrackFragment
fragment
;
public
final
TrackFragment
fragment
;
public
final
ParsableByteArray
scratch
;
public
Track
track
;
public
Track
track
;
public
DefaultSampleValues
defaultSampleValues
;
public
DefaultSampleValues
defaultSampleValues
;
...
@@ -1478,6 +1486,7 @@ public class FragmentedMp4Extractor implements Extractor {
...
@@ -1478,6 +1486,7 @@ public class FragmentedMp4Extractor implements Extractor {
public
TrackBundle
(
TrackOutput
output
)
{
public
TrackBundle
(
TrackOutput
output
)
{
this
.
output
=
output
;
this
.
output
=
output
;
fragment
=
new
TrackFragment
();
fragment
=
new
TrackFragment
();
scratch
=
new
ParsableByteArray
();
encryptionSignalByte
=
new
ParsableByteArray
(
1
);
encryptionSignalByte
=
new
ParsableByteArray
(
1
);
defaultInitializationVector
=
new
ParsableByteArray
();
defaultInitializationVector
=
new
ParsableByteArray
();
}
}
...
@@ -1545,9 +1554,13 @@ public class FragmentedMp4Extractor implements Extractor {
...
@@ -1545,9 +1554,13 @@ public class FragmentedMp4Extractor implements Extractor {
/**
/**
* Outputs the encryption data for the current sample.
* Outputs the encryption data for the current sample.
*
*
* @param sampleSize The size of the current sample in bytes, excluding any additional clear
* header that will be prefixed to the sample by the extractor.
* @param clearHeaderSize The size of a clear header that will be prefixed to the sample by the
* extractor, or 0.
* @return The number of written bytes.
* @return The number of written bytes.
*/
*/
public
int
outputSampleEncryptionData
()
{
public
int
outputSampleEncryptionData
(
int
sampleSize
,
int
clearHeaderSize
)
{
TrackEncryptionBox
encryptionBox
=
getEncryptionBoxIfEncrypted
();
TrackEncryptionBox
encryptionBox
=
getEncryptionBoxIfEncrypted
();
if
(
encryptionBox
==
null
)
{
if
(
encryptionBox
==
null
)
{
return
0
;
return
0
;
...
@@ -1566,23 +1579,61 @@ public class FragmentedMp4Extractor implements Extractor {
...
@@ -1566,23 +1579,61 @@ public class FragmentedMp4Extractor implements Extractor {
vectorSize
=
initVectorData
.
length
;
vectorSize
=
initVectorData
.
length
;
}
}
boolean
subsampleEncryption
=
fragment
.
sampleHasSubsampleEncryptionTable
(
currentSampleIndex
);
boolean
haveSubsampleEncryptionTable
=
fragment
.
sampleHasSubsampleEncryptionTable
(
currentSampleIndex
);
boolean
writeSubsampleEncryptionData
=
haveSubsampleEncryptionTable
||
clearHeaderSize
!=
0
;
// Write the signal byte, containing the vector size and the subsample encryption flag.
// Write the signal byte, containing the vector size and the subsample encryption flag.
encryptionSignalByte
.
data
[
0
]
=
(
byte
)
(
vectorSize
|
(
subsampleEncryption
?
0x80
:
0
));
encryptionSignalByte
.
data
[
0
]
=
(
byte
)
(
vectorSize
|
(
writeSubsampleEncryptionData
?
0x80
:
0
));
encryptionSignalByte
.
setPosition
(
0
);
encryptionSignalByte
.
setPosition
(
0
);
output
.
sampleData
(
encryptionSignalByte
,
1
);
output
.
sampleData
(
encryptionSignalByte
,
1
);
// Write the vector.
// Write the vector.
output
.
sampleData
(
initializationVectorData
,
vectorSize
);
output
.
sampleData
(
initializationVectorData
,
vectorSize
);
// If we don't have subsample encryption data, we're done.
if
(!
subsampleEncryption
)
{
if
(!
writeSubsampleEncryptionData
)
{
return
1
+
vectorSize
;
return
1
+
vectorSize
;
}
}
// Write the subsample encryption data.
if
(!
haveSubsampleEncryptionTable
)
{
// The sample is fully encrypted, except for the additional clear header that the extractor
// is going to prefix. We need to synthesize subsample encryption data that takes the header
// into account.
scratch
.
reset
(
SINGLE_SUBSAMPLE_ENCRYPTION_DATA_LENGTH
);
// subsampleCount = 1 (unsigned short)
scratch
.
data
[
0
]
=
(
byte
)
0
;
scratch
.
data
[
1
]
=
(
byte
)
1
;
// clearDataSize = clearHeaderSize (unsigned short)
scratch
.
data
[
2
]
=
(
byte
)
((
clearHeaderSize
>>
8
)
&
0xFF
);
scratch
.
data
[
3
]
=
(
byte
)
(
clearHeaderSize
&
0xFF
);
// encryptedDataSize = sampleSize (unsigned short)
scratch
.
data
[
4
]
=
(
byte
)
((
sampleSize
>>
24
)
&
0xFF
);
scratch
.
data
[
5
]
=
(
byte
)
((
sampleSize
>>
16
)
&
0xFF
);
scratch
.
data
[
6
]
=
(
byte
)
((
sampleSize
>>
8
)
&
0xFF
);
scratch
.
data
[
7
]
=
(
byte
)
(
sampleSize
&
0xFF
);
output
.
sampleData
(
scratch
,
SINGLE_SUBSAMPLE_ENCRYPTION_DATA_LENGTH
);
return
1
+
vectorSize
+
SINGLE_SUBSAMPLE_ENCRYPTION_DATA_LENGTH
;
}
ParsableByteArray
subsampleEncryptionData
=
fragment
.
sampleEncryptionData
;
ParsableByteArray
subsampleEncryptionData
=
fragment
.
sampleEncryptionData
;
int
subsampleCount
=
subsampleEncryptionData
.
readUnsignedShort
();
int
subsampleCount
=
subsampleEncryptionData
.
readUnsignedShort
();
subsampleEncryptionData
.
skipBytes
(-
2
);
subsampleEncryptionData
.
skipBytes
(-
2
);
int
subsampleDataLength
=
2
+
6
*
subsampleCount
;
int
subsampleDataLength
=
2
+
6
*
subsampleCount
;
if
(
clearHeaderSize
!=
0
)
{
// We need to account for the additional clear header by adding clearHeaderSize to
// clearDataSize for the first subsample specified in the subsample encryption data.
scratch
.
reset
(
subsampleDataLength
);
scratch
.
readBytes
(
subsampleEncryptionData
.
data
,
/* offset= */
0
,
subsampleDataLength
);
subsampleEncryptionData
.
skipBytes
(
subsampleDataLength
);
int
clearDataSize
=
(
scratch
.
data
[
2
]
&
0xFF
)
<<
8
|
(
scratch
.
data
[
3
]
&
0xFF
);
int
adjustedClearDataSize
=
clearDataSize
+
clearHeaderSize
;
scratch
.
data
[
2
]
=
(
byte
)
((
adjustedClearDataSize
>>
8
)
&
0xFF
);
scratch
.
data
[
3
]
=
(
byte
)
(
adjustedClearDataSize
&
0xFF
);
subsampleEncryptionData
=
scratch
;
}
output
.
sampleData
(
subsampleEncryptionData
,
subsampleDataLength
);
output
.
sampleData
(
subsampleEncryptionData
,
subsampleDataLength
);
return
1
+
vectorSize
+
subsampleDataLength
;
return
1
+
vectorSize
+
subsampleDataLength
;
}
}
...
...
library/core/src/test/assets/mp4/sample_ac4_protected.mp4
0 → 100644
View file @
2902452b
No preview for this file type
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.0.dump
0 → 100644
View file @
2902452b
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=950]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 2
sampleRate = 48000
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -1683793742
metadata = null
initializationData:
total output bytes = 7936
sample count = 19
sample 0:
time = 0
flags = 1073741825
data = length 384, hash 96EFFFF3
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 1:
time = 40000
flags = 1073741825
data = length 384, hash 899279C6
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 2:
time = 80000
flags = 1073741825
data = length 384, hash 9EA9F45
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 3:
time = 120000
flags = 1073741825
data = length 384, hash 82D362A9
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 4:
time = 160000
flags = 1073741825
data = length 384, hash B8705CFB
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 5:
time = 200000
flags = 1073741825
data = length 384, hash 58B5628E
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 6:
time = 240000
flags = 1073741825
data = length 384, hash 87F3C13B
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 7:
time = 280000
flags = 1073741825
data = length 384, hash 54333DC5
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 8:
time = 320000
flags = 1073741825
data = length 384, hash 1C49C4B3
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 9:
time = 360000
flags = 1073741825
data = length 384, hash 5FDC324F
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 10:
time = 400000
flags = 1073741825
data = length 384, hash B2A7F444
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 11:
time = 440000
flags = 1073741825
data = length 512, hash 5FD06C1E
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 12:
time = 480000
flags = 1073741825
data = length 537, hash 7ABBDCB
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 13:
time = 520000
flags = 1073741825
data = length 616, hash 3F657E23
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 14:
time = 560000
flags = 1073741825
data = length 453, hash 8FCF0529
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 15:
time = 600000
flags = 1073741825
data = length 383, hash 7F8C9E19
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 16:
time = 640000
flags = 1073741825
data = length 410, hash 3727858D
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 17:
time = 680000
flags = 1073741825
data = length 391, hash E2931212
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 18:
time = 720000
flags = 1073741825
data = length 410, hash 63017D46
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
tracksEnded = true
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.1.dump
0 → 100644
View file @
2902452b
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=950]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 2
sampleRate = 48000
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -1683793742
metadata = null
initializationData:
total output bytes = 5632
sample count = 13
sample 0:
time = 240000
flags = 1073741825
data = length 384, hash 87F3C13B
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 1:
time = 280000
flags = 1073741825
data = length 384, hash 54333DC5
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 2:
time = 320000
flags = 1073741825
data = length 384, hash 1C49C4B3
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 3:
time = 360000
flags = 1073741825
data = length 384, hash 5FDC324F
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 4:
time = 400000
flags = 1073741825
data = length 384, hash B2A7F444
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 5:
time = 440000
flags = 1073741825
data = length 512, hash 5FD06C1E
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 6:
time = 480000
flags = 1073741825
data = length 537, hash 7ABBDCB
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 7:
time = 520000
flags = 1073741825
data = length 616, hash 3F657E23
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 8:
time = 560000
flags = 1073741825
data = length 453, hash 8FCF0529
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 9:
time = 600000
flags = 1073741825
data = length 383, hash 7F8C9E19
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 10:
time = 640000
flags = 1073741825
data = length 410, hash 3727858D
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 11:
time = 680000
flags = 1073741825
data = length 391, hash E2931212
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 12:
time = 720000
flags = 1073741825
data = length 410, hash 63017D46
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
tracksEnded = true
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.2.dump
0 → 100644
View file @
2902452b
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=950]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 2
sampleRate = 48000
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -1683793742
metadata = null
initializationData:
total output bytes = 3200
sample count = 7
sample 0:
time = 480000
flags = 1073741825
data = length 537, hash 7ABBDCB
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 1:
time = 520000
flags = 1073741825
data = length 616, hash 3F657E23
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 2:
time = 560000
flags = 1073741825
data = length 453, hash 8FCF0529
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 3:
time = 600000
flags = 1073741825
data = length 383, hash 7F8C9E19
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 4:
time = 640000
flags = 1073741825
data = length 410, hash 3727858D
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 5:
time = 680000
flags = 1073741825
data = length 391, hash E2931212
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 6:
time = 720000
flags = 1073741825
data = length 410, hash 63017D46
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
tracksEnded = true
library/core/src/test/assets/mp4/sample_ac4_protected.mp4.3.dump
0 → 100644
View file @
2902452b
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=950]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 2
sampleRate = 48000
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -1683793742
metadata = null
initializationData:
total output bytes = 410
sample count = 1
sample 0:
time = 720000
flags = 1073741825
data = length 410, hash 63017D46
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
tracksEnded = true
library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java
View file @
2902452b
...
@@ -57,6 +57,12 @@ public final class FragmentedMp4ExtractorTest {
...
@@ -57,6 +57,12 @@ public final class FragmentedMp4ExtractorTest {
getExtractorFactory
(
Collections
.
emptyList
()),
"mp4/sample_ac4_fragmented.mp4"
);
getExtractorFactory
(
Collections
.
emptyList
()),
"mp4/sample_ac4_fragmented.mp4"
);
}
}
@Test
public
void
testSampleWithProtectedAc4Track
()
throws
Exception
{
ExtractorAsserts
.
assertBehavior
(
getExtractorFactory
(
Collections
.
emptyList
()),
"mp4/sample_ac4_protected.mp4"
);
}
private
static
ExtractorFactory
getExtractorFactory
(
final
List
<
Format
>
closedCaptionFormats
)
{
private
static
ExtractorFactory
getExtractorFactory
(
final
List
<
Format
>
closedCaptionFormats
)
{
return
()
->
new
FragmentedMp4Extractor
(
0
,
null
,
null
,
null
,
closedCaptionFormats
);
return
()
->
new
FragmentedMp4Extractor
(
0
,
null
,
null
,
null
,
closedCaptionFormats
);
}
}
...
...
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