Merge pull request #92 from google/dev

Dev -> Dev-l
This commit is contained in:
ojw28 2014-10-27 11:06:10 +00:00
commit 1057a45812
12 changed files with 196 additions and 57 deletions

View File

@ -174,7 +174,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
codecCounters = new CodecCounters(); codecCounters = new CodecCounters();
sampleHolder = new SampleHolder(false); sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DISABLED);
formatHolder = new MediaFormatHolder(); formatHolder = new MediaFormatHolder();
decodeOnlyPresentationTimestamps = new HashSet<Long>(); decodeOnlyPresentationTimestamps = new HashSet<Long>();
outputBufferInfo = new MediaCodec.BufferInfo(); outputBufferInfo = new MediaCodec.BufferInfo();

View File

@ -76,7 +76,7 @@ public class MediaCodecUtil {
for (int i = 0; i < numberOfCodecs; i++) { for (int i = 0; i < numberOfCodecs; i++) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
String codecName = info.getName(); String codecName = info.getName();
if (!info.isEncoder() && isOmxCodec(codecName)) { if (!info.isEncoder() && codecName.startsWith("OMX.") && !codecName.endsWith(".secure")) {
String[] supportedTypes = info.getSupportedTypes(); String[] supportedTypes = info.getSupportedTypes();
for (int j = 0; j < supportedTypes.length; j++) { for (int j = 0; j < supportedTypes.length; j++) {
String supportedType = supportedTypes[j]; String supportedType = supportedTypes[j];
@ -91,10 +91,6 @@ public class MediaCodecUtil {
return null; return null;
} }
private static boolean isOmxCodec(String name) {
return name.startsWith("OMX.");
}
private static boolean isAdaptive(CodecCapabilities capabilities) { private static boolean isAdaptive(CodecCapabilities capabilities) {
if (Util.SDK_INT >= 19) { if (Util.SDK_INT >= 19) {
return isAdaptiveV19(capabilities); return isAdaptiveV19(capabilities);

View File

@ -23,10 +23,19 @@ import java.nio.ByteBuffer;
public final class SampleHolder { public final class SampleHolder {
/** /**
* Whether a {@link SampleSource} is permitted to replace {@link #data} if its current value is * Disallows buffer replacement.
* null or of insufficient size to hold the sample.
*/ */
public final boolean allowDataBufferReplacement; public static final int BUFFER_REPLACEMENT_MODE_DISABLED = 0;
/**
* Allows buffer replacement using {@link ByteBuffer#allocate(int)}.
*/
public static final int BUFFER_REPLACEMENT_MODE_NORMAL = 1;
/**
* Allows buffer replacement using {@link ByteBuffer#allocateDirect(int)}.
*/
public static final int BUFFER_REPLACEMENT_MODE_DIRECT = 2;
public final CryptoInfo cryptoInfo; public final CryptoInfo cryptoInfo;
@ -57,12 +66,34 @@ public final class SampleHolder {
*/ */
public boolean decodeOnly; public boolean decodeOnly;
private final int bufferReplacementMode;
/** /**
* @param allowDataBufferReplacement See {@link #allowDataBufferReplacement}. * @param bufferReplacementMode Determines the behavior of {@link #replaceBuffer(int)}. One of
* {@link #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} and
* {@link #BUFFER_REPLACEMENT_MODE_DIRECT}.
*/ */
public SampleHolder(boolean allowDataBufferReplacement) { public SampleHolder(int bufferReplacementMode) {
this.cryptoInfo = new CryptoInfo(); this.cryptoInfo = new CryptoInfo();
this.allowDataBufferReplacement = allowDataBufferReplacement; this.bufferReplacementMode = bufferReplacementMode;
}
/**
* Attempts to replace {@link #data} with a {@link ByteBuffer} of the specified capacity.
*
* @param capacity The capacity of the replacement buffer, in bytes.
* @return True if the buffer was replaced. False otherwise.
*/
public boolean replaceBuffer(int capacity) {
switch (bufferReplacementMode) {
case BUFFER_REPLACEMENT_MODE_NORMAL:
data = ByteBuffer.allocate(capacity);
return true;
case BUFFER_REPLACEMENT_MODE_DIRECT:
data = ByteBuffer.allocateDirect(capacity);
return true;
}
return false;
} }
} }

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.NonBlockingInputStream; import com.google.android.exoplayer.upstream.NonBlockingInputStream;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import java.nio.ByteBuffer;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -97,9 +96,8 @@ public class SingleSampleMediaChunk extends MediaChunk {
if (headerData != null) { if (headerData != null) {
sampleSize += headerData.length; sampleSize += headerData.length;
} }
if (holder.allowDataBufferReplacement && if (holder.data == null || holder.data.capacity() < sampleSize) {
(holder.data == null || holder.data.capacity() < sampleSize)) { holder.replaceBuffer(sampleSize);
holder.data = ByteBuffer.allocate(sampleSize);
} }
int bytesRead; int bytesRead;
if (holder.data != null) { if (holder.data != null) {

View File

@ -751,9 +751,12 @@ public final class FragmentedMp4Extractor implements Extractor {
parseSenc(senc.data, out); parseSenc(senc.data, out);
} }
LeafAtom uuid = traf.getLeafAtomOfType(Atom.TYPE_uuid); int childrenSize = traf.children.size();
if (uuid != null) { for (int i = 0; i < childrenSize; i++) {
parseUuid(uuid.data, out, extendedTypeScratch); Atom atom = traf.children.get(i);
if (atom.type == Atom.TYPE_uuid) {
parseUuid(((LeafAtom) atom).data, out, extendedTypeScratch);
}
} }
} }
@ -1066,21 +1069,20 @@ public final class FragmentedMp4Extractor implements Extractor {
if (out == null) { if (out == null) {
return RESULT_NEED_SAMPLE_HOLDER; return RESULT_NEED_SAMPLE_HOLDER;
} }
ByteBuffer outputData = out.data;
out.timeUs = fragmentRun.getSamplePresentationTime(sampleIndex) * 1000L; out.timeUs = fragmentRun.getSamplePresentationTime(sampleIndex) * 1000L;
out.flags = 0; out.flags = 0;
if (fragmentRun.sampleIsSyncFrameTable[sampleIndex]) { if (fragmentRun.sampleIsSyncFrameTable[sampleIndex]) {
out.flags |= MediaExtractor.SAMPLE_FLAG_SYNC; out.flags |= MediaExtractor.SAMPLE_FLAG_SYNC;
lastSyncSampleIndex = sampleIndex; lastSyncSampleIndex = sampleIndex;
} }
if (out.allowDataBufferReplacement && (out.data == null || out.data.capacity() < sampleSize)) { if (out.data == null || out.data.capacity() < sampleSize) {
outputData = ByteBuffer.allocate(sampleSize); out.replaceBuffer(sampleSize);
out.data = outputData;
} }
if (fragmentRun.definesEncryptionData) { if (fragmentRun.definesEncryptionData) {
readSampleEncryptionData(fragmentRun.sampleEncryptionData, out); readSampleEncryptionData(fragmentRun.sampleEncryptionData, out);
} }
ByteBuffer outputData = out.data;
if (outputData == null) { if (outputData == null) {
inputStream.skip(sampleSize); inputStream.skip(sampleSize);
out.size = 0; out.size = 0;

View File

@ -347,13 +347,11 @@ public final class WebmExtractor implements Extractor {
throw new IllegalStateException("Lacing mode " + lacing + " not supported"); throw new IllegalStateException("Lacing mode " + lacing + " not supported");
} }
ByteBuffer outputData = sampleHolder.data; if (sampleHolder.data == null || sampleHolder.data.capacity() < sampleHolder.size) {
if (sampleHolder.allowDataBufferReplacement sampleHolder.replaceBuffer(sampleHolder.size);
&& (sampleHolder.data == null || sampleHolder.data.capacity() < sampleHolder.size)) {
outputData = ByteBuffer.allocate(sampleHolder.size);
sampleHolder.data = outputData;
} }
ByteBuffer outputData = sampleHolder.data;
if (outputData == null) { if (outputData == null) {
reader.skipBytes(inputStream, sampleHolder.size); reader.skipBytes(inputStream, sampleHolder.size);
sampleHolder.size = 0; sampleHolder.size = 0;

View File

@ -55,7 +55,7 @@ public class SubtitleParserHelper implements Handler.Callback {
* Flushes the helper, canceling the current parsing operation, if there is one. * Flushes the helper, canceling the current parsing operation, if there is one.
*/ */
public synchronized void flush() { public synchronized void flush() {
sampleHolder = new SampleHolder(true); sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
parsing = false; parsing = false;
result = null; result = null;
error = null; error = null;

View File

@ -21,7 +21,6 @@ import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.VerboseLogUtil;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.os.Handler; import android.os.Handler;
@ -29,7 +28,6 @@ import android.os.Handler.Callback;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.util.Log;
import java.io.IOException; import java.io.IOException;
@ -54,8 +52,6 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
} }
private static final String TAG = "TextTrackRenderer";
private static final int MSG_UPDATE_OVERLAY = 0; private static final int MSG_UPDATE_OVERLAY = 0;
private final Handler textRendererHandler; private final Handler textRendererHandler;
@ -145,14 +141,13 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
currentPositionUs = positionUs;
try { try {
source.continueBuffering(positionUs); source.continueBuffering(positionUs);
} catch (IOException e) { } catch (IOException e) {
throw new ExoPlaybackException(e); throw new ExoPlaybackException(e);
} }
currentPositionUs = positionUs;
if (parserHelper.isParsing()) { if (parserHelper.isParsing()) {
return; return;
} }
@ -188,7 +183,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
// We don't have a subtitle. Try and read the next one from the source, and if we succeed then // We don't have a subtitle. Try and read the next one from the source, and if we succeed then
// sync and set textRendererNeedsUpdate. // sync and set textRendererNeedsUpdate.
if (subtitle == null) { if (!inputStreamEnded && subtitle == null) {
try { try {
SampleHolder sampleHolder = parserHelper.getSampleHolder(); SampleHolder sampleHolder = parserHelper.getSampleHolder();
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
@ -215,12 +210,12 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
@Override @Override
protected void onDisabled() { protected void onDisabled() {
source.disable(trackIndex);
subtitle = null; subtitle = null;
parserThread.quit(); parserThread.quit();
parserThread = null; parserThread = null;
parserHelper = null; parserHelper = null;
clearTextRenderer(); clearTextRenderer();
source.disable(trackIndex);
} }
@Override @Override
@ -268,20 +263,18 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
private void updateTextRenderer(long positionUs) { private void updateTextRenderer(long positionUs) {
String text = subtitle.getText(positionUs); String text = subtitle.getText(positionUs);
log("updateTextRenderer; text=: " + text);
if (textRendererHandler != null) { if (textRendererHandler != null) {
textRendererHandler.obtainMessage(MSG_UPDATE_OVERLAY, text).sendToTarget(); textRendererHandler.obtainMessage(MSG_UPDATE_OVERLAY, text).sendToTarget();
} else { } else {
invokeTextRenderer(text); invokeRendererInternal(text);
} }
} }
private void clearTextRenderer() { private void clearTextRenderer() {
log("clearTextRenderer");
if (textRendererHandler != null) { if (textRendererHandler != null) {
textRendererHandler.obtainMessage(MSG_UPDATE_OVERLAY, null).sendToTarget(); textRendererHandler.obtainMessage(MSG_UPDATE_OVERLAY, null).sendToTarget();
} else { } else {
invokeTextRenderer(null); invokeRendererInternal(null);
} }
} }
@ -289,20 +282,14 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
public boolean handleMessage(Message msg) { public boolean handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
case MSG_UPDATE_OVERLAY: case MSG_UPDATE_OVERLAY:
invokeTextRenderer((String) msg.obj); invokeRendererInternal((String) msg.obj);
return true; return true;
} }
return false; return false;
} }
private void invokeTextRenderer(String text) { private void invokeRendererInternal(String text) {
textRenderer.onText(text); textRenderer.onText(text);
} }
private void log(String logMessage) {
if (VerboseLogUtil.isTagEnabled(TAG)) {
Log.v(TAG, logMessage);
}
}
} }

View File

@ -36,8 +36,27 @@ public final class FileDataSource implements DataSource {
} }
private final TransferListener listener;
private RandomAccessFile file; private RandomAccessFile file;
private long bytesRemaining; private long bytesRemaining;
private boolean opened;
/**
* Constructs a new {@link DataSource} that retrieves data from a file.
*/
public FileDataSource() {
this(null);
}
/**
* Constructs a new {@link DataSource} that retrieves data from a file.
*
* @param listener An optional listener. Specify {@code null} for no listener.
*/
public FileDataSource(TransferListener listener) {
this.listener = listener;
}
@Override @Override
public long open(DataSpec dataSpec) throws FileDataSourceException { public long open(DataSpec dataSpec) throws FileDataSourceException {
@ -46,10 +65,16 @@ public final class FileDataSource implements DataSource {
file.seek(dataSpec.position); file.seek(dataSpec.position);
bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? file.length() - dataSpec.position bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? file.length() - dataSpec.position
: dataSpec.length; : dataSpec.length;
return bytesRemaining;
} catch (IOException e) { } catch (IOException e) {
throw new FileDataSourceException(e); throw new FileDataSourceException(e);
} }
opened = true;
if (listener != null) {
listener.onTransferStart();
}
return bytesRemaining;
} }
@Override @Override
@ -63,7 +88,14 @@ public final class FileDataSource implements DataSource {
} catch (IOException e) { } catch (IOException e) {
throw new FileDataSourceException(e); throw new FileDataSourceException(e);
} }
if (bytesRead > 0) {
bytesRemaining -= bytesRead; bytesRemaining -= bytesRead;
if (listener != null) {
listener.onBytesTransferred(bytesRead);
}
}
return bytesRead; return bytesRead;
} }
} }
@ -75,8 +107,16 @@ public final class FileDataSource implements DataSource {
file.close(); file.close();
} catch (IOException e) { } catch (IOException e) {
throw new FileDataSourceException(e); throw new FileDataSourceException(e);
} } finally {
file = null; file = null;
if (opened) {
opened = false;
if (listener != null) {
listener.onTransferEnd();
}
}
}
} }
} }

View File

@ -0,0 +1,85 @@
/*
* 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.upstream;
import com.google.android.exoplayer.util.Assertions;
import java.io.IOException;
/**
* A data source that fetches data from a local or remote {@link DataSpec}.
*/
public final class UriDataSource implements DataSource {
private static final String FILE_URI_SCHEME = "file";
private final DataSource fileDataSource;
private final DataSource httpDataSource;
/**
* {@code null} if no data source is open. Otherwise, equal to {@link #fileDataSource} if the open
* data source is a file, or {@link #httpDataSource} otherwise.
*/
private DataSource dataSource;
/**
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and an
* {@link HttpDataSource} for other URIs.
*
* @param userAgent The User-Agent string that should be used when requesting remote data.
* @param transferListener An optional listener.
*/
public UriDataSource(String userAgent, TransferListener transferListener) {
this(new FileDataSource(transferListener),
new HttpDataSource(userAgent, null, transferListener));
}
/**
* Constructs a new data source using {@code fileDataSource} for file URIs, and
* {@code httpDataSource} for non-file URIs.
*
* @param fileDataSource {@link DataSource} to use for file URIs.
* @param httpDataSource {@link DataSource} to use for non-file URIs.
*/
public UriDataSource(DataSource fileDataSource, DataSource httpDataSource) {
this.fileDataSource = Assertions.checkNotNull(fileDataSource);
this.httpDataSource = Assertions.checkNotNull(httpDataSource);
}
@Override
public long open(DataSpec dataSpec) throws IOException {
Assertions.checkState(dataSource == null);
dataSource = dataSpec.uri.getScheme().equals(FILE_URI_SCHEME) ? fileDataSource : httpDataSource;
return dataSource.open(dataSpec);
}
@Override
public void close() throws IOException {
Assertions.checkNotNull(dataSource);
dataSource.close();
dataSource = null;
}
@Override
public int read(byte[] buffer, int offset, int readLength) throws IOException {
Assertions.checkNotNull(dataSource);
return dataSource.read(buffer, offset, readLength);
}
}

View File

@ -24,8 +24,8 @@ import android.util.Pair;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
/** /**
@ -282,10 +282,10 @@ public class ManifestFetcher<T> implements Loader.Callback {
@Override @Override
public void load() throws IOException, InterruptedException { public void load() throws IOException, InterruptedException {
String inputEncoding = null; String inputEncoding;
InputStream inputStream = null; InputStream inputStream = null;
try { try {
HttpURLConnection connection = configureHttpConnection(new URL(manifestUrl)); URLConnection connection = configureConnection(new URL(manifestUrl));
inputStream = connection.getInputStream(); inputStream = connection.getInputStream();
inputEncoding = connection.getContentEncoding(); inputEncoding = connection.getContentEncoding();
result = parser.parse(inputStream, inputEncoding, contentId, result = parser.parse(inputStream, inputEncoding, contentId,
@ -297,8 +297,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
} }
} }
private HttpURLConnection configureHttpConnection(URL url) throws IOException { private URLConnection configureConnection(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); URLConnection connection = url.openConnection();
connection.setConnectTimeout(TIMEOUT_MILLIS); connection.setConnectTimeout(TIMEOUT_MILLIS);
connection.setReadTimeout(TIMEOUT_MILLIS); connection.setReadTimeout(TIMEOUT_MILLIS);
connection.setDoOutput(false); connection.setDoOutput(false);

View File

@ -32,6 +32,8 @@ public class MimeTypes {
public static final String AUDIO_MP4 = BASE_TYPE_AUDIO + "/mp4"; public static final String AUDIO_MP4 = BASE_TYPE_AUDIO + "/mp4";
public static final String AUDIO_AAC = BASE_TYPE_AUDIO + "/mp4a-latm"; public static final String AUDIO_AAC = BASE_TYPE_AUDIO + "/mp4a-latm";
public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
public static final String AUDIO_EC3 = BASE_TYPE_AUDIO + "/eac3";
public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt"; public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt";