Commit 06e9e5d3 by ojw28

Merge pull request #234 from google/dev

dev -> dev-hls
parents a6e94af2 a879819d
...@@ -20,6 +20,7 @@ import com.google.android.exoplayer.DefaultLoadControl; ...@@ -20,6 +20,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl; import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecUtil; import com.google.android.exoplayer.MediaCodecUtil;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
...@@ -172,7 +173,13 @@ public class DashRendererBuilder implements RendererBuilder, ...@@ -172,7 +173,13 @@ public class DashRendererBuilder implements RendererBuilder,
// Determine which video representations we should use for playback. // Determine which video representations we should use for playback.
ArrayList<Integer> videoRepresentationIndexList = new ArrayList<Integer>(); ArrayList<Integer> videoRepresentationIndexList = new ArrayList<Integer>();
if (videoAdaptationSet != null) { if (videoAdaptationSet != null) {
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize(); int maxDecodableFrameSize;
try {
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
} catch (DecoderQueryException e) {
callback.onRenderersError(e);
return;
}
List<Representation> videoRepresentations = videoAdaptationSet.representations; List<Representation> videoRepresentations = videoAdaptationSet.representations;
for (int i = 0; i < videoRepresentations.size(); i++) { for (int i = 0; i < videoRepresentations.size(); i++) {
Format format = videoRepresentations.get(i).format; Format format = videoRepresentations.get(i).format;
......
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl; import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecUtil; import com.google.android.exoplayer.MediaCodecUtil;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSource;
...@@ -125,7 +126,13 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, ...@@ -125,7 +126,13 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
} }
// Obtain stream elements for playback. // Obtain stream elements for playback.
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize(); int maxDecodableFrameSize;
try {
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
} catch (DecoderQueryException e) {
callback.onRenderersError(e);
return;
}
int audioStreamElementCount = 0; int audioStreamElementCount = 0;
int textStreamElementCount = 0; int textStreamElementCount = 0;
int videoStreamElementIndex = -1; int videoStreamElementIndex = -1;
......
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl; import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecUtil; import com.google.android.exoplayer.MediaCodecUtil;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSource;
...@@ -99,7 +100,14 @@ import java.util.List; ...@@ -99,7 +100,14 @@ import java.util.List;
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
// Determine which video representations we should use for playback. // Determine which video representations we should use for playback.
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize(); int maxDecodableFrameSize;
try {
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
} catch (DecoderQueryException e) {
callback.onRenderersError(e);
return;
}
int videoAdaptationSetIndex = period.getAdaptationSetIndex(AdaptationSet.TYPE_VIDEO); int videoAdaptationSetIndex = period.getAdaptationSetIndex(AdaptationSet.TYPE_VIDEO);
List<Representation> videoRepresentations = List<Representation> videoRepresentations =
period.adaptationSets.get(videoAdaptationSetIndex).representations; period.adaptationSets.get(videoAdaptationSetIndex).representations;
......
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl; import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecUtil; import com.google.android.exoplayer.MediaCodecUtil;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSource;
...@@ -94,7 +95,13 @@ import java.util.ArrayList; ...@@ -94,7 +95,13 @@ import java.util.ArrayList;
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
// Obtain stream elements for playback. // Obtain stream elements for playback.
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize(); int maxDecodableFrameSize;
try {
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
} catch (DecoderQueryException e) {
callback.onRenderersError(e);
return;
}
int audioStreamElementIndex = -1; int audioStreamElementIndex = -1;
int videoStreamElementIndex = -1; int videoStreamElementIndex = -1;
ArrayList<Integer> videoTrackIndexList = new ArrayList<Integer>(); ArrayList<Integer> videoTrackIndexList = new ArrayList<Integer>();
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
...@@ -67,8 +68,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -67,8 +68,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
*/ */
public static class DecoderInitializationException extends Exception { public static class DecoderInitializationException extends Exception {
private static final int CUSTOM_ERROR_CODE_BASE = -50000;
private static final int NO_SUITABLE_DECODER_ERROR = CUSTOM_ERROR_CODE_BASE + 1;
private static final int DECODER_QUERY_ERROR = CUSTOM_ERROR_CODE_BASE + 2;
/** /**
* The name of the decoder that failed to initialize. * The name of the decoder that failed to initialize. Null if no suitable decoder was found.
*/ */
public final String decoderName; public final String decoderName;
...@@ -77,8 +82,14 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -77,8 +82,14 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
*/ */
public final String diagnosticInfo; public final String diagnosticInfo;
public DecoderInitializationException(String decoderName, MediaFormat mediaFormat, public DecoderInitializationException(MediaFormat mediaFormat, Throwable cause, int errorCode) {
Throwable cause) { super("Decoder init failed: [" + errorCode + "], " + mediaFormat, cause);
this.decoderName = null;
this.diagnosticInfo = buildCustomDiagnosticInfo(errorCode);
}
public DecoderInitializationException(MediaFormat mediaFormat, Throwable cause,
String decoderName) {
super("Decoder init failed: " + decoderName + ", " + mediaFormat, cause); super("Decoder init failed: " + decoderName + ", " + mediaFormat, cause);
this.decoderName = decoderName; this.decoderName = decoderName;
this.diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null; this.diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null;
...@@ -92,6 +103,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -92,6 +103,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
return null; return null;
} }
private static String buildCustomDiagnosticInfo(int errorCode) {
String sign = errorCode < 0 ? "neg_" : "";
return "com.google.android.exoplayer.MediaCodecTrackRenderer_" + sign + Math.abs(errorCode);
}
} }
/** /**
...@@ -281,21 +297,29 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -281,21 +297,29 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
} }
DecoderInfo selectedDecoderInfo = MediaCodecUtil.getDecoderInfo(mimeType, DecoderInfo decoderInfo = null;
requiresSecureDecoder); try {
String selectedDecoderName = selectedDecoderInfo.name; decoderInfo = MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
codecIsAdaptive = selectedDecoderInfo.adaptive; } catch (DecoderQueryException e) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
DecoderInitializationException.DECODER_QUERY_ERROR));
}
if (decoderInfo == null) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, null,
DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
}
String decoderName = decoderInfo.name;
codecIsAdaptive = decoderInfo.adaptive;
try { try {
codec = MediaCodec.createByCodecName(selectedDecoderName); codec = MediaCodec.createByCodecName(decoderName);
configureCodec(codec, format.getFrameworkMediaFormatV16(), mediaCrypto); configureCodec(codec, format.getFrameworkMediaFormatV16(), mediaCrypto);
codec.start(); codec.start();
inputBuffers = codec.getInputBuffers(); inputBuffers = codec.getInputBuffers();
outputBuffers = codec.getOutputBuffers(); outputBuffers = codec.getOutputBuffers();
} catch (Exception e) { } catch (Exception e) {
DecoderInitializationException exception = new DecoderInitializationException( notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, decoderName));
selectedDecoderName, format, e);
notifyDecoderInitializationError(exception);
throw new ExoPlaybackException(exception);
} }
codecHotswapTimeMs = getState() == TrackRenderer.STATE_STARTED ? codecHotswapTimeMs = getState() == TrackRenderer.STATE_STARTED ?
SystemClock.elapsedRealtime() : -1; SystemClock.elapsedRealtime() : -1;
...@@ -305,6 +329,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -305,6 +329,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
codecCounters.codecInitCount++; codecCounters.codecInitCount++;
} }
private void notifyAndThrowDecoderInitError(DecoderInitializationException e)
throws ExoPlaybackException {
notifyDecoderInitializationError(e);
throw new ExoPlaybackException(e);
}
protected boolean shouldInitCodec() { protected boolean shouldInitCodec() {
return codec == null && format != null; return codec == null && format != null;
} }
......
...@@ -35,6 +35,20 @@ import java.util.HashMap; ...@@ -35,6 +35,20 @@ import java.util.HashMap;
@TargetApi(16) @TargetApi(16)
public class MediaCodecUtil { public class MediaCodecUtil {
/**
* Thrown when an error occurs querying the device for its underlying media capabilities.
* <p>
* Such failures are not expected in normal operation and are normally temporary (e.g. if the
* mediaserver process has crashed and is yet to restart).
*/
public static class DecoderQueryException extends Exception {
private DecoderQueryException(Throwable cause) {
super("Failed to query underlying media codecs", cause);
}
}
private static final String TAG = "MediaCodecUtil"; private static final String TAG = "MediaCodecUtil";
private static final HashMap<CodecKey, Pair<String, CodecCapabilities>> codecs = private static final HashMap<CodecKey, Pair<String, CodecCapabilities>> codecs =
...@@ -48,7 +62,8 @@ public class MediaCodecUtil { ...@@ -48,7 +62,8 @@ public class MediaCodecUtil {
* unless secure decryption really is required. * unless secure decryption really is required.
* @return Information about the decoder that will be used, or null if no decoder exists. * @return Information about the decoder that will be used, or null if no decoder exists.
*/ */
public static DecoderInfo getDecoderInfo(String mimeType, boolean secure) { public static DecoderInfo getDecoderInfo(String mimeType, boolean secure)
throws DecoderQueryException {
Pair<String, CodecCapabilities> info = getMediaCodecInfo(mimeType, secure); Pair<String, CodecCapabilities> info = getMediaCodecInfo(mimeType, secure);
if (info == null) { if (info == null) {
return null; return null;
...@@ -66,14 +81,19 @@ public class MediaCodecUtil { ...@@ -66,14 +81,19 @@ public class MediaCodecUtil {
* unless secure decryption really is required. * unless secure decryption really is required.
*/ */
public static synchronized void warmCodec(String mimeType, boolean secure) { public static synchronized void warmCodec(String mimeType, boolean secure) {
try {
getMediaCodecInfo(mimeType, secure); getMediaCodecInfo(mimeType, secure);
} catch (DecoderQueryException e) {
// Codec warming is best effort, so we can swallow the exception.
Log.e(TAG, "Codec warming failed", e);
}
} }
/** /**
* Returns the name of the best decoder and its capabilities for the given mimeType. * Returns the name of the best decoder and its capabilities for the given mimeType.
*/ */
private static synchronized Pair<String, CodecCapabilities> getMediaCodecInfo( private static synchronized Pair<String, CodecCapabilities> getMediaCodecInfo(
String mimeType, boolean secure) { String mimeType, boolean secure) throws DecoderQueryException {
CodecKey key = new CodecKey(mimeType, secure); CodecKey key = new CodecKey(mimeType, secure);
if (codecs.containsKey(key)) { if (codecs.containsKey(key)) {
return codecs.get(key); return codecs.get(key);
...@@ -95,6 +115,17 @@ public class MediaCodecUtil { ...@@ -95,6 +115,17 @@ public class MediaCodecUtil {
} }
private static Pair<String, CodecCapabilities> getMediaCodecInfo(CodecKey key, private static Pair<String, CodecCapabilities> getMediaCodecInfo(CodecKey key,
MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
try {
return getMediaCodecInfoInternal(key, mediaCodecList);
} catch (Exception e) {
// If the underlying mediaserver is in a bad state, we may catch an IllegalStateException
// or an IllegalArgumentException here.
throw new DecoderQueryException(e);
}
}
private static Pair<String, CodecCapabilities> getMediaCodecInfoInternal(CodecKey key,
MediaCodecListCompat mediaCodecList) { MediaCodecListCompat mediaCodecList) {
String mimeType = key.mimeType; String mimeType = key.mimeType;
int numberOfCodecs = mediaCodecList.getCodecCount(); int numberOfCodecs = mediaCodecList.getCodecCount();
...@@ -153,7 +184,8 @@ public class MediaCodecUtil { ...@@ -153,7 +184,8 @@ public class MediaCodecUtil {
* @param level An AVC profile level from {@link CodecProfileLevel}. * @param level An AVC profile level from {@link CodecProfileLevel}.
* @return Whether the specified profile is supported at the specified level. * @return Whether the specified profile is supported at the specified level.
*/ */
public static boolean isH264ProfileSupported(int profile, int level) { public static boolean isH264ProfileSupported(int profile, int level)
throws DecoderQueryException {
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false); Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
if (info == null) { if (info == null) {
return false; return false;
...@@ -173,7 +205,7 @@ public class MediaCodecUtil { ...@@ -173,7 +205,7 @@ public class MediaCodecUtil {
/** /**
* @return the maximum frame size for an H264 stream that can be decoded on the device. * @return the maximum frame size for an H264 stream that can be decoded on the device.
*/ */
public static int maxH264DecodableFrameSize() { public static int maxH264DecodableFrameSize() throws DecoderQueryException {
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false); Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
if (info == null) { if (info == null) {
return 0; return 0;
...@@ -248,20 +280,23 @@ public class MediaCodecUtil { ...@@ -248,20 +280,23 @@ public class MediaCodecUtil {
@TargetApi(21) @TargetApi(21)
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat { private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
private final MediaCodecInfo[] mediaCodecInfos; private final int codecKind;
private MediaCodecInfo[] mediaCodecInfos;
public MediaCodecListCompatV21(boolean includeSecure) { public MediaCodecListCompatV21(boolean includeSecure) {
int codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS; codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
} }
@Override @Override
public int getCodecCount() { public int getCodecCount() {
ensureMediaCodecInfosInitialized();
return mediaCodecInfos.length; return mediaCodecInfos.length;
} }
@Override @Override
public MediaCodecInfo getCodecInfoAt(int index) { public MediaCodecInfo getCodecInfoAt(int index) {
ensureMediaCodecInfosInitialized();
return mediaCodecInfos[index]; return mediaCodecInfos[index];
} }
...@@ -275,6 +310,12 @@ public class MediaCodecUtil { ...@@ -275,6 +310,12 @@ public class MediaCodecUtil {
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback); return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
} }
private void ensureMediaCodecInfosInitialized() {
if (mediaCodecInfos == null) {
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
}
}
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
......
...@@ -24,6 +24,7 @@ import com.google.android.exoplayer.upstream.DataSpec; ...@@ -24,6 +24,7 @@ import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.Loader.Loadable; import com.google.android.exoplayer.upstream.Loader.Loadable;
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 com.google.android.exoplayer.util.TraceUtil;
import java.io.IOException; import java.io.IOException;
...@@ -173,7 +174,12 @@ public abstract class Chunk implements Loadable { ...@@ -173,7 +174,12 @@ public abstract class Chunk implements Loadable {
@Override @Override
public final void load() throws IOException, InterruptedException { public final void load() throws IOException, InterruptedException {
TraceUtil.beginSection("chunkLoad");
try {
dataSourceStream.load(); dataSourceStream.load();
} finally {
TraceUtil.endSection();
}
} }
} }
...@@ -137,7 +137,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback { ...@@ -137,7 +137,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
/** /**
* The default minimum number of times to retry loading data prior to failing. * The default minimum number of times to retry loading data prior to failing.
*/ */
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 1; public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
private static final int STATE_UNPREPARED = 0; private static final int STATE_UNPREPARED = 0;
private static final int STATE_PREPARED = 1; private static final int STATE_PREPARED = 1;
......
...@@ -489,8 +489,8 @@ public final class WebmExtractor implements Extractor { ...@@ -489,8 +489,8 @@ public final class WebmExtractor implements Extractor {
} else if (CODEC_ID_OPUS.equals(codecId)) { } else if (CODEC_ID_OPUS.equals(codecId)) {
ArrayList<byte[]> opusInitializationData = new ArrayList<byte[]>(3); ArrayList<byte[]> opusInitializationData = new ArrayList<byte[]>(3);
opusInitializationData.add(codecPrivate); opusInitializationData.add(codecPrivate);
opusInitializationData.add(ByteBuffer.allocate(8).putLong(codecDelayNs).array()); opusInitializationData.add(ByteBuffer.allocate(Long.SIZE).putLong(codecDelayNs).array());
opusInitializationData.add(ByteBuffer.allocate(8).putLong(seekPreRollNs).array()); opusInitializationData.add(ByteBuffer.allocate(Long.SIZE).putLong(seekPreRollNs).array());
format = MediaFormat.createAudioFormat( format = MediaFormat.createAudioFormat(
MimeTypes.AUDIO_OPUS, OPUS_MAX_INPUT_SIZE, channelCount, sampleRate, MimeTypes.AUDIO_OPUS, OPUS_MAX_INPUT_SIZE, channelCount, sampleRate,
opusInitializationData); opusInitializationData);
......
...@@ -393,18 +393,22 @@ public class HttpDataSource implements DataSource { ...@@ -393,18 +393,22 @@ public class HttpDataSource implements DataSource {
connection.setRequestProperty(property.getKey(), property.getValue()); connection.setRequestProperty(property.getKey(), property.getValue());
} }
} }
setRangeHeader(connection, dataSpec);
connection.setRequestProperty("User-Agent", userAgent); connection.setRequestProperty("User-Agent", userAgent);
connection.setRequestProperty("Range", buildRangeHeader(dataSpec));
connection.connect(); connection.connect();
return connection; return connection;
} }
private String buildRangeHeader(DataSpec dataSpec) { private void setRangeHeader(HttpURLConnection connection, DataSpec dataSpec) {
if (dataSpec.position == 0 && dataSpec.length == C.LENGTH_UNBOUNDED) {
// Not required.
return;
}
String rangeRequest = "bytes=" + dataSpec.position + "-"; String rangeRequest = "bytes=" + dataSpec.position + "-";
if (dataSpec.length != C.LENGTH_UNBOUNDED) { if (dataSpec.length != C.LENGTH_UNBOUNDED) {
rangeRequest += (dataSpec.position + dataSpec.length - 1); rangeRequest += (dataSpec.position + dataSpec.length - 1);
} }
return rangeRequest; connection.setRequestProperty("Range", rangeRequest);
} }
private long getContentLength(HttpURLConnection connection) { private long getContentLength(HttpURLConnection connection) {
......
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