diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/SingleSampleSeekMap.java b/libraries/extractor/src/main/java/androidx/media3/extractor/SingleSampleSeekMap.java new file mode 100644 index 0000000000..87d2b33c1b --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/SingleSampleSeekMap.java @@ -0,0 +1,67 @@ +/* + * Copyright 2023 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 androidx.media3.extractor; + +import androidx.media3.common.C; +import androidx.media3.common.util.UnstableApi; + +/** + * A {@link SeekMap} implementation that maps the given point back onto itself. + * + *
Used for single sample media. + */ +@UnstableApi +public final class SingleSampleSeekMap implements SeekMap { + private final long durationUs; + private final long startPosition; + + /** + * Creates an instance with {@code startPosition} set to 0. + * + * @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if the + * duration is unknown. + */ + public SingleSampleSeekMap(long durationUs) { + this(durationUs, /* startPosition= */ 0); + } + + /** + * Creates an instance. + * + * @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if the + * duration is unknown. + * @param startPosition The position (byte offset) of the start of the media. + */ + public SingleSampleSeekMap(long durationUs, long startPosition) { + this.durationUs = durationUs; + this.startPosition = startPosition; + } + + @Override + public boolean isSeekable() { + return true; + } + + @Override + public long getDurationUs() { + return durationUs; + } + + @Override + public SeekPoints getSeekPoints(long timeUs) { + return new SeekPoints(new SeekPoint(timeUs, startPosition)); + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/png/PngExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/png/PngExtractor.java index 8429bd807f..7ad335e2aa 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/png/PngExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/png/PngExtractor.java @@ -29,7 +29,7 @@ import androidx.media3.extractor.Extractor; import androidx.media3.extractor.ExtractorInput; import androidx.media3.extractor.ExtractorOutput; import androidx.media3.extractor.PositionHolder; -import androidx.media3.extractor.SeekMap; +import androidx.media3.extractor.SingleSampleSeekMap; import androidx.media3.extractor.TrackOutput; import java.io.IOException; import java.lang.annotation.Documented; @@ -42,7 +42,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @UnstableApi // TODO: b/289989902 - Move methods of this class into ImageExtractorUtil once there are multiple // image extractors. -public class PngExtractor implements Extractor { +public final class PngExtractor implements Extractor { /** Parser states. */ @Documented @@ -110,7 +110,7 @@ public class PngExtractor implements Extractor { TrackOutput imageTrackOutput = extractorOutput.track(IMAGE_TRACK_ID, C.TRACK_TYPE_IMAGE); imageTrackOutput.format(new Format.Builder().setContainerMimeType(MimeTypes.IMAGE_PNG).build()); extractorOutput.endTracks(); - extractorOutput.seekMap(new SeekMap.Unseekable(/* durationUs= */ C.TIME_UNSET)); + extractorOutput.seekMap(new SingleSampleSeekMap(/* durationUs= */ C.TIME_UNSET)); state = STATE_READING_IMAGE; } diff --git a/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.0.dump b/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.0.dump index 59edcbfd97..e5081e39b9 100644 --- a/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.0.dump +++ b/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.0.dump @@ -1,7 +1,8 @@ seekMap: - isSeekable = false + isSeekable = true duration = UNSET TIME getPosition(0) = [[timeUs=0, position=0]] + getPosition(1) = [[timeUs=1, position=0]] numberOfTracks = 1 track 1024: total output bytes = 29063 diff --git a/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.unknown_length.dump index 59edcbfd97..e5081e39b9 100644 --- a/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.unknown_length.dump +++ b/libraries/test_data/src/test/assets/extractordumps/png/non-motion-photo-shortened.png.unknown_length.dump @@ -1,7 +1,8 @@ seekMap: - isSeekable = false + isSeekable = true duration = UNSET TIME getPosition(0) = [[timeUs=0, position=0]] + getPosition(1) = [[timeUs=1, position=0]] numberOfTracks = 1 track 1024: total output bytes = 29063