From 00436a04a4f0fec8ee9154fc1568ca4013ca5c7d Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 27 Jan 2023 14:01:47 +0000 Subject: [PATCH] Fix timestamp comparison for seeks in fMP4 When seeking in fMP4, we try to extract as little samples as possible by only starting at the preceding sync frame. This comparison should use <= to allow sync frames at exactly the seek position. Issue: google/ExoPlayer#10941 #minor-release PiperOrigin-RevId: 505098172 --- RELEASENOTES.md | 3 + .../extractor/mp4/FragmentedMp4Extractor.java | 6 +- .../mp4/sample_ac3_fragmented.mp4.1.dump | 18 ++--- .../mp4/sample_ac3_fragmented.mp4.2.dump | 12 +-- .../mp4/sample_eac3_fragmented.mp4.1.dump | 78 +++++++++---------- .../mp4/sample_eac3_fragmented.mp4.2.dump | 42 +++++----- 6 files changed, 73 insertions(+), 86 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 531d9edef5..a0d569a5cb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -20,9 +20,12 @@ for seeking. * Use theme when loading drawables on API 21+ ([#220](https://github.com/androidx/media/issues/220)). +* Extractors: * Throw a ParserException instead of a NullPointerException if the sample table (stbl) is missing a required sample description (stsd) when parsing trak atoms. + * Correctly skip samples when seeking directly to a sync frame in fMP4 + ([#10941](https://github.com/google/ExoPlayer/issues/10941)). * Audio: * Use the compressed audio format bitrate to calculate the min buffer size for `AudioTrack` in direct playbacks (passthrough). diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index a4f1dfb794..9e3b45da20 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -1675,15 +1675,15 @@ public class FragmentedMp4Extractor implements Extractor { } /** - * Advances {@link #firstSampleToOutputIndex} to point to the sync sample before the specified - * seek time in the current fragment. + * Advances {@link #firstSampleToOutputIndex} to point to the sync sample at or before the + * specified seek time in the current fragment. * * @param timeUs The seek time, in microseconds. */ public void seek(long timeUs) { int searchIndex = currentSampleIndex; while (searchIndex < fragment.sampleCount - && fragment.getSamplePresentationTimeUs(searchIndex) < timeUs) { + && fragment.getSamplePresentationTimeUs(searchIndex) <= timeUs) { if (fragment.sampleIsSyncFrameTable[searchIndex]) { firstSampleToOutputIndex = searchIndex; } diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.1.dump index 0f902e441a..4bcb712c34 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.1.dump @@ -7,8 +7,8 @@ seekMap: getPosition(288000) = [[timeUs=0, position=636]] numberOfTracks = 1 track 0: - total output bytes = 10752 - sample count = 7 + total output bytes = 9216 + sample count = 6 format 0: averageBitrate = 384000 peakBitrate = 384000 @@ -18,30 +18,26 @@ track 0: sampleRate = 48000 language = und sample 0: - time = 64000 - flags = 1 - data = length 1536, hash 5D09685 - sample 1: time = 96000 flags = 1 data = length 1536, hash A9A24E44 - sample 2: + sample 1: time = 128000 flags = 1 data = length 1536, hash 6F856273 - sample 3: + sample 2: time = 160000 flags = 1 data = length 1536, hash B1737D3C - sample 4: + sample 3: time = 192000 flags = 1 data = length 1536, hash 98FDEB9D - sample 5: + sample 4: time = 224000 flags = 1 data = length 1536, hash 99B9B943 - sample 6: + sample 5: time = 256000 flags = 1 data = length 1536, hash AAD9FCD2 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.2.dump index d747be40c5..c03c03e6c0 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac3_fragmented.mp4.2.dump @@ -7,8 +7,8 @@ seekMap: getPosition(288000) = [[timeUs=0, position=636]] numberOfTracks = 1 track 0: - total output bytes = 6144 - sample count = 4 + total output bytes = 4608 + sample count = 3 format 0: averageBitrate = 384000 peakBitrate = 384000 @@ -18,18 +18,14 @@ track 0: sampleRate = 48000 language = und sample 0: - time = 160000 - flags = 1 - data = length 1536, hash B1737D3C - sample 1: time = 192000 flags = 1 data = length 1536, hash 98FDEB9D - sample 2: + sample 1: time = 224000 flags = 1 data = length 1536, hash 99B9B943 - sample 3: + sample 2: time = 256000 flags = 1 data = length 1536, hash AAD9FCD2 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump index 027e7eb633..e33b92c7bc 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump @@ -7,8 +7,8 @@ seekMap: getPosition(1728000) = [[timeUs=0, position=638]] numberOfTracks = 1 track 0: - total output bytes = 148000 - sample count = 37 + total output bytes = 144000 + sample count = 36 format 0: peakBitrate = 1000000 id = 1 @@ -17,150 +17,146 @@ track 0: sampleRate = 48000 language = und sample 0: - time = 544000 - flags = 1 - data = length 4000, hash 27F20D29 - sample 1: time = 576000 flags = 1 data = length 4000, hash 6F565894 - sample 2: + sample 1: time = 608000 flags = 1 data = length 4000, hash A6F07C4A - sample 3: + sample 2: time = 640000 flags = 1 data = length 4000, hash 3A0CA15C - sample 4: + sample 3: time = 672000 flags = 1 data = length 4000, hash DB365414 - sample 5: + sample 4: time = 704000 flags = 1 data = length 4000, hash 31E08469 - sample 6: + sample 5: time = 736000 flags = 1 data = length 4000, hash 315F5C28 - sample 7: + sample 6: time = 768000 flags = 1 data = length 4000, hash CC65DF80 - sample 8: + sample 7: time = 800000 flags = 1 data = length 4000, hash 503FB64C - sample 9: + sample 8: time = 832000 flags = 1 data = length 4000, hash 817CF735 - sample 10: + sample 9: time = 864000 flags = 1 data = length 4000, hash 37391ADA - sample 11: + sample 10: time = 896000 flags = 1 data = length 4000, hash 37391ADA - sample 12: + sample 11: time = 928000 flags = 1 data = length 4000, hash 64DBF751 - sample 13: + sample 12: time = 960000 flags = 1 data = length 4000, hash 81AE828E - sample 14: + sample 13: time = 992000 flags = 1 data = length 4000, hash 767D6C98 - sample 15: + sample 14: time = 1024000 flags = 1 data = length 4000, hash A5F6D4E - sample 16: + sample 15: time = 1056000 flags = 1 data = length 4000, hash EABC6B0D - sample 17: + sample 16: time = 1088000 flags = 1 data = length 4000, hash F47EF742 - sample 18: + sample 17: time = 1120000 flags = 1 data = length 4000, hash 9B2549DA - sample 19: + sample 18: time = 1152000 flags = 1 data = length 4000, hash A12733C9 - sample 20: + sample 19: time = 1184000 flags = 1 data = length 4000, hash 95F62E99 - sample 21: + sample 20: time = 1216000 flags = 1 data = length 4000, hash A4D858 - sample 22: + sample 21: time = 1248000 flags = 1 data = length 4000, hash A4D858 - sample 23: + sample 22: time = 1280000 flags = 1 data = length 4000, hash 22C1A129 - sample 24: + sample 23: time = 1312000 flags = 1 data = length 4000, hash 2C51E4A1 - sample 25: + sample 24: time = 1344000 flags = 1 data = length 4000, hash 3782E8BB - sample 26: + sample 25: time = 1376000 flags = 1 data = length 4000, hash 2C51E4A1 - sample 27: + sample 26: time = 1408000 flags = 1 data = length 4000, hash BDB3D129 - sample 28: + sample 27: time = 1440000 flags = 1 data = length 4000, hash F642A55 - sample 29: + sample 28: time = 1472000 flags = 1 data = length 4000, hash 32F259F4 - sample 30: + sample 29: time = 1504000 flags = 1 data = length 4000, hash 4C987B7C - sample 31: + sample 30: time = 1536000 flags = 1 data = length 4000, hash 57C98E1C - sample 32: + sample 31: time = 1568000 flags = 1 data = length 4000, hash 4C987B7C - sample 33: + sample 32: time = 1600000 flags = 1 data = length 4000, hash 4C987B7C - sample 34: + sample 33: time = 1632000 flags = 1 data = length 4000, hash 4C987B7C - sample 35: + sample 34: time = 1664000 flags = 1 data = length 4000, hash 4C987B7C - sample 36: + sample 35: time = 1696000 flags = 1 data = length 4000, hash 4C987B7C diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump index db94e2636e..a079fe334e 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump @@ -7,8 +7,8 @@ seekMap: getPosition(1728000) = [[timeUs=0, position=638]] numberOfTracks = 1 track 0: - total output bytes = 76000 - sample count = 19 + total output bytes = 72000 + sample count = 18 format 0: peakBitrate = 1000000 id = 1 @@ -17,78 +17,74 @@ track 0: sampleRate = 48000 language = und sample 0: - time = 1120000 - flags = 1 - data = length 4000, hash 9B2549DA - sample 1: time = 1152000 flags = 1 data = length 4000, hash A12733C9 - sample 2: + sample 1: time = 1184000 flags = 1 data = length 4000, hash 95F62E99 - sample 3: + sample 2: time = 1216000 flags = 1 data = length 4000, hash A4D858 - sample 4: + sample 3: time = 1248000 flags = 1 data = length 4000, hash A4D858 - sample 5: + sample 4: time = 1280000 flags = 1 data = length 4000, hash 22C1A129 - sample 6: + sample 5: time = 1312000 flags = 1 data = length 4000, hash 2C51E4A1 - sample 7: + sample 6: time = 1344000 flags = 1 data = length 4000, hash 3782E8BB - sample 8: + sample 7: time = 1376000 flags = 1 data = length 4000, hash 2C51E4A1 - sample 9: + sample 8: time = 1408000 flags = 1 data = length 4000, hash BDB3D129 - sample 10: + sample 9: time = 1440000 flags = 1 data = length 4000, hash F642A55 - sample 11: + sample 10: time = 1472000 flags = 1 data = length 4000, hash 32F259F4 - sample 12: + sample 11: time = 1504000 flags = 1 data = length 4000, hash 4C987B7C - sample 13: + sample 12: time = 1536000 flags = 1 data = length 4000, hash 57C98E1C - sample 14: + sample 13: time = 1568000 flags = 1 data = length 4000, hash 4C987B7C - sample 15: + sample 14: time = 1600000 flags = 1 data = length 4000, hash 4C987B7C - sample 16: + sample 15: time = 1632000 flags = 1 data = length 4000, hash 4C987B7C - sample 17: + sample 16: time = 1664000 flags = 1 data = length 4000, hash 4C987B7C - sample 18: + sample 17: time = 1696000 flags = 1 data = length 4000, hash 4C987B7C