Commit 365812e8 by andrewlewis Committed by Oliver Woodman

Remove FrameworkSampleSource.

ExtractorSampleSource covers almost all use cases (except for AMR, AVI and
legacy Widevine). The FLAC extension must be compiled for support for FLAC
extraction.

FrameworkSampleSource has device-specific issues, makes blocking calls to the
underlying MediaExtractor, and does not provide much control over buffering.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=124850102
parent a6b7cfb5
...@@ -18,7 +18,6 @@ package com.google.android.exoplayer; ...@@ -18,7 +18,6 @@ package com.google.android.exoplayer;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.media.MediaExtractor;
/** /**
* Compatibility wrapper around {@link android.media.MediaCodec.CryptoInfo}. * Compatibility wrapper around {@link android.media.MediaCodec.CryptoInfo}.
...@@ -73,22 +72,6 @@ public final class CryptoInfo { ...@@ -73,22 +72,6 @@ public final class CryptoInfo {
} }
/** /**
* Equivalent to {@link MediaExtractor#getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo)}.
*
* @param extractor The extractor from which to retrieve the crypto information.
*/
@TargetApi(16)
public void setFromExtractorV16(MediaExtractor extractor) {
extractor.getSampleCryptoInfo(frameworkCryptoInfo);
numSubSamples = frameworkCryptoInfo.numSubSamples;
numBytesOfClearData = frameworkCryptoInfo.numBytesOfClearData;
numBytesOfEncryptedData = frameworkCryptoInfo.numBytesOfEncryptedData;
key = frameworkCryptoInfo.key;
iv = frameworkCryptoInfo.iv;
mode = frameworkCryptoInfo.mode;
}
/**
* Returns an equivalent {@link android.media.MediaCodec.CryptoInfo} instance. * Returns an equivalent {@link android.media.MediaCodec.CryptoInfo} instance.
* <p> * <p>
* Successive calls to this method on a single {@link CryptoInfo} will return the same instance. * Successive calls to this method on a single {@link CryptoInfo} will return the same instance.
......
...@@ -442,18 +442,6 @@ public final class Format implements Parcelable { ...@@ -442,18 +442,6 @@ public final class Format implements Parcelable {
return frameworkMediaFormat; return frameworkMediaFormat;
} }
/**
* Sets the {@link MediaFormat} returned by {@link #getFrameworkMediaFormatV16()}.
*
* @deprecated This method only exists for FrameworkSampleSource, which is itself deprecated.
* @param frameworkSampleFormat The format.
*/
@Deprecated
@TargetApi(16)
/* package */ final void setFrameworkMediaFormatV16(MediaFormat frameworkSampleFormat) {
this.frameworkMediaFormat = frameworkSampleFormat;
}
@Override @Override
public String toString() { public String toString() {
return "Format(" + id + ", " + containerMimeType + ", " + sampleMimeType + ", " + bitrate + ", " return "Format(" + id + ", " + containerMimeType + ", " + sampleMimeType + ", " + bitrate + ", "
......
/*
* Copyright (C) 2014 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 com.google.android.exoplayer;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Extracts samples from a stream using Android's {@link MediaExtractor}.
* <p>
* Warning - This class is marked as deprecated because there are known device specific issues
* associated with its use, including playbacks not starting, playbacks stuttering and other
* miscellaneous failures. For mp4, m4a, mp3, webm, mkv, mpeg-ts, ogg, wav and aac playbacks it is
* strongly recommended to use {@link ExtractorSampleSource} instead. Where this is not possible
* this class can still be used, but please be aware of the associated risks. Playing container
* formats for which an ExoPlayer extractor does not yet exist (e.g. avi) is a valid use case of
* this class.
* <p>
* Over time we hope to enhance {@link ExtractorSampleSource} to support more formats, and hence
* make use of this class unnecessary.
*/
// TODO: This implementation needs to be fixed so that its methods are non-blocking (either
// through use of a background thread, or through changes to the framework's MediaExtractor API).
@Deprecated
@TargetApi(16)
public final class FrameworkSampleSource implements SampleSource {
private static final int TRACK_STATE_DISABLED = 0;
private static final int TRACK_STATE_ENABLED = 1;
private static final int TRACK_STATE_FORMAT_SENT = 2;
// Parameters for a Uri data source.
private final Context context;
private final Uri uri;
private final Map<String, String> headers;
// Parameters for a FileDescriptor data source.
private final FileDescriptor fileDescriptor;
private final long fileDescriptorOffset;
private final long fileDescriptorLength;
private boolean prepared;
private boolean notifyReset;
private long durationUs;
private MediaExtractor extractor;
private TrackGroupArray tracks;
private int[] trackStates;
private int enabledTrackCount;
private long lastSeekPositionUs;
private long pendingSeekPositionUs;
/**
* Instantiates a new sample extractor reading from the specified {@code uri}.
*
* @param context Context for resolving {@code uri}.
* @param uri The content URI from which to extract data.
* @param headers Headers to send with requests for data.
*/
public FrameworkSampleSource(Context context, Uri uri, Map<String, String> headers) {
Assertions.checkState(Util.SDK_INT >= 16);
this.context = Assertions.checkNotNull(context);
this.uri = Assertions.checkNotNull(uri);
this.headers = headers;
fileDescriptor = null;
fileDescriptorOffset = 0;
fileDescriptorLength = 0;
durationUs = C.UNSET_TIME_US;
}
/**
* Instantiates a new sample extractor reading from the specified seekable {@code fileDescriptor}.
* The caller is responsible for releasing the file descriptor.
*
* @param fileDescriptor File descriptor from which to read.
* @param fileDescriptorOffset The offset in bytes where the data to be extracted starts.
* @param fileDescriptorLength The length in bytes of the data to be extracted.
*/
public FrameworkSampleSource(FileDescriptor fileDescriptor, long fileDescriptorOffset,
long fileDescriptorLength) {
Assertions.checkState(Util.SDK_INT >= 16);
this.fileDescriptor = Assertions.checkNotNull(fileDescriptor);
this.fileDescriptorOffset = fileDescriptorOffset;
this.fileDescriptorLength = fileDescriptorLength;
context = null;
uri = null;
headers = null;
durationUs = C.UNSET_TIME_US;
}
// SampleSource implementation.
@Override
public boolean prepare(long positionUs) throws IOException {
if (prepared) {
return true;
}
extractor = new MediaExtractor();
if (context != null) {
extractor.setDataSource(context, uri, headers);
} else {
extractor.setDataSource(fileDescriptor, fileDescriptorOffset, fileDescriptorLength);
}
trackStates = new int[extractor.getTrackCount()];
TrackGroup[] trackArray = new TrackGroup[trackStates.length];
DrmInitData drmInitData = Util.SDK_INT >= 18 ? getDrmInitDataV18() : null;
for (int i = 0; i < trackStates.length; i++) {
MediaFormat format = extractor.getTrackFormat(i);
if (format.containsKey(MediaFormat.KEY_DURATION)) {
durationUs = Math.max(durationUs, format.getLong(MediaFormat.KEY_DURATION));
}
trackArray[i] = new TrackGroup(createFormat(i, format, drmInitData));
}
tracks = new TrackGroupArray(trackArray);
prepared = true;
return true;
}
@Override
public long getDurationUs() {
return durationUs;
}
@Override
public TrackGroupArray getTrackGroups() {
return tracks;
}
@Override
public TrackStream[] selectTracks(List<TrackStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) {
Assertions.checkState(prepared);
// Unselect old tracks.
for (int i = 0; i < oldStreams.size(); i++) {
int track = ((TrackStreamImpl) oldStreams.get(i)).track;
Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
enabledTrackCount--;
trackStates[track] = TRACK_STATE_DISABLED;
extractor.unselectTrack(track);
}
// Select new tracks.
TrackStream[] newStreams = new TrackStream[newSelections.size()];
for (int i = 0; i < newStreams.length; i++) {
TrackSelection selection = newSelections.get(i);
Assertions.checkState(selection.length == 1);
Assertions.checkState(selection.getTrack(0) == 0);
int track = selection.group;
Assertions.checkState(trackStates[track] == TRACK_STATE_DISABLED);
enabledTrackCount++;
trackStates[track] = TRACK_STATE_ENABLED;
extractor.selectTrack(track);
newStreams[i] = new TrackStreamImpl(track);
}
// Seek if necessary.
if (enabledTrackCount > 0) {
seekToUsInternal(positionUs, positionUs != 0);
}
return newStreams;
}
@Override
public void continueBuffering(long positionUs) {
// MediaExtractor takes care of buffering. Do nothing.
}
@Override
public long readReset() {
if (notifyReset) {
notifyReset = false;
return lastSeekPositionUs;
}
return C.UNSET_TIME_US;
}
@Override
public void seekToUs(long positionUs) {
if (enabledTrackCount == 0) {
return;
}
seekToUsInternal(positionUs, false);
}
@Override
public long getBufferedPositionUs() {
if (enabledTrackCount == 0) {
return C.END_OF_SOURCE_US;
}
long bufferedDurationUs = extractor.getCachedDuration();
if (bufferedDurationUs == -1) {
return C.UNSET_TIME_US;
}
long sampleTime = extractor.getSampleTime();
return sampleTime == -1 ? C.END_OF_SOURCE_US : sampleTime + bufferedDurationUs;
}
@Override
public void release() {
if (extractor != null) {
extractor.release();
extractor = null;
}
}
// TrackStream methods.
/* package */ int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer) {
Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
if (notifyReset) {
return TrackStream.NOTHING_READ;
}
if (trackStates[track] != TRACK_STATE_FORMAT_SENT) {
formatHolder.format = tracks.get(track).getFormat(0);
trackStates[track] = TRACK_STATE_FORMAT_SENT;
return TrackStream.FORMAT_READ;
}
int extractorTrackIndex = extractor.getSampleTrackIndex();
if (extractorTrackIndex == track) {
ByteBuffer bufferData = buffer.data;
int offset = bufferData.position();
int size = extractor.readSampleData(bufferData, offset);
bufferData.position(offset + size);
buffer.timeUs = extractor.getSampleTime();
int flags = extractor.getSampleFlags();
if ((flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
}
if ((flags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
buffer.addFlag(C.BUFFER_FLAG_ENCRYPTED);
buffer.cryptoInfo.setFromExtractorV16(extractor);
}
pendingSeekPositionUs = C.UNSET_TIME_US;
extractor.advance();
return TrackStream.BUFFER_READ;
} else if (extractorTrackIndex < 0) {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
return TrackStream.BUFFER_READ;
} else {
return TrackStream.NOTHING_READ;
}
}
// Internal methods.
@TargetApi(18)
private DrmInitData getDrmInitDataV18() {
// MediaExtractor only supports psshInfo for MP4, so it's ok to hard code the mimeType here.
Map<UUID, byte[]> psshInfo = extractor.getPsshInfo();
if (psshInfo == null || psshInfo.isEmpty()) {
return null;
}
SchemeData[] schemeDatas = new SchemeData[psshInfo.size()];
int count = 0;
for (UUID uuid : psshInfo.keySet()) {
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid));
schemeDatas[count++] = new SchemeData(uuid, MimeTypes.VIDEO_MP4, psshAtom);
}
return new DrmInitData(schemeDatas);
}
private void seekToUsInternal(long positionUs, boolean force) {
// Unless forced, avoid duplicate calls to the underlying extractor's seek method in the case
// that there have been no interleaving calls to readSample.
if (force || pendingSeekPositionUs != positionUs) {
lastSeekPositionUs = positionUs;
pendingSeekPositionUs = positionUs;
extractor.seekTo(positionUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
notifyReset = true;
}
}
@SuppressLint("InlinedApi")
private static Format createFormat(int index, MediaFormat mediaFormat, DrmInitData drmInitData) {
String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
String language = getOptionalStringV16(mediaFormat, MediaFormat.KEY_LANGUAGE);
int maxInputSize = getOptionalIntegerV16(mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE);
int width = getOptionalIntegerV16(mediaFormat, MediaFormat.KEY_WIDTH);
int height = getOptionalIntegerV16(mediaFormat, MediaFormat.KEY_HEIGHT);
float frameRate;
try {
frameRate = getOptionalIntegerV16(mediaFormat, MediaFormat.KEY_FRAME_RATE);
} catch (ClassCastException e) {
// There's an entry for KEY_FRAME_RATE but it's not a integer. It must be a float.
frameRate = getOptionalFloatV16(mediaFormat, MediaFormat.KEY_FRAME_RATE);
}
int rotationDegrees = getOptionalIntegerV16(mediaFormat, "rotation-degrees");
int channelCount = getOptionalIntegerV16(mediaFormat, MediaFormat.KEY_CHANNEL_COUNT);
int sampleRate = getOptionalIntegerV16(mediaFormat, MediaFormat.KEY_SAMPLE_RATE);
int encoderDelay = getOptionalIntegerV16(mediaFormat, "encoder-delay");
int encoderPadding = getOptionalIntegerV16(mediaFormat, "encoder-padding");
ArrayList<byte[]> initializationData = new ArrayList<>();
for (int i = 0; mediaFormat.containsKey("csd-" + i); i++) {
ByteBuffer buffer = mediaFormat.getByteBuffer("csd-" + i);
byte[] data = new byte[buffer.limit()];
buffer.get(data);
initializationData.add(data);
buffer.flip();
}
int pcmEncoding = MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT : Format.NO_VALUE;
Format format = new Format(Integer.toString(index), null, mimeType, null, Format.NO_VALUE,
maxInputSize, width, height, frameRate, rotationDegrees, Format.NO_VALUE, channelCount,
sampleRate, pcmEncoding, encoderDelay, encoderPadding, 0, language,
Format.OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false);
format.setFrameworkMediaFormatV16(mediaFormat);
return format;
}
@TargetApi(16)
private static String getOptionalStringV16(MediaFormat format, String key) {
return format.containsKey(key) ? format.getString(key) : null;
}
@TargetApi(16)
private static int getOptionalIntegerV16(MediaFormat format, String key) {
return format.containsKey(key) ? format.getInteger(key) : Format.NO_VALUE;
}
@TargetApi(16)
private static float getOptionalFloatV16(MediaFormat format, String key) {
return format.containsKey(key) ? format.getFloat(key) : Format.NO_VALUE;
}
private final class TrackStreamImpl implements TrackStream {
private final int track;
public TrackStreamImpl(int track) {
this.track = track;
}
@Override
public boolean isReady() {
// MediaExtractor takes care of buffering and blocks until it has samples, so we can always
// return true here. Although note that the blocking behavior is itself as bug, as per the
// TODO further up this file. This method will need to return something else as part of fixing
// the TODO.
return true;
}
@Override
public void maybeThrowError() throws IOException {
// Do nothing.
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
return FrameworkSampleSource.this.readData(track, formatHolder, buffer);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment