diff --git a/media/Android.bp b/media/Android.bp index 2f75e4458ef5e..022fa9b7bb9e4 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 0000000000000..c06e2837bcdc0 --- /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. + * + *
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. + * + *
Users must implement the following to use this class. + * + *
{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with + * timestamp equal to or smaller than {@code timeUs}. + * + *
{@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 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * TODO: Replace string with media type object.
+ */
+ public static List 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.
+ *
+ * This method will block until some progress has been made.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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 0000000000000..d89d9d3343d11
--- /dev/null
+++ b/media/jarjar_rules.txt
@@ -0,0 +1 @@
+rule com.google.android.exoplayer2.** android.media.internal.exo.@1