mirror of
https://github.com/samsonjs/media.git
synced 2026-04-15 12:55:46 +00:00
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
This commit is contained in:
parent
a6b7cfb5e4
commit
365812e820
3 changed files with 0 additions and 415 deletions
|
|
@ -18,7 +18,6 @@ package com.google.android.exoplayer;
|
|||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaExtractor;
|
||||
|
||||
/**
|
||||
* Compatibility wrapper around {@link android.media.MediaCodec.CryptoInfo}.
|
||||
|
|
@ -72,22 +71,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.
|
||||
* <p>
|
||||
|
|
|
|||
|
|
@ -442,18 +442,6 @@ public final class Format implements Parcelable {
|
|||
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
|
||||
public String toString() {
|
||||
return "Format(" + id + ", " + containerMimeType + ", " + sampleMimeType + ", " + bitrate + ", "
|
||||
|
|
|
|||
|
|
@ -1,386 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue