Add APIs to set data source using AssetFileDescriptor & FileDescriptor

Introduced three `setDataSource` APIs in `MediaExtractorCompat`, enabling the use of `AssetFileDescriptor` and `FileDescriptor` to set the data source.

PiperOrigin-RevId: 653957035
This commit is contained in:
rohks 2024-07-19 03:57:54 -07:00 committed by Copybara-Service
parent 1e28755b4a
commit 0def3b215c

View File

@ -22,6 +22,7 @@ import static androidx.media3.exoplayer.source.SampleStream.FLAG_PEEK;
import static androidx.media3.exoplayer.source.SampleStream.FLAG_REQUIRE_FORMAT;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
@ -41,6 +42,7 @@ import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DataSourceUtil;
import androidx.media3.datasource.DataSpec;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.datasource.FileDescriptorDataSource;
import androidx.media3.decoder.DecoderInputBuffer;
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
import androidx.media3.exoplayer.source.SampleQueue;
@ -67,6 +69,7 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.EOFException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -136,9 +139,18 @@ public final class MediaExtractorCompat {
}
/**
* Creates a new instance using the given {@link ExtractorsFactory extractorsFactory} to create
* the {@link Extractor extractors} to use for obtaining media samples from a DataSource generated
* by the given {@link DataSource.Factory dataSourceFactory}.
* Creates a new instance using the given {@link ExtractorsFactory} to create the {@linkplain
* Extractor extractors} to use for obtaining media samples from a {@link DataSource} generated by
* the given {@link DataSource.Factory}.
*
* <p>Note: The {@link DataSource.Factory} provided will not be used to generate {@link
* DataSource} when setting data source using methods:
*
* <ul>
* <li>{@link #setDataSource(AssetFileDescriptor)}
* <li>{@link #setDataSource(FileDescriptor)}
* <li>{@link #setDataSource(FileDescriptor, long, long)}
* </ul>
*/
public MediaExtractorCompat(
ExtractorsFactory extractorsFactory, DataSource.Factory dataSourceFactory) {
@ -156,8 +168,8 @@ public final class MediaExtractorCompat {
}
/**
* Initializes the internal state with the media stream obtained from the given {@code uri} at the
* given {@code offset}.
* Sets the data source using the media stream obtained from the given {@link Uri} at the given
* {@code offset}.
*
* @param uri The content {@link Uri} to extract from.
* @param offset The offset into the file where the data to be extracted starts, in bytes.
@ -167,13 +179,75 @@ public final class MediaExtractorCompat {
* @throws IllegalStateException If this method is called twice on the same instance.
*/
public void setDataSource(Uri uri, long offset) throws IOException {
prepareDataSource(
dataSourceFactory.createDataSource(), buildDataSpec(uri, /* position= */ offset));
}
/**
* Sets the data source using the media stream obtained from the provided {@link
* AssetFileDescriptor}.
*
* <p>Note: The caller is responsible for closing the {@link AssetFileDescriptor}. It is safe to
* do so immediately after this method returns.
*
* @param assetFileDescriptor The {@link AssetFileDescriptor} for the file to extract from.
* @throws IOException If an error occurs while extracting the media.
* @throws UnrecognizedInputFormatException If none of the available extractors successfully
* sniffs the input.
* @throws IllegalStateException If this method is called twice on the same instance.
*/
public void setDataSource(AssetFileDescriptor assetFileDescriptor) throws IOException {
if (assetFileDescriptor.getLength() == AssetFileDescriptor.UNKNOWN_LENGTH) {
setDataSource(assetFileDescriptor.getFileDescriptor());
} else {
setDataSource(
assetFileDescriptor.getFileDescriptor(),
assetFileDescriptor.getStartOffset(),
assetFileDescriptor.getLength());
}
}
/**
* Sets the data source using the media stream obtained from the provided {@link FileDescriptor}.
*
* @param fileDescriptor The {@link FileDescriptor} for the file to extract from.
* @throws IOException If an error occurs while extracting the media.
* @throws UnrecognizedInputFormatException If none of the available extractors successfully
* sniffs the input.
* @throws IllegalStateException If this method is called twice on the same instance.
*/
public void setDataSource(FileDescriptor fileDescriptor) throws IOException {
setDataSource(fileDescriptor, /* offset= */ 0, /* length= */ C.LENGTH_UNSET);
}
/**
* Sets the data source using the media stream obtained from the provided {@link FileDescriptor},
* with a specified {@code offset} and {@code length}.
*
* @param fileDescriptor The {@link FileDescriptor} for the file to extract from.
* @param offset The offset into the file where the data to be extracted starts, in bytes.
* @param length The length of the data to be extracted, in bytes, or {@link C#LENGTH_UNSET} if it
* is unknown.
* @throws IOException If an error occurs while extracting the media.
* @throws UnrecognizedInputFormatException If none of the available extractors successfully
* sniffs the input.
* @throws IllegalStateException If this method is called twice on the same instance.
*/
public void setDataSource(FileDescriptor fileDescriptor, long offset, long length)
throws IOException {
FileDescriptorDataSource fileDescriptorDataSource =
new FileDescriptorDataSource(fileDescriptor, offset, length);
DataSpec dataSpec = new DataSpec(Uri.EMPTY);
prepareDataSource(fileDescriptorDataSource, dataSpec);
}
private void prepareDataSource(DataSource dataSource, DataSpec dataSpec) throws IOException {
// Assert that this instance is not being re-prepared, which is not currently supported.
Assertions.checkState(!hasBeenPrepared);
hasBeenPrepared = true;
offsetInCurrentFile = offset;
DataSpec dataSpec = buildDataSpec(uri, /* position= */ offsetInCurrentFile);
offsetInCurrentFile = dataSpec.position;
currentDataSource = dataSource;
currentDataSource = dataSourceFactory.createDataSource();
long length = currentDataSource.open(dataSpec);
ExtractorInput currentExtractorInput =
new DefaultExtractorInput(currentDataSource, /* position= */ 0, length);