Allow missing full_range_flag in colr box with type=nclx

Test file produced with:
$ MP4Box -add "sample.mp4#video:colr=nclc,1,1,1" -new sample_18byte_nclx_colr.mp4

And then manually changing the `nclc` bytes to `nclx`.

This produces an 18-byte `colr` box with type `nclx`. The bitstream of
this file does not contain HDR content, so the file itself is invalid
for playback with a real decoder, but adding the box is enough to test
the extractor change in this commit.

(aside: MP4Box will let you pass `nclx`, but it requires 4 parameters, i.e. it
requires the full_range_flag to be set, resulting in a valid 19-byte colr box)

#minor-release
Issue: #9332
PiperOrigin-RevId: 405842520
This commit is contained in:
ibaker 2021-10-27 10:34:03 +01:00 committed by Andrew Lewis
parent 031f26ba61
commit e8fdab353f
8 changed files with 759 additions and 3 deletions

View File

@ -1198,14 +1198,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
}
} else if (childAtomType == Atom.TYPE_colr) {
int colorType = parent.readInt();
boolean isNclx = colorType == TYPE_nclx;
if (isNclx || colorType == TYPE_nclc) {
if (colorType == TYPE_nclx || colorType == TYPE_nclc) {
// For more info on syntax, see Section 8.5.2.2 in ISO/IEC 14496-12:2012(E) and
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html.
int colorPrimaries = parent.readUnsignedShort();
int transferCharacteristics = parent.readUnsignedShort();
parent.skipBytes(2); // matrix_coefficients.
boolean fullRangeFlag = isNclx && (parent.readUnsignedByte() & 0b10000000) != 0;
// Only try and read full_range_flag if the box is long enough. It should be present in
// all colr boxes with type=nclx (Section 8.5.2.2 in ISO/IEC 14496-12:2012(E)) but some
// device cameras record videos with type=nclx without this final flag (and therefore
// size=18): https://github.com/google/ExoPlayer/issues/9332
boolean fullRangeFlag =
childAtomSize == 19 && (parent.readUnsignedByte() & 0b10000000) != 0;
colorInfo =
new ColorInfo(
ColorInfo.isoColorPrimariesToColorSpace(colorPrimaries),

View File

@ -103,6 +103,17 @@ public final class Mp4ExtractorTest {
Mp4Extractor::new, "media/mp4/sample_with_color_info.mp4", simulationConfig);
}
/**
* Test case for https://github.com/google/ExoPlayer/issues/9332. The file contains a colr box
* with size=18 and type=nclx. This is not valid according to the spec (size must be 19), but
* files like this exist in the wild.
*/
@Test
public void mp4Sample18ByteNclxColr() throws Exception {
ExtractorAsserts.assertBehavior(
Mp4Extractor::new, "media/mp4/sample_18byte_nclx_colr.mp4", simulationConfig);
}
@Test
public void mp4SampleWithDolbyTrueHDTrack() throws Exception {
ExtractorAsserts.assertBehavior(

View File

@ -0,0 +1,148 @@
seekMap:
isSeekable = true
duration = 1001000
getPosition(0) = [[timeUs=0, position=1160]]
getPosition(1) = [[timeUs=0, position=1160]]
getPosition(500500) = [[timeUs=0, position=1160]]
getPosition(1001000) = [[timeUs=0, position=1160]]
numberOfTracks = 1
track 0:
total output bytes = 89876
sample count = 30
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.64001F
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
colorInfo:
colorSpace = 1
colorRange = 2
colorTransfer = 3
hdrStaticInfo = length 0, hash 0
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
tracksEnded = true

View File

@ -0,0 +1,148 @@
seekMap:
isSeekable = true
duration = 1001000
getPosition(0) = [[timeUs=0, position=1160]]
getPosition(1) = [[timeUs=0, position=1160]]
getPosition(500500) = [[timeUs=0, position=1160]]
getPosition(1001000) = [[timeUs=0, position=1160]]
numberOfTracks = 1
track 0:
total output bytes = 89876
sample count = 30
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.64001F
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
colorInfo:
colorSpace = 1
colorRange = 2
colorTransfer = 3
hdrStaticInfo = length 0, hash 0
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
tracksEnded = true

View File

@ -0,0 +1,148 @@
seekMap:
isSeekable = true
duration = 1001000
getPosition(0) = [[timeUs=0, position=1160]]
getPosition(1) = [[timeUs=0, position=1160]]
getPosition(500500) = [[timeUs=0, position=1160]]
getPosition(1001000) = [[timeUs=0, position=1160]]
numberOfTracks = 1
track 0:
total output bytes = 89876
sample count = 30
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.64001F
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
colorInfo:
colorSpace = 1
colorRange = 2
colorTransfer = 3
hdrStaticInfo = length 0, hash 0
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
tracksEnded = true

View File

@ -0,0 +1,148 @@
seekMap:
isSeekable = true
duration = 1001000
getPosition(0) = [[timeUs=0, position=1160]]
getPosition(1) = [[timeUs=0, position=1160]]
getPosition(500500) = [[timeUs=0, position=1160]]
getPosition(1001000) = [[timeUs=0, position=1160]]
numberOfTracks = 1
track 0:
total output bytes = 89876
sample count = 30
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.64001F
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
colorInfo:
colorSpace = 1
colorRange = 2
colorTransfer = 3
hdrStaticInfo = length 0, hash 0
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
tracksEnded = true

View File

@ -0,0 +1,148 @@
seekMap:
isSeekable = true
duration = 1001000
getPosition(0) = [[timeUs=0, position=1160]]
getPosition(1) = [[timeUs=0, position=1160]]
getPosition(500500) = [[timeUs=0, position=1160]]
getPosition(1001000) = [[timeUs=0, position=1160]]
numberOfTracks = 1
track 0:
total output bytes = 89876
sample count = 30
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.64001F
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
colorInfo:
colorSpace = 1
colorRange = 2
colorTransfer = 3
hdrStaticInfo = length 0, hash 0
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
tracksEnded = true