diff options
-rw-r--r-- | media/Android.bp | 13 | ||||
-rw-r--r-- | media/apex/java/android/media/MediaParser.java | 391 | ||||
-rw-r--r-- | media/jarjar_rules.txt | 1 |
3 files changed, 405 insertions, 0 deletions
diff --git a/media/Android.bp b/media/Android.bp index 2f75e4458ef5..022fa9b7bb9e 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -34,6 +34,11 @@ java_library { "android_system_stubs_current", ], + static_libs: [ + "exoplayer2-core" + ], + jarjar_rules: "jarjar_rules.txt", + plugins: ["java_api_finder"], } @@ -41,6 +46,7 @@ filegroup { name: "updatable-media-srcs", srcs: [ ":mediasession2-srcs", + ":mediaparser-srcs", ], } @@ -63,6 +69,13 @@ filegroup { path: "apex/java", } +filegroup { + name: "mediaparser-srcs", + srcs: [ + "apex/java/android/media/MediaParser.java" + ] +} + metalava_updatable_media_args = " --error UnhiddenSystemApi " + "--hide RequiresPermission " + "--hide MissingPermission --hide BroadcastBehavior " + diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java new file mode 100644 index 000000000000..c06e2837bcdc --- /dev/null +++ b/media/apex/java/android/media/MediaParser.java @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2019 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 android.media; + +import android.util.Pair; + +import java.io.IOException; +import java.util.List; + +/** + * Parses media container formats and extracts contained media samples and metadata. + * + * <p>This class provides access to a battery of low-level media container parsers. Each instance of + * this class is associated to a specific media parser implementation which is suitable for + * extraction from a specific media container format. The media parser implementation assignment + * depends on the factory method (see {@link #create} and {@link #createByName}) used to create the + * instance. + * + * <p>Users must implement the following to use this class. + * + * <ul> + * <li>{@link Input}: Provides the media containers bytes to parse. + * <li>{@link OutputCallback}: Provides a sink for all extracted data and metadata. + * </ul> + * + * TODO: Add usage example here. + */ +// @HiddenApi +public final class MediaParser { + + /** Maps seek positions to corresponding positions in the stream. */ + public interface SeekMap { + + /** Returned by {@link #getDurationUs()} when the duration is unknown. */ + int UNKNOWN_DURATION = Integer.MIN_VALUE; + + /** Returns whether seeking is supported. */ + boolean isSeekable(); + + /** + * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the + * duration is unknown. + */ + long getDurationUs(); + + /** + * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds. + * + * <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with + * timestamp equal to or smaller than {@code timeUs}. + * + * <p>{@code getSeekPoints(timeUs).second} contains the earlies seek point for samples with + * timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code + * timeUs}, the returned pair will contain the same {@link SeekPoint} twice. + * + * @param timeUs A seek time in microseconds. + * @return The corresponding {@link SeekPoint SeekPoints}. + */ + Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs); + } + + /** Defines a seek point in a media stream. */ + public static final class SeekPoint { + + /** A {@link SeekPoint} whose time and byte offset are both set to 0. */ + public static final SeekPoint START = new SeekPoint(0, 0); + + /** The time of the seek point, in microseconds. */ + public final long mTimeUs; + + /** The byte offset of the seek point. */ + public final long mPosition; + + /** + * @param timeUs The time of the seek point, in microseconds. + * @param position The byte offset of the seek point. + */ + public SeekPoint(long timeUs, long position) { + this.mTimeUs = timeUs; + this.mPosition = position; + } + + @Override + public String toString() { + return "[timeUs=" + mTimeUs + ", position=" + mPosition + "]"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + SeekPoint other = (SeekPoint) obj; + return mTimeUs == other.mTimeUs && mPosition == other.mPosition; + } + + @Override + public int hashCode() { + int result = (int) mTimeUs; + result = 31 * result + (int) mPosition; + return result; + } + } + + /** Provides input data to {@link MediaParser}. */ + public interface Input { + + /** + * Reads up to {@code readLength} bytes of data and stores them into {@code buffer}, + * starting at index {@code offset}. + * + * <p>The call will block until at least one byte of data has been read. + * + * @param buffer The buffer into which the read data should be stored. + * @param offset The start offset into {@code buffer} at which data should be written. + * @param readLength The maximum number of bytes to read. + * @return The non-zero number of bytes read, or -1 if no data is available because the end + * of the input has been reached. + * @throws java.io.IOException If an error occurs reading from the source. + */ + int read(byte[] buffer, int offset, int readLength) + throws IOException, InterruptedException; + + /** Returns the current read position (byte offset) in the stream. */ + long getPosition(); + + /** Returns the length of the input in bytes, or -1 if the length is unknown. */ + long getLength(); + } + + /** Receives extracted media sample data and metadata from {@link MediaParser}. */ + public interface OutputCallback { + + /** + * Called when the number of tracks is defined. + * + * @param numberOfTracks The number of tracks in the stream. + */ + void onTracksFound(int numberOfTracks); + + /** + * Called when a {@link SeekMap} has been extracted from the stream. + * + * @param seekMap The extracted {@link SeekMap}. + */ + void onSeekMap(SeekMap seekMap); + + /** + * Called when the {@link MediaFormat} of the track is extracted from the stream. + * + * @param trackIndex The index of the track for which the {@link MediaFormat} was found. + * @param format The extracted {@link MediaFormat}. + */ + void onFormat(int trackIndex, MediaFormat format); + + /** + * Called to write sample data to the output. + * + * <p>Implementers must attempt to consume the entirety of the input, but should surface any + * thrown {@link IOException} caused by reading from {@code input}. + * + * @param trackIndex The index of the track to which the sample data corresponds. + * @param input The {@link Input} from which to read the data. + * @return + */ + int onSampleData(int trackIndex, Input input) throws IOException, InterruptedException; + + /** + * Defines the boundaries and metadata of an extracted sample. + * + * <p>The corresponding sample data will have already been passed to the output via calls to + * {@link #onSampleData}. + * + * @param trackIndex The index of the track to which the sample corresponds. + * @param timeUs The media timestamp associated with the sample, in microseconds. + * @param flags Flags associated with the sample. See {@link MediaCodec + * MediaCodec.BUFFER_FLAG_*}. + * @param size The size of the sample data, in bytes. + * @param offset The number of bytes that have been passed to {@link #onSampleData} since + * the last byte belonging to the sample whose metadata is being passed. + * @param cryptoData Encryption data required to decrypt the sample. May be null for + * unencrypted samples. + */ + void onSampleCompleted( + int trackIndex, + long timeUs, + int flags, + int size, + int offset, + MediaCodec.CryptoInfo cryptoData); + } + + /** + * Controls the behavior of extractors' implementations. + * + * <p>DESIGN NOTE: For setting flags like workarounds and special behaviors for adaptive + * streaming. + */ + public static final class Parameters { + + // TODO: Implement. + + } + + /** Holds the result of an {@link #advance} invocation. */ + public static final class ResultHolder { + + /** Creates a new instance with {@link #result} holding {@link #ADVANCE_RESULT_CONTINUE}. */ + public ResultHolder() { + result = ADVANCE_RESULT_CONTINUE; + } + + /** + * May hold {@link #ADVANCE_RESULT_END_OF_INPUT}, {@link #ADVANCE_RESULT_CONTINUE}, {@link + * #ADVANCE_RESULT_SEEK}. + */ + public int result; + + /** + * If {@link #result} holds {@link #ADVANCE_RESULT_SEEK}, holds the stream position required + * from the passed {@link Input} to the next {@link #advance} call. If {@link #result} does + * not hold {@link #ADVANCE_RESULT_SEEK}, the value of this variable is undefined and should + * be ignored. + */ + public long seekPosition; + } + + /** + * Thrown if all extractors implementations provided to {@link #create} failed to sniff the + * input content. + */ + // @HiddenApi + public static final class UnrecognizedInputFormatException extends IOException { + + /** + * Creates a new instance which signals that the extractors with the given names failed to + * parse the input. + */ + public static UnrecognizedInputFormatException createForExtractors( + String... extractorNames) { + StringBuilder builder = new StringBuilder(); + builder.append("None of the available extractors ( "); + builder.append(extractorNames[0]); + for (int i = 1; i < extractorNames.length; i++) { + builder.append(", "); + builder.append(extractorNames[i]); + } + builder.append(") could read the stream."); + return new UnrecognizedInputFormatException(builder.toString()); + } + + private UnrecognizedInputFormatException(String extractorNames) { + super(extractorNames); + } + } + + // Public constants. + + /** + * Returned by {@link #advance} if the {@link Input} passed to the next {@link #advance} is + * required to provide data continuing from the position in the stream reached by the returning + * call. + */ + public static final int ADVANCE_RESULT_CONTINUE = -1; + /** Returned by {@link #advance} if the end of the {@link Input} was reached. */ + public static final int ADVANCE_RESULT_END_OF_INPUT = -2; + /** + * Returned by {@link #advance} when its next call expects a specific stream position, which + * will be held by {@link ResultHolder#seekPosition}. + */ + public static final int ADVANCE_RESULT_SEEK = -3; + + // Instance creation methods. + + /** + * Creates an instance backed by the extractor with the given {@code name}. The returned + * instance will attempt extraction without sniffing the content. + * + * @param name The name of the extractor that will be associated with the created instance. + * @param outputCallback The {@link OutputCallback} to which track data and samples are pushed. + * @param parameters Parameters that control specific aspects of the behavior of the extractors. + * @return A new instance. + */ + public static MediaParser createByName( + String name, OutputCallback outputCallback, Parameters parameters) { + throw new UnsupportedOperationException(); + } + + /** + * Creates an instance whose backing extractor will be selected by sniffing the content during + * the first {@link #advance} call. Extractor implementations will sniff the content in order of + * appearance in {@code extractorNames}. + * + * @param outputCallback The {@link OutputCallback} to track data and samples are obtained. + * @param parameters Parameters that control specific aspects of the behavior of the extractors. + * @param extractorNames The names of the extractors to sniff the content with. If empty, a + * default array of names is used. + * @return A new instance. + */ + public static MediaParser create( + OutputCallback outputCallback, Parameters parameters, String... extractorNames) { + throw new UnsupportedOperationException(); + } + + // Misc static methods. + + /** + * Returns an immutable list with the names of the extractors that are suitable for container + * formats with the given {@code mimeTypes}. If an empty string is passed, all available + * extractors' names are returned. + * + * <p>TODO: Replace string with media type object. + */ + public static List<String> getExtractorNames(String mimeTypes) { + throw new UnsupportedOperationException(); + } + + // Public methods. + + /** + * Returns the name of the backing extractor implementation. + * + * <p>If this instance was creating using {@link #createByName}, the provided name is returned. + * If this instance was created using {@link #create}, this method will return null until the + * first call to {@link #advance}, after which the name of the backing extractor implementation + * is returned. + * + * @return The name of the backing extractor implementation, or null if the backing extractor + * implementation has not yet been selected. + */ + public String getExtractorName() { + throw new UnsupportedOperationException(); + } + + /** + * Makes progress in the extraction of the input media stream, unless the end of the input has + * been reached. + * + * <p>This method will block until some progress has been made. + * + * <p>If this instance was created using {@link #create}. the first call to this method will + * sniff the content with the extractors with the provided names. + * + * @param input The {@link Input} from which to obtain the media container data. + * @param resultHolder The {@link ResultHolder} into which the result of the operation will be + * written. + * @throws UnrecognizedInputFormatException + */ + public void advance(Input input, ResultHolder resultHolder) + throws IOException, InterruptedException { + throw new UnsupportedOperationException(); + } + + /** + * Seeks within the media container being extracted. + * + * <p>Following a call to this method, the {@link Input} passed to the next invocation of {@link + * #advance} must provide data starting from {@link SeekPoint#mPosition} in the stream. + * + * @param seekPoint The {@link SeekPoint} to seek to. + */ + public void seek(SeekPoint seekPoint) { + throw new UnsupportedOperationException(); + } + + /** + * Releases any acquired resources. + * + * <p>After calling this method, this instance becomes unusable and no other methods should be + * invoked. DESIGN NOTE: Should be removed. There shouldn't be any resource for releasing. + */ + public void release() { + throw new UnsupportedOperationException(); + } +} diff --git a/media/jarjar_rules.txt b/media/jarjar_rules.txt new file mode 100644 index 000000000000..d89d9d3343d1 --- /dev/null +++ b/media/jarjar_rules.txt @@ -0,0 +1 @@ +rule com.google.android.exoplayer2.** android.media.internal.exo.@1 |