/* * Copyright (C) 2012 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.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.media.metrics.LogSessionId; import android.net.Uri; import android.os.IBinder; import android.os.IHwBinder; import android.os.PersistableBundle; import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; /** * MediaExtractor facilitates extraction of demuxed, typically encoded, media data * from a data source. *
It is generally used like this: *
* MediaExtractor extractor = new MediaExtractor(); * extractor.setDataSource(...); * int numTracks = extractor.getTrackCount(); * for (int i = 0; i < numTracks; ++i) { * MediaFormat format = extractor.getTrackFormat(i); * String mime = format.getString(MediaFormat.KEY_MIME); * if (weAreInterestedInThisTrack) { * extractor.selectTrack(i); * } * } * ByteBuffer inputBuffer = ByteBuffer.allocate(...) * while (extractor.readSampleData(inputBuffer, ...) >= 0) { * int trackIndex = extractor.getSampleTrackIndex(); * long presentationTimeUs = extractor.getSampleTime(); * ... * extractor.advance(); * } * * extractor.release(); * extractor = null; ** *
This class requires the {@link android.Manifest.permission#INTERNET} permission * when used with network-based content. */ public final class MediaExtractor { public MediaExtractor() { native_setup(); } /** * Sets the data source (MediaDataSource) to use. * * @param dataSource the MediaDataSource for the media you want to extract from * * @throws IllegalArgumentException if dataSource is invalid. */ public native final void setDataSource(@NonNull MediaDataSource dataSource) throws IOException; /** * Sets the data source as a content Uri. * * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to extract from. * *
When When When When
* @see MediaExtractor#setMediaCas
*
* @return a byte array containing the private data. A null return value
* indicates that the private data is unavailable. An empty array,
* on the other hand, indicates that the private data is empty
* (zero in length).
*/
@Nullable
public byte[] getPrivateData() {
return mPrivateData;
}
/**
* Retrieves the {@link MediaCas.Session} associated with a track. The
* session is needed to initialize a descrambler in order to decode the
* scrambled track. The session object can only be retrieved after a valid
* {@link MediaCas} object is set on the extractor.
*
* @see MediaExtractor#setMediaCas
* @see MediaDescrambler#setMediaCasSession
*
* @return a {@link MediaCas.Session} object associated with a track.
*/
public MediaCas.Session getSession() {
return mSession;
}
}
/**
* Retrieves the information about the conditional access system used to scramble
* a track.
*
* @param index of the track.
* @return an {@link CasInfo} object describing the conditional access system.
*/
public CasInfo getCasInfo(int index) {
Map
* The following table summarizes support for format keys across android releases:
*
* Notes: Note that that level information contained in the container many times
* does not match the level of the actual bitstream. You may want to clear the level using
* {@code MediaFormat.setString(KEY_LEVEL, null)} before using the track format to find a
* decoder that can play back a particular track.
* *Pixel (sample) aspect ratio is returned in the following
* keys. The display width can be calculated for example as:
*
* display-width = display-height * crop-width / crop-height * sar-width / sar-height
*
* Note:As of API 21, on success the position and limit of
* {@code byteBuf} is updated to point to the data just read.
* @param byteBuf the destination byte buffer
* @return the sample size (or -1 if no more samples are available).
*/
public native int readSampleData(@NonNull ByteBuffer byteBuf, int offset);
/**
* Returns the track index the current sample originates from (or -1
* if no more samples are available)
*/
public native int getSampleTrackIndex();
/**
* Returns the current sample's presentation time in microseconds.
* or -1 if no more samples are available.
*/
public native long getSampleTime();
/**
* @return size of the current sample in bytes or -1 if no more
* samples are available.
*/
public native long getSampleSize();
// Keep these in sync with their equivalents in NuMediaExtractor.h
/**
* The sample is a sync sample (or in {@link MediaCodec}'s terminology
* it is a key frame.)
*
* @see MediaCodec#BUFFER_FLAG_KEY_FRAME
*/
public static final int SAMPLE_FLAG_SYNC = 1;
/**
* The sample is (at least partially) encrypted, see also the documentation
* for {@link android.media.MediaCodec#queueSecureInputBuffer}
*/
public static final int SAMPLE_FLAG_ENCRYPTED = 2;
/**
* This indicates that the buffer only contains part of a frame,
* and the decoder should batch the data until a buffer without
* this flag appears before decoding the frame.
*
* @see MediaCodec#BUFFER_FLAG_PARTIAL_FRAME
*/
public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4;
/** @hide */
@IntDef(
flag = true,
value = {
SAMPLE_FLAG_SYNC,
SAMPLE_FLAG_ENCRYPTED,
SAMPLE_FLAG_PARTIAL_FRAME,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SampleFlag {}
/**
* Returns the current sample's flags.
*/
@SampleFlag
public native int getSampleFlags();
/**
* If the sample flags indicate that the current sample is at least
* partially encrypted, this call returns relevant information about
* the structure of the sample data required for decryption.
* @param info The android.media.MediaCodec.CryptoInfo structure
* to be filled in.
* @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED}
*/
public native boolean getSampleCryptoInfo(@NonNull MediaCodec.CryptoInfo info);
/**
* Returns an estimate of how much data is presently cached in memory
* expressed in microseconds. Returns -1 if that information is unavailable
* or not applicable (no cache).
*/
public native long getCachedDuration();
/**
* Returns true iff we are caching data and the cache has reached the
* end of the data stream (for now, a future seek may of course restart
* the fetching of data).
* This API only returns a meaningful result if {@link #getCachedDuration}
* indicates the presence of a cache, i.e. does NOT return -1.
*/
public native boolean hasCacheReachedEndOfStream();
/**
* Sets the {@link LogSessionId} for MediaExtractor.
*/
public void setLogSessionId(@NonNull LogSessionId logSessionId) {
mLogSessionId = Objects.requireNonNull(logSessionId);
native_setLogSessionId(logSessionId.getStringId());
}
/**
* Returns the {@link LogSessionId} for MediaExtractor.
*/
@NonNull
public LogSessionId getLogSessionId() {
return mLogSessionId;
}
/**
* Return Metrics data about the current media container.
*
* @return a {@link PersistableBundle} containing the set of attributes and values
* available for the media container being handled by this instance
* of MediaExtractor.
* The attributes are descibed in {@link MetricsConstants}.
*
* Additional vendor-specific fields may also be present in
* the return value.
*/
public PersistableBundle getMetrics() {
PersistableBundle bundle = native_getMetrics();
return bundle;
}
private native void native_setLogSessionId(String logSessionId);
private native PersistableBundle native_getMetrics();
private static native final void native_init();
private native final void native_setup();
private native final void native_finalize();
static {
System.loadLibrary("media_jni");
native_init();
}
private MediaCas mMediaCas;
@NonNull private LogSessionId mLogSessionId = LogSessionId.LOG_SESSION_ID_NONE;
private long mNativeContext;
public final static class MetricsConstants
{
private MetricsConstants() {}
/**
* Key to extract the container format
* from the {@link MediaExtractor#getMetrics} return value.
* The value is a String.
*/
public static final String FORMAT = "android.media.mediaextractor.fmt";
/**
* Key to extract the container MIME type
* from the {@link MediaExtractor#getMetrics} return value.
* The value is a String.
*/
public static final String MIME_TYPE = "android.media.mediaextractor.mime";
/**
* Key to extract the number of tracks in the container
* from the {@link MediaExtractor#getMetrics} return value.
* The value is an integer.
*/
public static final String TRACKS = "android.media.mediaextractor.ntrk";
}
}
uri
refers to a network file the
* {@link android.Manifest.permission#INTERNET} permission is required.
*
* @param headers the headers to be sent together with the request for the data.
* This can be {@code null} if no specific headers are to be sent with the
* request.
*/
public final void setDataSource(
@NonNull Context context, @NonNull Uri uri, @Nullable Mappath
refers to a network file the
* {@link android.Manifest.permission#INTERNET} permission is required.
*
* @param headers the headers associated with the http request for the stream you want to play.
* This can be {@code null} if no specific headers are to be sent with the
* request.
*/
public final void setDataSource(@NonNull String path, @Nullable Mappath
refers to a local file, the file may actually be opened by a
* process other than the calling application. This implies that the pathname
* should be an absolute path (as any other process runs with unspecified current working
* directory), and that the pathname should reference a world-readable file.
* As an alternative, the application could first open the file for reading,
* and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
*
* path
refers to a network file the
* {@link android.Manifest.permission#INTERNET} permission is required.
*/
public final void setDataSource(@NonNull String path) throws IOException {
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path),
path,
null,
null);
}
/**
* Sets the data source (AssetFileDescriptor) to use. It is the caller's
* responsibility to close the file descriptor. It is safe to do so as soon
* as this call returns.
*
* @param afd the AssetFileDescriptor for the file you want to extract from.
*/
public final void setDataSource(@NonNull AssetFileDescriptor afd)
throws IOException, IllegalArgumentException, IllegalStateException {
Preconditions.checkNotNull(afd);
// Note: using getDeclaredLength so that our behavior is the same
// as previous versions when the content provider is returning
// a full file.
if (afd.getDeclaredLength() < 0) {
setDataSource(afd.getFileDescriptor());
} else {
setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
}
}
/**
* Sets the data source (FileDescriptor) to use. It is the caller's responsibility
* to close the file descriptor. It is safe to do so as soon as this call returns.
*
* @param fd the FileDescriptor for the file you want to extract from.
*/
public final void setDataSource(@NonNull FileDescriptor fd) throws IOException {
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
/**
* Sets the data source (FileDescriptor) to use. The FileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
* to close the file descriptor. It is safe to do so as soon as this call returns.
*
* @param fd the FileDescriptor for the file you want to extract from.
* @param offset the offset into the file where the data to be extracted starts, in bytes
* @param length the length in bytes of the data to be extracted
*/
public native final void setDataSource(
@NonNull FileDescriptor fd, long offset, long length) throws IOException;
/**
* Sets the MediaCas instance to use. This should be called after a successful setDataSource()
* if at least one track reports mime type of
* {@link android.media.MediaFormat#MIMETYPE_AUDIO_SCRAMBLED} or
* {@link android.media.MediaFormat#MIMETYPE_VIDEO_SCRAMBLED}. Stream parsing will not proceed
* until a valid MediaCas object is provided.
*
* @param mediaCas the MediaCas object to use.
* @deprecated Use the {@code Descrambler} system API instead, or DRM public APIs like
* {@link MediaDrm}.
*/
@Deprecated
public final void setMediaCas(@NonNull MediaCas mediaCas) {
mMediaCas = mediaCas;
nativeSetMediaCas(mediaCas.getBinder());
}
private native final void nativeSetMediaCas(@NonNull IHwBinder casBinder);
/**
* Describes the conditional access system used to scramble a track.
*/
public static final class CasInfo {
private final int mSystemId;
private final MediaCas.Session mSession;
private final byte[] mPrivateData;
CasInfo(int systemId, @Nullable MediaCas.Session session, @Nullable byte[] privateData) {
mSystemId = systemId;
mSession = session;
mPrivateData = privateData;
}
/**
* Retrieves the system id of the conditional access system.
*
* @return CA system id of the CAS used to scramble the track.
*/
public int getSystemId() {
return mSystemId;
}
/**
* Retrieves the private data in the CA_Descriptor associated with a track.
* Some CAS systems may need this to initialize the CAS plugin object. This
* private data can only be retrieved before a valid {@link MediaCas} object
* is set on the extractor.
*
*
*
*
*/
@NonNull
public MediaFormat getTrackFormat(int index) {
return new MediaFormat(getTrackFormatNative(index));
}
@NonNull
private native Map
* OS Version(s)
* {@code MediaFormat} keys used for
*
*
*
*
* All Tracks
* Audio Tracks
* Video Tracks
*
* {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
* {@link MediaFormat#KEY_MIME},
*
* {@link MediaFormat#KEY_DURATION},
* {@link MediaFormat#KEY_MAX_INPUT_SIZE}{@link MediaFormat#KEY_SAMPLE_RATE},
*
* {@link MediaFormat#KEY_CHANNEL_COUNT},
* {@link MediaFormat#KEY_CHANNEL_MASK},
* gapless playback information.mp3, .mp4,
* {@link MediaFormat#KEY_IS_ADTS}AAC if streaming,
* codec-specific dataAAC, Vorbis{@link MediaFormat#KEY_WIDTH},
*
* {@link MediaFormat#KEY_HEIGHT},
* codec-specific dataAVC, MPEG4
* {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*
* {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
* as above, plus
*
* Pixel aspect ratio informationAVC, *
* {@link android.os.Build.VERSION_CODES#KITKAT}
*
* {@link android.os.Build.VERSION_CODES#KITKAT_WATCH}
*
* {@link android.os.Build.VERSION_CODES#LOLLIPOP}
* as above, plus
*
* {@link MediaFormat#KEY_BIT_RATE}AAC,
* codec-specific dataOpusas above, plus
*
* {@link MediaFormat#KEY_ROTATION}.mp4,
* {@link MediaFormat#KEY_BIT_RATE}MPEG4,
* codec-specific dataHEVC
* {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}
*
* {@link android.os.Build.VERSION_CODES#M}
* as above, plus
*
* gapless playback informationOpusas above, plus
*
* {@link MediaFormat#KEY_FRAME_RATE} (integer)
*
* {@link android.os.Build.VERSION_CODES#N}
* as above, plus
*
* {@link MediaFormat#KEY_TRACK_ID},
*
* {@link MediaFormat#KEY_BIT_RATE}#, .mp4as above, plus
*
* {@link MediaFormat#KEY_PCM_ENCODING},
* {@link MediaFormat#KEY_PROFILE}AACas above, plus
*
* {@link MediaFormat#KEY_HDR_STATIC_INFO}#, .webm,
* {@link MediaFormat#KEY_COLOR_STANDARD}#,
* {@link MediaFormat#KEY_COLOR_TRANSFER}#,
* {@link MediaFormat#KEY_COLOR_RANGE}#,
* {@link MediaFormat#KEY_PROFILE}MPEG2, H.263, MPEG4, AVC, HEVC, VP9,
* {@link MediaFormat#KEY_LEVEL}H.263, MPEG4, AVC, HEVC, VP9,
* codec-specific dataVP9
*
*
*
* #: container-specified value only.
* .mp4, .webm…: for listed containers
* MPEG4, AAC…: for listed codecs
*
*
*
*
*
*
*
* Format Key Value Type Description
*
* {@code "sar-width"} Integer Pixel aspect ratio width
*
*
*
* {@code "sar-height"} Integer Pixel aspect ratio height
*