Commit 87d0be25 by ojw28

Merge pull request #149 from google/dev

dev -> dev-hls
parents 3cfe894b bc303b73
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
package com.google.android.exoplayer.demo.full; package com.google.android.exoplayer.demo.full;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer.AudioTrackInitializationException;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.demo.full.player.DemoPlayer; import com.google.android.exoplayer.demo.full.player.DemoPlayer;
import com.google.android.exoplayer.util.VerboseLogUtil; import com.google.android.exoplayer.util.VerboseLogUtil;
...@@ -149,7 +149,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener ...@@ -149,7 +149,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
} }
@Override @Override
public void onAudioTrackInitializationError(AudioTrackInitializationException e) { public void onAudioTrackInitializationError(AudioTrack.InitializationException e) {
printInternalError("audioTrackInitializationError", e); printInternalError("audioTrackInitializationError", e);
} }
......
...@@ -19,10 +19,10 @@ import com.google.android.exoplayer.DummyTrackRenderer; ...@@ -19,10 +19,10 @@ import com.google.android.exoplayer.DummyTrackRenderer;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer.AudioTrackInitializationException;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
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.audio.AudioTrack;
import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSource;
import com.google.android.exoplayer.chunk.MultiTrackChunkSource; import com.google.android.exoplayer.chunk.MultiTrackChunkSource;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
...@@ -110,7 +110,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -110,7 +110,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
*/ */
public interface InternalErrorListener { public interface InternalErrorListener {
void onRendererInitializationError(Exception e); void onRendererInitializationError(Exception e);
void onAudioTrackInitializationError(AudioTrackInitializationException e); void onAudioTrackInitializationError(AudioTrack.InitializationException e);
void onDecoderInitializationError(DecoderInitializationException e); void onDecoderInitializationError(DecoderInitializationException e);
void onCryptoError(CryptoException e); void onCryptoError(CryptoException e);
void onUpstreamError(int sourceId, IOException e); void onUpstreamError(int sourceId, IOException e);
...@@ -454,7 +454,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -454,7 +454,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
} }
@Override @Override
public void onAudioTrackInitializationError(AudioTrackInitializationException e) { public void onAudioTrackInitializationError(AudioTrack.InitializationException e) {
if (internalErrorListener != null) { if (internalErrorListener != null) {
internalErrorListener.onAudioTrackInitializationError(e); internalErrorListener.onAudioTrackInitializationError(e);
} }
......
...@@ -29,7 +29,7 @@ public final class DecoderInfo { ...@@ -29,7 +29,7 @@ public final class DecoderInfo {
public final String name; public final String name;
/** /**
* Whether the decoder is adaptive. * Whether the decoder supports seamless resolution switches.
* *
* @see android.media.MediaCodecInfo.CodecCapabilities#isFeatureSupported(String) * @see android.media.MediaCodecInfo.CodecCapabilities#isFeatureSupported(String)
* @see android.media.MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback * @see android.media.MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback
......
...@@ -17,9 +17,9 @@ package com.google.android.exoplayer; ...@@ -17,9 +17,9 @@ package com.google.android.exoplayer;
import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent; import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.PriorityHandlerThread;
import com.google.android.exoplayer.util.TraceUtil; import com.google.android.exoplayer.util.TraceUtil;
import android.annotation.SuppressLint;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
...@@ -83,7 +83,6 @@ import java.util.List; ...@@ -83,7 +83,6 @@ import java.util.List;
private volatile long positionUs; private volatile long positionUs;
private volatile long bufferedPositionUs; private volatile long bufferedPositionUs;
@SuppressLint("HandlerLeak")
public ExoPlayerImplInternal(Handler eventHandler, boolean playWhenReady, public ExoPlayerImplInternal(Handler eventHandler, boolean playWhenReady,
boolean[] rendererEnabledFlags, int minBufferMs, int minRebufferMs) { boolean[] rendererEnabledFlags, int minBufferMs, int minRebufferMs) {
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
...@@ -101,15 +100,10 @@ import java.util.List; ...@@ -101,15 +100,10 @@ import java.util.List;
mediaClock = new MediaClock(); mediaClock = new MediaClock();
enabledRenderers = new ArrayList<TrackRenderer>(rendererEnabledFlags.length); enabledRenderers = new ArrayList<TrackRenderer>(rendererEnabledFlags.length);
internalPlaybackThread = new HandlerThread(getClass().getSimpleName() + ":Handler") {
@Override
public void run() {
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can // Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
// not normally change to this priority" is incorrect. // not normally change to this priority" is incorrect.
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO); internalPlaybackThread = new PriorityHandlerThread(getClass().getSimpleName() + ":Handler",
super.run(); Process.THREAD_PRIORITY_AUDIO);
}
};
internalPlaybackThread.start(); internalPlaybackThread.start();
handler = new Handler(internalPlaybackThread.getLooper(), this); handler = new Handler(internalPlaybackThread.getLooper(), this);
} }
......
...@@ -280,11 +280,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -280,11 +280,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
} }
DecoderInfo selectedDecoderInfo = MediaCodecUtil.getDecoderInfo(mimeType); DecoderInfo selectedDecoderInfo = MediaCodecUtil.getDecoderInfo(mimeType,
requiresSecureDecoder);
String selectedDecoderName = selectedDecoderInfo.name; String selectedDecoderName = selectedDecoderInfo.name;
if (requiresSecureDecoder) {
selectedDecoderName = getSecureDecoderName(selectedDecoderName);
}
codecIsAdaptive = selectedDecoderInfo.adaptive; codecIsAdaptive = selectedDecoderInfo.adaptive;
try { try {
codec = MediaCodec.createByCodecName(selectedDecoderName); codec = MediaCodec.createByCodecName(selectedDecoderName);
...@@ -765,13 +763,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -765,13 +763,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
MediaCodec codec, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex, MediaCodec codec, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex,
boolean shouldSkip) throws ExoPlaybackException; boolean shouldSkip) throws ExoPlaybackException;
/**
* Returns the name of the secure variant of a given decoder.
*/
private static String getSecureDecoderName(String rawDecoderName) {
return rawDecoderName + ".secure";
}
private void notifyDecoderInitializationError(final DecoderInitializationException e) { private void notifyDecoderInitializationError(final DecoderInitializationException e) {
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() { eventHandler.post(new Runnable() {
......
...@@ -23,6 +23,7 @@ import android.media.MediaCodecInfo; ...@@ -23,6 +23,7 @@ import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecList; import android.media.MediaCodecList;
import android.text.TextUtils;
import android.util.Pair; import android.util.Pair;
import java.util.HashMap; import java.util.HashMap;
...@@ -33,60 +34,79 @@ import java.util.HashMap; ...@@ -33,60 +34,79 @@ import java.util.HashMap;
@TargetApi(16) @TargetApi(16)
public class MediaCodecUtil { public class MediaCodecUtil {
private static final HashMap<String, Pair<MediaCodecInfo, CodecCapabilities>> codecs = private static final HashMap<CodecKey, Pair<String, CodecCapabilities>> codecs =
new HashMap<String, Pair<MediaCodecInfo, CodecCapabilities>>(); new HashMap<CodecKey, Pair<String, CodecCapabilities>>();
/** /**
* Get information about the decoder that will be used for a given mime type. If no decoder * Get information about the decoder that will be used for a given mime type.
* exists for the mime type then null is returned.
* *
* @param mimeType The mime type. * @param mimeType The mime type.
* @param secure Whether the decoder is required to support secure decryption. Always pass false
* 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) { public static DecoderInfo getDecoderInfo(String mimeType, boolean secure) {
Pair<MediaCodecInfo, CodecCapabilities> info = getMediaCodecInfo(mimeType); Pair<String, CodecCapabilities> info = getMediaCodecInfo(mimeType, secure);
if (info == null) { if (info == null) {
return null; return null;
} }
return new DecoderInfo(info.first.getName(), isAdaptive(info.second)); return new DecoderInfo(info.first, isAdaptive(info.second));
} }
/** /**
* Optional call to warm the codec cache. Call from any appropriate * Optional call to warm the codec cache for a given mime type.
* place to hide latency. * <p>
* Calling this method may speed up subsequent calls to {@link #getDecoderInfo(String, boolean)}.
*
* @param mimeType The mime type.
* @param secure Whether the decoder is required to support secure decryption. Always pass false
* unless secure decryption really is required.
*/ */
public static synchronized void warmCodecs(String[] mimeTypes) { public static synchronized void warmCodec(String mimeType, boolean secure) {
for (int i = 0; i < mimeTypes.length; i++) { getMediaCodecInfo(mimeType, secure);
getMediaCodecInfo(mimeTypes[i]);
}
} }
/** /**
* Returns the best decoder and its capabilities for the given mimeType. If there's no decoder * Returns the name of the best decoder and its capabilities for the given mimeType.
* returns null.
*
* TODO: We need to use the new object based MediaCodecList API.
*/ */
@SuppressWarnings("deprecation") private static synchronized Pair<String, CodecCapabilities> getMediaCodecInfo(
private static synchronized Pair<MediaCodecInfo, CodecCapabilities> getMediaCodecInfo( String mimeType, boolean secure) {
String mimeType) { CodecKey key = new CodecKey(mimeType, secure);
Pair<MediaCodecInfo, CodecCapabilities> result = codecs.get(mimeType); if (codecs.containsKey(key)) {
if (result != null) { return codecs.get(key);
return result;
} }
int numberOfCodecs = MediaCodecList.getCodecCount(); MediaCodecListCompat mediaCodecList = Util.SDK_INT >= 21
? new MediaCodecListCompatV21(secure) : new MediaCodecListCompatV16();
int numberOfCodecs = mediaCodecList.getCodecCount();
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
// Note: MediaCodecList is sorted by the framework such that the best decoders come first. // Note: MediaCodecList is sorted by the framework such that the best decoders come first.
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() && codecName.startsWith("OMX.") && !codecName.endsWith(".secure")) { if (!info.isEncoder() && codecName.startsWith("OMX.")
&& (secureDecodersExplicit || !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];
if (supportedType.equalsIgnoreCase(mimeType)) { if (supportedType.equalsIgnoreCase(mimeType)) {
result = Pair.create(info, info.getCapabilitiesForType(supportedType)); CodecCapabilities capabilities = info.getCapabilitiesForType(supportedType);
codecs.put(mimeType, result); if (!secureDecodersExplicit) {
return result; // Cache variants for secure and insecure playback. Note that the secure decoder is
// inferred, and may not actually exist.
codecs.put(key.secure ? new CodecKey(mimeType, false) : key,
Pair.create(codecName, capabilities));
codecs.put(key.secure ? key : new CodecKey(mimeType, true),
Pair.create(codecName + ".secure", capabilities));
} else {
// We can only cache this variant. The other should be listed explicitly.
boolean codecSecure = mediaCodecList.isSecurePlaybackSupported(
info.getCapabilitiesForType(supportedType));
codecs.put(key.secure == codecSecure ? key : new CodecKey(mimeType, codecSecure),
Pair.create(codecName, capabilities));
}
if (codecs.containsKey(key)) {
return codecs.get(key);
}
} }
} }
} }
...@@ -113,7 +133,7 @@ public class MediaCodecUtil { ...@@ -113,7 +133,7 @@ public class MediaCodecUtil {
* @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) {
Pair<MediaCodecInfo, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264); Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
if (info == null) { if (info == null) {
return false; return false;
} }
...@@ -133,7 +153,7 @@ public class MediaCodecUtil { ...@@ -133,7 +153,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() {
Pair<MediaCodecInfo, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264); Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
if (info == null) { if (info == null) {
return 0; return 0;
} }
...@@ -177,4 +197,123 @@ public class MediaCodecUtil { ...@@ -177,4 +197,123 @@ public class MediaCodecUtil {
} }
} }
private interface MediaCodecListCompat {
/**
* The number of codecs in the list.
*/
public int getCodecCount();
/**
* The info at the specified index in the list.
*
* @param index The index.
*/
public MediaCodecInfo getCodecInfoAt(int index);
/**
* @return Returns whether secure decoders are explicitly listed, if present.
*/
public boolean secureDecodersExplicit();
/**
* Whether secure playback is supported for the given {@link CodecCapabilities}, which should
* have been obtained from a {@link MediaCodecInfo} obtained from this list.
* <p>
* May only be called if {@link #secureDecodersExplicit()} returns true.
*/
public boolean isSecurePlaybackSupported(CodecCapabilities capabilities);
}
@TargetApi(21)
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
private final MediaCodecInfo[] mediaCodecInfos;
public MediaCodecListCompatV21(boolean includeSecure) {
int codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
}
@Override
public int getCodecCount() {
return mediaCodecInfos.length;
}
@Override
public MediaCodecInfo getCodecInfoAt(int index) {
return mediaCodecInfos[index];
}
@Override
public boolean secureDecodersExplicit() {
return true;
}
@Override
public boolean isSecurePlaybackSupported(CodecCapabilities capabilities) {
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
}
}
@SuppressWarnings("deprecation")
private static final class MediaCodecListCompatV16 implements MediaCodecListCompat {
@Override
public int getCodecCount() {
return MediaCodecList.getCodecCount();
}
@Override
public MediaCodecInfo getCodecInfoAt(int index) {
return MediaCodecList.getCodecInfoAt(index);
}
@Override
public boolean secureDecodersExplicit() {
return false;
}
@Override
public boolean isSecurePlaybackSupported(CodecCapabilities capabilities) {
throw new UnsupportedOperationException();
}
}
private static final class CodecKey {
public final String mimeType;
public final boolean secure;
public CodecKey(String mimeType, boolean secure) {
this.mimeType = mimeType;
this.secure = secure;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode());
result = prime * result + (secure ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || obj.getClass() != CodecKey.class) {
return false;
}
CodecKey other = (CodecKey) obj;
return TextUtils.equals(mimeType, other.mimeType) && secure == other.secure;
}
}
} }
/*
* 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.audio;
import com.google.android.exoplayer.util.Util;
import android.annotation.TargetApi;
import android.media.AudioFormat;
import java.util.HashSet;
import java.util.Set;
/**
* Represents the set of audio formats a device is capable of playing back.
*/
@TargetApi(21)
public final class AudioCapabilities {
private final Set<Integer> supportedEncodings;
private final int maxChannelCount;
/**
* Constructs new audio capabilities based on a set of supported encodings and a maximum channel
* count.
*
* @param supportedEncodings Supported audio encodings from {@link android.media.AudioFormat}'s
* {@code ENCODING_*} constants.
* @param maxChannelCount The maximum number of audio channels that can be played simultaneously.
*/
public AudioCapabilities(int[] supportedEncodings, int maxChannelCount) {
this.supportedEncodings = new HashSet<Integer>();
if (supportedEncodings != null) {
for (int i : supportedEncodings) {
this.supportedEncodings.add(i);
}
}
this.maxChannelCount = maxChannelCount;
}
/** Returns whether the device supports playback of AC-3. */
public boolean supportsAc3() {
return Util.SDK_INT >= 21 && supportedEncodings.contains(AudioFormat.ENCODING_AC3);
}
/** Returns whether the device supports playback of enhanced AC-3. */
public boolean supportsEAc3() {
return Util.SDK_INT >= 21 && supportedEncodings.contains(AudioFormat.ENCODING_E_AC3);
}
/** Returns whether the device supports playback of 16-bit PCM. */
public boolean supportsPcm() {
return supportedEncodings.contains(AudioFormat.ENCODING_PCM_16BIT);
}
/** Returns the maximum number of channels the device can play at the same time. */
public int getMaxChannelCount() {
return maxChannelCount;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AudioCapabilities)) {
return false;
}
AudioCapabilities audioCapabilities = (AudioCapabilities) other;
return supportedEncodings.equals(audioCapabilities.supportedEncodings)
&& maxChannelCount == audioCapabilities.maxChannelCount;
}
@Override
public int hashCode() {
return maxChannelCount + 31 * supportedEncodings.hashCode();
}
@Override
public String toString() {
return "AudioCapabilities[maxChannelCount=" + maxChannelCount
+ ", supportedEncodings=" + supportedEncodings + "]";
}
}
/*
* 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.audio;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioFormat;
import android.media.AudioManager;
/**
* Notifies a listener when the audio playback capabilities change. Call {@link #register} to start
* receiving notifications, and {@link #unregister} to stop.
*/
public final class AudioCapabilitiesReceiver {
/** Listener notified when audio capabilities change. */
public interface Listener {
/** Called when the audio capabilities change. */
void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities);
}
/** Default to stereo PCM on SDK <= 21 and when HDMI is unplugged. */
private static final AudioCapabilities DEFAULT_AUDIO_CAPABILITIES =
new AudioCapabilities(new int[] {AudioFormat.ENCODING_PCM_16BIT}, 2);
private final Context context;
private final Listener listener;
private final BroadcastReceiver receiver;
/**
* Constructs a new audio capabilities receiver.
*
* @param context Application context for registering to receive broadcasts.
* @param listener Listener to notify when audio capabilities change.
*/
public AudioCapabilitiesReceiver(Context context, Listener listener) {
this.context = Assertions.checkNotNull(context);
this.listener = Assertions.checkNotNull(listener);
this.receiver = Util.SDK_INT >= 21 ? new HdmiAudioPlugBroadcastReceiver() : null;
}
/**
* Registers to notify the listener when audio capabilities change. The listener will immediately
* receive the current audio capabilities. It is important to call {@link #unregister} so that
* the listener can be garbage collected.
*/
@TargetApi(21)
public void register() {
if (receiver != null) {
context.registerReceiver(receiver, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG));
}
listener.onAudioCapabilitiesChanged(DEFAULT_AUDIO_CAPABILITIES);
}
/** Unregisters to stop notifying the listener when audio capabilities change. */
public void unregister() {
if (receiver != null) {
context.unregisterReceiver(receiver);
}
}
@TargetApi(21)
private final class HdmiAudioPlugBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!action.equals(AudioManager.ACTION_HDMI_AUDIO_PLUG)) {
return;
}
listener.onAudioCapabilitiesChanged(
new AudioCapabilities(intent.getIntArrayExtra(AudioManager.EXTRA_ENCODINGS),
intent.getIntExtra(AudioManager.EXTRA_MAX_CHANNEL_COUNT, 0)));
}
}
}
...@@ -87,7 +87,7 @@ public class DashChunkSource implements ChunkSource { ...@@ -87,7 +87,7 @@ public class DashChunkSource implements ChunkSource {
formats[i] = representations[i].format; formats[i] = representations[i].format;
maxWidth = Math.max(formats[i].width, maxWidth); maxWidth = Math.max(formats[i].width, maxWidth);
maxHeight = Math.max(formats[i].height, maxHeight); maxHeight = Math.max(formats[i].height, maxHeight);
Extractor extractor = formats[i].mimeType.startsWith(MimeTypes.VIDEO_WEBM) Extractor extractor = mimeTypeIsWebm(formats[i].mimeType)
? new WebmExtractor() : new FragmentedMp4Extractor(); ? new WebmExtractor() : new FragmentedMp4Extractor();
extractors.put(formats[i].id, extractor); extractors.put(formats[i].id, extractor);
this.representations.put(formats[i].id, representations[i]); this.representations.put(formats[i].id, representations[i]);
...@@ -197,6 +197,10 @@ public class DashChunkSource implements ChunkSource { ...@@ -197,6 +197,10 @@ public class DashChunkSource implements ChunkSource {
// Do nothing. // Do nothing.
} }
private boolean mimeTypeIsWebm(String mimeType) {
return mimeType.startsWith(MimeTypes.VIDEO_WEBM) || mimeType.startsWith(MimeTypes.AUDIO_WEBM);
}
private Chunk newInitializationChunk(RangedUri initializationUri, RangedUri indexUri, private Chunk newInitializationChunk(RangedUri initializationUri, RangedUri indexUri,
Representation representation, Extractor extractor, DataSource dataSource, Representation representation, Extractor extractor, DataSource dataSource,
int trigger) { int trigger) {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer.parser.webm; package com.google.android.exoplayer.parser.webm;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException;
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;
...@@ -134,7 +135,7 @@ import java.util.Stack; ...@@ -134,7 +135,7 @@ import java.util.Stack;
} }
@Override @Override
public int read(NonBlockingInputStream inputStream) { public int read(NonBlockingInputStream inputStream) throws ParserException {
Assertions.checkState(eventHandler != null); Assertions.checkState(eventHandler != null);
while (true) { while (true) {
while (!masterElementsStack.isEmpty() while (!masterElementsStack.isEmpty()
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.parser.webm; package com.google.android.exoplayer.parser.webm;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.upstream.NonBlockingInputStream; import com.google.android.exoplayer.upstream.NonBlockingInputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -46,41 +47,47 @@ import java.nio.ByteBuffer; ...@@ -46,41 +47,47 @@ import java.nio.ByteBuffer;
* @param elementOffsetBytes The byte offset where this element starts * @param elementOffsetBytes The byte offset where this element starts
* @param headerSizeBytes The byte length of this element's ID and size header * @param headerSizeBytes The byte length of this element's ID and size header
* @param contentsSizeBytes The byte length of this element's children * @param contentsSizeBytes The byte length of this element's children
* @throws ParserException If a parsing error occurs.
*/ */
public void onMasterElementStart( public void onMasterElementStart(
int id, long elementOffsetBytes, int headerSizeBytes, long contentsSizeBytes); int id, long elementOffsetBytes, int headerSizeBytes,
long contentsSizeBytes) throws ParserException;
/** /**
* Called when a master element has finished reading in all of its children from the * Called when a master element has finished reading in all of its children from the
* {@link NonBlockingInputStream}. * {@link NonBlockingInputStream}.
* *
* @param id The integer ID of this element * @param id The integer ID of this element
* @throws ParserException If a parsing error occurs.
*/ */
public void onMasterElementEnd(int id); public void onMasterElementEnd(int id) throws ParserException;
/** /**
* Called when an integer element is encountered in the {@link NonBlockingInputStream}. * Called when an integer element is encountered in the {@link NonBlockingInputStream}.
* *
* @param id The integer ID of this element * @param id The integer ID of this element
* @param value The integer value this element contains * @param value The integer value this element contains
* @throws ParserException If a parsing error occurs.
*/ */
public void onIntegerElement(int id, long value); public void onIntegerElement(int id, long value) throws ParserException;
/** /**
* Called when a float element is encountered in the {@link NonBlockingInputStream}. * Called when a float element is encountered in the {@link NonBlockingInputStream}.
* *
* @param id The integer ID of this element * @param id The integer ID of this element
* @param value The float value this element contains * @param value The float value this element contains
* @throws ParserException If a parsing error occurs.
*/ */
public void onFloatElement(int id, double value); public void onFloatElement(int id, double value) throws ParserException;
/** /**
* Called when a string element is encountered in the {@link NonBlockingInputStream}. * Called when a string element is encountered in the {@link NonBlockingInputStream}.
* *
* @param id The integer ID of this element * @param id The integer ID of this element
* @param value The string value this element contains * @param value The string value this element contains
* @throws ParserException If a parsing error occurs.
*/ */
public void onStringElement(int id, String value); public void onStringElement(int id, String value) throws ParserException;
/** /**
* Called when a binary element is encountered in the {@link NonBlockingInputStream}. * Called when a binary element is encountered in the {@link NonBlockingInputStream}.
...@@ -109,9 +116,10 @@ import java.nio.ByteBuffer; ...@@ -109,9 +116,10 @@ import java.nio.ByteBuffer;
* @param inputStream The {@link NonBlockingInputStream} from which this * @param inputStream The {@link NonBlockingInputStream} from which this
* element's contents should be read * element's contents should be read
* @return True if the element was read. False otherwise. * @return True if the element was read. False otherwise.
* @throws ParserException If a parsing error occurs.
*/ */
public boolean onBinaryElement( public boolean onBinaryElement(
int id, long elementOffsetBytes, int headerSizeBytes, int contentsSizeBytes, int id, long elementOffsetBytes, int headerSizeBytes, int contentsSizeBytes,
NonBlockingInputStream inputStream); NonBlockingInputStream inputStream) throws ParserException;
} }
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.parser.webm; package com.google.android.exoplayer.parser.webm;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.upstream.NonBlockingInputStream; import com.google.android.exoplayer.upstream.NonBlockingInputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -53,8 +54,9 @@ import java.nio.ByteBuffer; ...@@ -53,8 +54,9 @@ import java.nio.ByteBuffer;
* *
* @param inputStream The input stream from which data should be read * @param inputStream The input stream from which data should be read
* @return One of the {@code RESULT_*} flags defined in this interface * @return One of the {@code RESULT_*} flags defined in this interface
* @throws ParserException If parsing fails.
*/ */
public int read(NonBlockingInputStream inputStream); public int read(NonBlockingInputStream inputStream) throws ParserException;
/** /**
* The total number of bytes consumed by the reader since first created or last {@link #reset()}. * The total number of bytes consumed by the reader since first created or last {@link #reset()}.
......
...@@ -34,6 +34,8 @@ public class MimeTypes { ...@@ -34,6 +34,8 @@ public class MimeTypes {
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_AC3 = BASE_TYPE_AUDIO + "/ac3";
public static final String AUDIO_EC3 = BASE_TYPE_AUDIO + "/eac3"; public static final String AUDIO_EC3 = BASE_TYPE_AUDIO + "/eac3";
public static final String AUDIO_WEBM = BASE_TYPE_AUDIO + "/webm";
public static final String AUDIO_VORBIS = BASE_TYPE_AUDIO + "/vorbis";
public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt"; public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt";
......
/*
* 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.util;
import android.os.HandlerThread;
import android.os.Process;
/**
* A {@link HandlerThread} with a specified process priority.
*/
public class PriorityHandlerThread extends HandlerThread {
private final int priority;
/**
* @param name The name of the thread.
* @param priority The priority level. See {@link Process#setThreadPriority(int)} for details.
*/
public PriorityHandlerThread(String name, int priority) {
super(name);
this.priority = priority;
}
@Override
public void run() {
Process.setThreadPriority(priority);
super.run();
}
}
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