Skip to content

Commit e0ce5ed

Browse files
icbakercopybara-github
authored andcommitted
Ignore empty FLAC seek tables
This also ignores seek tables that only contain placeholder seek points. This change adds two tests which assert that the seeking behaviour is the same for a file with no seek table and a file that only contains placeholder seek points. `bear_no_seek_table.flac` was generated by copying `bear.flac` then modifying it with: ```shell $ metaflac --remove --block-type=SEEKTABLE bear_no_seek_table.flac ``` Then this was copied to `bear_placeholder_seek_point_only.flac` and modified to add a single placeholder seek point: ```shell $ metaflac --add-seekpoint=X bear_empty_seek_table.flac ``` I couldn't work out how to convince `metaflac` to either remove all seek points, or to add an empty seek table, but a seek table containing only a single placeholder seek point is effectively empty, because placeholder points are removed here: https://github.com/androidx/media/blob/839de45e8ac200c38c44b2d720ee798047c34739/libraries/extractor/src/main/java/androidx/media3/extractor/FlacMetadataReader.java#L202-L209 PiperOrigin-RevId: 761959629
1 parent cf0b67a commit e0ce5ed

File tree

10 files changed

+546
-1
lines changed

10 files changed

+546
-1
lines changed

RELEASENOTES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
This behavior can be enabled using the `FLAG_MERGE_FRAGMENTED_SIDX` flag
1616
on `FragmentedMp4Extractor`
1717
([#9373](https://github.com/google/ExoPlayer/issues/9373)).
18+
* Ignore empty seek tables in FLAC files (including those containing only
19+
placeholder seek points), and fall back to binary search seeking if the
20+
duration of the file is known
21+
([#2327]()https://github.com/androidx/media/issues/2327).
1822
* DataSource:
1923
* Audio:
2024
* Add support for all linear PCM sample formats in

libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ private void getFrameStartMarker(ExtractorInput input) throws IOException {
329329

330330
private SeekMap getSeekMap(long firstFramePosition, long streamLength) {
331331
Assertions.checkNotNull(flacStreamMetadata);
332-
if (flacStreamMetadata.seekTable != null) {
332+
if (flacStreamMetadata.seekTable != null
333+
&& flacStreamMetadata.seekTable.pointSampleNumbers.length > 0) {
333334
return new FlacSeekTableSeekMap(flacStreamMetadata, firstFramePosition);
334335
} else if (streamLength != C.LENGTH_UNSET && flacStreamMetadata.totalSamples > 0) {
335336
binarySearchSeeker =

libraries/extractor/src/test/java/androidx/media3/extractor/flac/FlacExtractorTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,31 @@ public void sampleWithId3HeaderAndId3Disabled() throws Exception {
7777
simulationConfig);
7878
}
7979

80+
@Test
81+
public void sampleWithNoSeekTable_usesBinarySeeker() throws Exception {
82+
ExtractorAsserts.assertBehavior(
83+
FlacExtractor::new,
84+
"media/flac/bear_no_seek_table.flac",
85+
new AssertionConfig.Builder()
86+
.setDumpFilesPrefix("extractordumps/flac/bear_binary_seeking_flac")
87+
.build(),
88+
simulationConfig);
89+
}
90+
91+
// https://github.com/androidx/media/issues/2327
92+
@Test
93+
public void sampleWithEmptySeekTable_usesBinarySeeker() throws Exception {
94+
// This test asserts that a file with an effectively empty seek table is handled
95+
// the same way as a file with no seek table.
96+
ExtractorAsserts.assertBehavior(
97+
FlacExtractor::new,
98+
"media/flac/bear_placeholder_seek_point_only.flac",
99+
new AssertionConfig.Builder()
100+
.setDumpFilesPrefix("extractordumps/flac/bear_binary_seeking_flac")
101+
.build(),
102+
simulationConfig);
103+
}
104+
80105
@Test
81106
public void sampleUnseekable() throws Exception {
82107
ExtractorAsserts.assertBehavior(
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
seekMap:
2+
isSeekable = true
3+
duration = 2741000
4+
getPosition(0) = [[timeUs=0, position=8880]]
5+
getPosition(1) = [[timeUs=1, position=8880]]
6+
getPosition(1370500) = [[timeUs=1370500, position=83874]]
7+
getPosition(2741000) = [[timeUs=2741000, position=161977]]
8+
numberOfTracks = 1
9+
track 0:
10+
total output bytes = 164431
11+
sample count = 33
12+
track duration = 2741000
13+
format 0:
14+
containerMimeType = audio/flac
15+
sampleMimeType = audio/flac
16+
maxInputSize = 5776
17+
channelCount = 2
18+
sampleRate = 48000
19+
pcmEncoding = 2
20+
initializationData:
21+
data = length 42, hash 83F6895
22+
sample 0:
23+
time = 0
24+
flags = 1
25+
data = length 5030, hash D2B60530
26+
sample 1:
27+
time = 85333
28+
flags = 1
29+
data = length 5066, hash 4C932A54
30+
sample 2:
31+
time = 170666
32+
flags = 1
33+
data = length 5112, hash 7E5A7B61
34+
sample 3:
35+
time = 256000
36+
flags = 1
37+
data = length 5044, hash 7EF93F13
38+
sample 4:
39+
time = 341333
40+
flags = 1
41+
data = length 4943, hash DE7E27F8
42+
sample 5:
43+
time = 426666
44+
flags = 1
45+
data = length 5121, hash 6D0D0B40
46+
sample 6:
47+
time = 512000
48+
flags = 1
49+
data = length 5068, hash 9924644F
50+
sample 7:
51+
time = 597333
52+
flags = 1
53+
data = length 5143, hash 6C34F0CE
54+
sample 8:
55+
time = 682666
56+
flags = 1
57+
data = length 5109, hash E3B7BEFB
58+
sample 9:
59+
time = 768000
60+
flags = 1
61+
data = length 5129, hash 44111D9B
62+
sample 10:
63+
time = 853333
64+
flags = 1
65+
data = length 5031, hash 9D55EA53
66+
sample 11:
67+
time = 938666
68+
flags = 1
69+
data = length 5119, hash E1CB9BA6
70+
sample 12:
71+
time = 1024000
72+
flags = 1
73+
data = length 5360, hash 17265C5D
74+
sample 13:
75+
time = 1109333
76+
flags = 1
77+
data = length 5340, hash A90FDDF1
78+
sample 14:
79+
time = 1194666
80+
flags = 1
81+
data = length 5162, hash 31F65AD5
82+
sample 15:
83+
time = 1280000
84+
flags = 1
85+
data = length 5168, hash F2394F2D
86+
sample 16:
87+
time = 1365333
88+
flags = 1
89+
data = length 5776, hash 58437AB3
90+
sample 17:
91+
time = 1450666
92+
flags = 1
93+
data = length 5394, hash EBAB20A8
94+
sample 18:
95+
time = 1536000
96+
flags = 1
97+
data = length 5168, hash BF37C7A5
98+
sample 19:
99+
time = 1621333
100+
flags = 1
101+
data = length 5324, hash 59546B7B
102+
sample 20:
103+
time = 1706666
104+
flags = 1
105+
data = length 5172, hash 6036EF0B
106+
sample 21:
107+
time = 1792000
108+
flags = 1
109+
data = length 5102, hash 5A131071
110+
sample 22:
111+
time = 1877333
112+
flags = 1
113+
data = length 5111, hash 3D9EBB3B
114+
sample 23:
115+
time = 1962666
116+
flags = 1
117+
data = length 5113, hash 61101D4F
118+
sample 24:
119+
time = 2048000
120+
flags = 1
121+
data = length 5229, hash D2E55742
122+
sample 25:
123+
time = 2133333
124+
flags = 1
125+
data = length 5162, hash 7F2E97FA
126+
sample 26:
127+
time = 2218666
128+
flags = 1
129+
data = length 5255, hash D92A782
130+
sample 27:
131+
time = 2304000
132+
flags = 1
133+
data = length 5196, hash 98FE5138
134+
sample 28:
135+
time = 2389333
136+
flags = 1
137+
data = length 5214, hash 3D35C38C
138+
sample 29:
139+
time = 2474666
140+
flags = 1
141+
data = length 5211, hash 7E25420F
142+
sample 30:
143+
time = 2560000
144+
flags = 1
145+
data = length 5230, hash 2AD96FBC
146+
sample 31:
147+
time = 2645333
148+
flags = 1
149+
data = length 3384, hash 938BCDD9
150+
sample 32:
151+
time = 2730666
152+
flags = 1
153+
data = length 445, hash A388E3D6
154+
tracksEnded = true
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
seekMap:
2+
isSeekable = true
3+
duration = 2741000
4+
getPosition(0) = [[timeUs=0, position=8880]]
5+
getPosition(1) = [[timeUs=1, position=8880]]
6+
getPosition(1370500) = [[timeUs=1370500, position=83874]]
7+
getPosition(2741000) = [[timeUs=2741000, position=161977]]
8+
numberOfTracks = 1
9+
track 0:
10+
total output bytes = 113666
11+
sample count = 23
12+
track duration = 2741000
13+
format 0:
14+
containerMimeType = audio/flac
15+
sampleMimeType = audio/flac
16+
maxInputSize = 5776
17+
channelCount = 2
18+
sampleRate = 48000
19+
pcmEncoding = 2
20+
initializationData:
21+
data = length 42, hash 83F6895
22+
sample 0:
23+
time = 853333
24+
flags = 1
25+
data = length 5031, hash 9D55EA53
26+
sample 1:
27+
time = 938666
28+
flags = 1
29+
data = length 5119, hash E1CB9BA6
30+
sample 2:
31+
time = 1024000
32+
flags = 1
33+
data = length 5360, hash 17265C5D
34+
sample 3:
35+
time = 1109333
36+
flags = 1
37+
data = length 5340, hash A90FDDF1
38+
sample 4:
39+
time = 1194666
40+
flags = 1
41+
data = length 5162, hash 31F65AD5
42+
sample 5:
43+
time = 1280000
44+
flags = 1
45+
data = length 5168, hash F2394F2D
46+
sample 6:
47+
time = 1365333
48+
flags = 1
49+
data = length 5776, hash 58437AB3
50+
sample 7:
51+
time = 1450666
52+
flags = 1
53+
data = length 5394, hash EBAB20A8
54+
sample 8:
55+
time = 1536000
56+
flags = 1
57+
data = length 5168, hash BF37C7A5
58+
sample 9:
59+
time = 1621333
60+
flags = 1
61+
data = length 5324, hash 59546B7B
62+
sample 10:
63+
time = 1706666
64+
flags = 1
65+
data = length 5172, hash 6036EF0B
66+
sample 11:
67+
time = 1792000
68+
flags = 1
69+
data = length 5102, hash 5A131071
70+
sample 12:
71+
time = 1877333
72+
flags = 1
73+
data = length 5111, hash 3D9EBB3B
74+
sample 13:
75+
time = 1962666
76+
flags = 1
77+
data = length 5113, hash 61101D4F
78+
sample 14:
79+
time = 2048000
80+
flags = 1
81+
data = length 5229, hash D2E55742
82+
sample 15:
83+
time = 2133333
84+
flags = 1
85+
data = length 5162, hash 7F2E97FA
86+
sample 16:
87+
time = 2218666
88+
flags = 1
89+
data = length 5255, hash D92A782
90+
sample 17:
91+
time = 2304000
92+
flags = 1
93+
data = length 5196, hash 98FE5138
94+
sample 18:
95+
time = 2389333
96+
flags = 1
97+
data = length 5214, hash 3D35C38C
98+
sample 19:
99+
time = 2474666
100+
flags = 1
101+
data = length 5211, hash 7E25420F
102+
sample 20:
103+
time = 2560000
104+
flags = 1
105+
data = length 5230, hash 2AD96FBC
106+
sample 21:
107+
time = 2645333
108+
flags = 1
109+
data = length 3384, hash 938BCDD9
110+
sample 22:
111+
time = 2730666
112+
flags = 1
113+
data = length 445, hash A388E3D6
114+
tracksEnded = true
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
seekMap:
2+
isSeekable = true
3+
duration = 2741000
4+
getPosition(0) = [[timeUs=0, position=8880]]
5+
getPosition(1) = [[timeUs=1, position=8880]]
6+
getPosition(1370500) = [[timeUs=1370500, position=83874]]
7+
getPosition(2741000) = [[timeUs=2741000, position=161977]]
8+
numberOfTracks = 1
9+
track 0:
10+
total output bytes = 55652
11+
sample count = 12
12+
track duration = 2741000
13+
format 0:
14+
containerMimeType = audio/flac
15+
sampleMimeType = audio/flac
16+
maxInputSize = 5776
17+
channelCount = 2
18+
sampleRate = 48000
19+
pcmEncoding = 2
20+
initializationData:
21+
data = length 42, hash 83F6895
22+
sample 0:
23+
time = 1792000
24+
flags = 1
25+
data = length 5102, hash 5A131071
26+
sample 1:
27+
time = 1877333
28+
flags = 1
29+
data = length 5111, hash 3D9EBB3B
30+
sample 2:
31+
time = 1962666
32+
flags = 1
33+
data = length 5113, hash 61101D4F
34+
sample 3:
35+
time = 2048000
36+
flags = 1
37+
data = length 5229, hash D2E55742
38+
sample 4:
39+
time = 2133333
40+
flags = 1
41+
data = length 5162, hash 7F2E97FA
42+
sample 5:
43+
time = 2218666
44+
flags = 1
45+
data = length 5255, hash D92A782
46+
sample 6:
47+
time = 2304000
48+
flags = 1
49+
data = length 5196, hash 98FE5138
50+
sample 7:
51+
time = 2389333
52+
flags = 1
53+
data = length 5214, hash 3D35C38C
54+
sample 8:
55+
time = 2474666
56+
flags = 1
57+
data = length 5211, hash 7E25420F
58+
sample 9:
59+
time = 2560000
60+
flags = 1
61+
data = length 5230, hash 2AD96FBC
62+
sample 10:
63+
time = 2645333
64+
flags = 1
65+
data = length 3384, hash 938BCDD9
66+
sample 11:
67+
time = 2730666
68+
flags = 1
69+
data = length 445, hash A388E3D6
70+
tracksEnded = true

0 commit comments

Comments
 (0)