From a85a169604ef09a12c72a82b6f85dd79dadde7fd Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 28 May 2015 17:18:26 +0100 Subject: [PATCH] Simplify the demo app. --- .../android/exoplayer/demo/DemoUtil.java | 102 ------------- .../exoplayer/demo/PlayerActivity.java | 79 ++++++---- .../android/exoplayer/demo/Samples.java | 46 +++--- .../SmoothStreamingTestMediaDrmCallback.java | 5 +- .../demo/WidevineTestMediaDrmCallback.java | 5 +- .../demo/player/DashRendererBuilder.java | 57 ++------ .../demo/player/DebugTrackRenderer.java | 138 ------------------ .../exoplayer/demo/player/DemoPlayer.java | 81 +++++----- .../demo/player/ExtractorRendererBuilder.java | 13 +- .../demo/player/HlsRendererBuilder.java | 17 +-- .../SmoothStreamingRendererBuilder.java | 39 +---- .../drm/StreamingDrmSessionManager.java | 30 ++-- .../drm}/UnsupportedDrmException.java | 14 +- .../exoplayer/util/DebugTextViewHelper.java | 125 ++++++++++++++++ .../google/android/exoplayer/util/Util.java | 54 +++++++ 15 files changed, 357 insertions(+), 448 deletions(-) delete mode 100644 demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java delete mode 100644 demo/src/main/java/com/google/android/exoplayer/demo/player/DebugTrackRenderer.java rename {demo/src/main/java/com/google/android/exoplayer/demo/player => library/src/main/java/com/google/android/exoplayer/drm}/UnsupportedDrmException.java (70%) create mode 100644 library/src/main/java/com/google/android/exoplayer/util/DebugTextViewHelper.java diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java b/demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java deleted file mode 100644 index 558b294f09..0000000000 --- a/demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java +++ /dev/null @@ -1,102 +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.demo; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.CookiePolicy; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Map; - -/** - * Utility methods for the demo application. - */ -public class DemoUtil { - - public static final int TYPE_DASH = 0; - public static final int TYPE_SS = 1; - public static final int TYPE_HLS = 2; - public static final int TYPE_MP4 = 3; - public static final int TYPE_MP3 = 4; - public static final int TYPE_M4A = 5; - public static final int TYPE_WEBM = 6; - public static final int TYPE_TS = 7; - public static final int TYPE_AAC = 8; - - private static final CookieManager defaultCookieManager; - - static { - defaultCookieManager = new CookieManager(); - defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); - } - - public static byte[] executePost(String url, byte[] data, Map requestProperties) - throws IOException { - HttpURLConnection urlConnection = null; - try { - urlConnection = (HttpURLConnection) new URL(url).openConnection(); - urlConnection.setRequestMethod("POST"); - urlConnection.setDoOutput(data != null); - urlConnection.setDoInput(true); - if (requestProperties != null) { - for (Map.Entry requestProperty : requestProperties.entrySet()) { - urlConnection.setRequestProperty(requestProperty.getKey(), requestProperty.getValue()); - } - } - if (data != null) { - OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); - out.write(data); - out.close(); - } - InputStream in = new BufferedInputStream(urlConnection.getInputStream()); - return convertInputStreamToByteArray(in); - } finally { - if (urlConnection != null) { - urlConnection.disconnect(); - } - } - } - - private static byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException { - byte[] bytes = null; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte data[] = new byte[1024]; - int count; - while ((count = inputStream.read(data)) != -1) { - bos.write(data, 0, count); - } - bos.flush(); - bos.close(); - inputStream.close(); - bytes = bos.toByteArray(); - return bytes; - } - - public static void setDefaultCookieManager() { - CookieHandler currentHandler = CookieHandler.getDefault(); - if (currentHandler != defaultCookieManager) { - CookieHandler.setDefault(defaultCookieManager); - } - } - -} diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index 822c85ec4e..a47d082e0a 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -25,7 +25,7 @@ import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder; import com.google.android.exoplayer.demo.player.ExtractorRendererBuilder; import com.google.android.exoplayer.demo.player.HlsRendererBuilder; import com.google.android.exoplayer.demo.player.SmoothStreamingRendererBuilder; -import com.google.android.exoplayer.demo.player.UnsupportedDrmException; +import com.google.android.exoplayer.drm.UnsupportedDrmException; import com.google.android.exoplayer.extractor.mp3.Mp3Extractor; import com.google.android.exoplayer.extractor.mp4.Mp4Extractor; import com.google.android.exoplayer.extractor.ts.AdtsExtractor; @@ -37,6 +37,7 @@ import com.google.android.exoplayer.metadata.TxxxMetadata; import com.google.android.exoplayer.text.CaptionStyleCompat; import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.SubtitleLayout; +import com.google.android.exoplayer.util.DebugTextViewHelper; import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.VerboseLogUtil; @@ -65,6 +66,9 @@ import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.TextView; import android.widget.Toast; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.CookiePolicy; import java.util.List; import java.util.Map; @@ -75,14 +79,30 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener, AudioCapabilitiesReceiver.Listener { + public static final int TYPE_DASH = 0; + public static final int TYPE_SS = 1; + public static final int TYPE_HLS = 2; + public static final int TYPE_MP4 = 3; + public static final int TYPE_MP3 = 4; + public static final int TYPE_FMP4 = 5; + public static final int TYPE_WEBM = 6; + public static final int TYPE_TS = 7; + public static final int TYPE_AAC = 8; + public static final int TYPE_M4A = 9; + public static final String CONTENT_TYPE_EXTRA = "content_type"; public static final String CONTENT_ID_EXTRA = "content_id"; private static final String TAG = "PlayerActivity"; - private static final int MENU_GROUP_TRACKS = 1; private static final int ID_OFFSET = 2; + private static final CookieManager defaultCookieManager; + static { + defaultCookieManager = new CookieManager(); + defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); + } + private EventLogger eventLogger; private MediaController mediaController; private View debugRootView; @@ -97,6 +117,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, private Button retryButton; private DemoPlayer player; + private DebugTextViewHelper debugViewHelper; private boolean playerNeedsPrepare; private long playerPosition; @@ -162,7 +183,10 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, audioButton = (Button) findViewById(R.id.audio_controls); textButton = (Button) findViewById(R.id.text_controls); - DemoUtil.setDefaultCookieManager(); + CookieHandler currentHandler = CookieHandler.getDefault(); + if (currentHandler != defaultCookieManager) { + CookieHandler.setDefault(defaultCookieManager); + } } @Override @@ -220,31 +244,26 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, private RendererBuilder getRendererBuilder() { String userAgent = Util.getUserAgent(this, "ExoPlayerDemo"); switch (contentType) { - case DemoUtil.TYPE_SS: + case TYPE_SS: return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(), - new SmoothStreamingTestMediaDrmCallback(), debugTextView); - case DemoUtil.TYPE_DASH: + new SmoothStreamingTestMediaDrmCallback()); + case TYPE_DASH: return new DashRendererBuilder(this, userAgent, contentUri.toString(), - new WidevineTestMediaDrmCallback(contentId), debugTextView, audioCapabilities); - case DemoUtil.TYPE_HLS: - return new HlsRendererBuilder(this, userAgent, contentUri.toString(), debugTextView, - audioCapabilities); - case DemoUtil.TYPE_M4A: // There are no file format differences between M4A and MP4. - case DemoUtil.TYPE_MP4: - return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView, - new Mp4Extractor()); - case DemoUtil.TYPE_MP3: - return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView, - new Mp3Extractor()); - case DemoUtil.TYPE_TS: - return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView, + new WidevineTestMediaDrmCallback(contentId), audioCapabilities); + case TYPE_HLS: + return new HlsRendererBuilder(this, userAgent, contentUri.toString(), audioCapabilities); + case TYPE_M4A: // There are no file format differences between M4A and MP4. + case TYPE_MP4: + return new ExtractorRendererBuilder(this, userAgent, contentUri, new Mp4Extractor()); + case TYPE_MP3: + return new ExtractorRendererBuilder(this, userAgent, contentUri, new Mp3Extractor()); + case TYPE_TS: + return new ExtractorRendererBuilder(this, userAgent, contentUri, new TsExtractor(0, audioCapabilities)); - case DemoUtil.TYPE_AAC: - return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView, - new AdtsExtractor()); - case DemoUtil.TYPE_WEBM: - return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView, - new WebmExtractor()); + case TYPE_AAC: + return new ExtractorRendererBuilder(this, userAgent, contentUri, new AdtsExtractor()); + case TYPE_WEBM: + return new ExtractorRendererBuilder(this, userAgent, contentUri, new WebmExtractor()); default: throw new IllegalStateException("Unsupported type: " + contentType); } @@ -265,6 +284,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, player.addListener(eventLogger); player.setInfoListener(eventLogger); player.setInternalErrorListener(eventLogger); + debugViewHelper = new DebugTextViewHelper(player, debugTextView); + debugViewHelper.start(); } if (playerNeedsPrepare) { player.prepare(); @@ -277,6 +298,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, private void releasePlayer() { if (player != null) { + debugViewHelper.stop(); + debugViewHelper = null; playerPosition = player.getCurrentPosition(); player.release(); player = null; @@ -322,11 +345,9 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, if (e instanceof UnsupportedDrmException) { // Special case DRM failures. UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e; - int stringId = unsupportedDrmException.reason == UnsupportedDrmException.REASON_NO_DRM - ? R.string.drm_error_not_supported + int stringId = Util.SDK_INT < 18 ? R.string.drm_error_not_supported : unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.drm_error_unsupported_scheme - : R.string.drm_error_unknown; + ? R.string.drm_error_unsupported_scheme : R.string.drm_error_unknown; Toast.makeText(getApplicationContext(), stringId, Toast.LENGTH_LONG).show(); } playerNeedsPrepare = true; diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java b/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java index f68bb58892..cda6b9cc2c 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java @@ -47,12 +47,12 @@ import java.util.Locale; "http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?" + "as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" + "ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7." - + "8506521BFC350652163895D4C26DEE124209AA9E&key=ik0", DemoUtil.TYPE_DASH), + + "8506521BFC350652163895D4C26DEE124209AA9E&key=ik0", PlayerActivity.TYPE_DASH), new Sample("Google Play", "http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?" + "as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" + "ipbits=0&expire=19000000000&signature=A2716F75795F5D2AF0E88962FFCD10DB79384F29." - + "84308FF04844498CE6FBCE4731507882B8307798&key=ik0", DemoUtil.TYPE_DASH), + + "84308FF04844498CE6FBCE4731507882B8307798&key=ik0", PlayerActivity.TYPE_DASH), }; public static final Sample[] YOUTUBE_DASH_WEBM = new Sample[] { @@ -60,21 +60,21 @@ import java.util.Locale; "http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?" + "as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" + "ipbits=0&expire=19000000000&signature=249B04F79E984D7F86B4D8DB48AE6FAF41C17AB3." - + "7B9F0EC0505E1566E59B8E488E9419F253DDF413&key=ik0", DemoUtil.TYPE_DASH), + + "7B9F0EC0505E1566E59B8E488E9419F253DDF413&key=ik0", PlayerActivity.TYPE_DASH), new Sample("Google Play", "http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?" + "as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" + "ipbits=0&expire=19000000000&signature=B1C2A74783AC1CC4865EB312D7DD2D48230CC9FD." - + "BD153B9882175F1F94BFE5141A5482313EA38E8D&key=ik0", DemoUtil.TYPE_DASH), + + "BD153B9882175F1F94BFE5141A5482313EA38E8D&key=ik0", PlayerActivity.TYPE_DASH), }; public static final Sample[] SMOOTHSTREAMING = new Sample[] { new Sample("Super speed", "http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism", - DemoUtil.TYPE_SS), + PlayerActivity.TYPE_SS), new Sample("Super speed (PlayReady)", "http://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism", - DemoUtil.TYPE_SS), + PlayerActivity.TYPE_SS), }; public static final Sample[] WIDEVINE_GTS = new Sample[] { @@ -82,72 +82,72 @@ import java.util.Locale; "http://www.youtube.com/api/manifest/dash/id/d286538032258a1c/source/youtube?" + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" + "&ipbits=0&expire=19000000000&signature=477CF7D478BE26C205045D507E9358F85F84C065." - + "8971631EB657BC33EC2F48A2FF4211956760C3E9&key=ik0", DemoUtil.TYPE_DASH), + + "8971631EB657BC33EC2F48A2FF4211956760C3E9&key=ik0", PlayerActivity.TYPE_DASH), new Sample("WV: HDCP not required", "48fcc369939ac96c", "http://www.youtube.com/api/manifest/dash/id/48fcc369939ac96c/source/youtube?" + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" + "&ipbits=0&expire=19000000000&signature=171DAE48D00B5BE7434BC1A9F84DAE0463C7EA7A." - + "0925B4DBB5605BEE9F5D088C48F25F5108E96191&key=ik0", DemoUtil.TYPE_DASH), + + "0925B4DBB5605BEE9F5D088C48F25F5108E96191&key=ik0", PlayerActivity.TYPE_DASH), new Sample("WV: HDCP required", "e06c39f1151da3df", "http://www.youtube.com/api/manifest/dash/id/e06c39f1151da3df/source/youtube?" + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" + "&ipbits=0&expire=19000000000&signature=8D3B8AF4E3F72B7F127C8D0D39B7AFCF37B30519." - + "A118BADEBF3582AD2CC257B0EE6E579C6955D8AA&key=ik0", DemoUtil.TYPE_DASH), + + "A118BADEBF3582AD2CC257B0EE6E579C6955D8AA&key=ik0", PlayerActivity.TYPE_DASH), new Sample("WV: Secure video path required", "0894c7c8719b28a0", "http://www.youtube.com/api/manifest/dash/id/0894c7c8719b28a0/source/youtube?" + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" + "&ipbits=0&expire=19000000000&signature=A41D835C7387885A4A820628F57E481E00095931." - + "9D50DBEEB5E37344647EE11BDA129A7FCDE8B7B9&key=ik0", DemoUtil.TYPE_DASH), + + "9D50DBEEB5E37344647EE11BDA129A7FCDE8B7B9&key=ik0", PlayerActivity.TYPE_DASH), new Sample("WV: HDCP + secure video path required", "efd045b1eb61888a", "http://www.youtube.com/api/manifest/dash/id/efd045b1eb61888a/source/youtube" + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" + "&ipbits=0&expire=19000000000&signature=A97C9032C9D0C74F1643DB17C178873887C229E4." - + "0A657BF6F23C8BC1538F276137383478330B76DE&key=ik0", DemoUtil.TYPE_DASH), + + "0A657BF6F23C8BC1538F276137383478330B76DE&key=ik0", PlayerActivity.TYPE_DASH), new Sample("WV: 30s license duration (fails at ~30s)", "f9a34cab7b05881a", "http://www.youtube.com/api/manifest/dash/id/f9a34cab7b05881a/source/youtube?" + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" + "&ipbits=0&expire=19000000000&signature=80648A12A7D5FC1FA02B52B4250E4EB74CF0C5FD." - + "66A261130CA137AA5C541EA9CED2DBF240829EE6&key=ik0", DemoUtil.TYPE_DASH), + + "66A261130CA137AA5C541EA9CED2DBF240829EE6&key=ik0", PlayerActivity.TYPE_DASH), }; public static final Sample[] HLS = new Sample[] { new Sample("Apple master playlist", "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/" - + "bipbop_4x3_variant.m3u8", DemoUtil.TYPE_HLS), + + "bipbop_4x3_variant.m3u8", PlayerActivity.TYPE_HLS), new Sample("Apple master playlist advanced", "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/" - + "bipbop_16x9_variant.m3u8", DemoUtil.TYPE_HLS), + + "bipbop_16x9_variant.m3u8", PlayerActivity.TYPE_HLS), new Sample("Apple TS media playlist", "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/" - + "prog_index.m3u8", DemoUtil.TYPE_HLS), + + "prog_index.m3u8", PlayerActivity.TYPE_HLS), new Sample("Apple AAC media playlist", "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear0/" - + "prog_index.m3u8", DemoUtil.TYPE_HLS), + + "prog_index.m3u8", PlayerActivity.TYPE_HLS), new Sample("Apple ID3 metadata", "http://devimages.apple.com/samplecode/adDemo/ad.m3u8", - DemoUtil.TYPE_HLS), + PlayerActivity.TYPE_HLS), }; public static final Sample[] MISC = new Sample[] { new Sample("Dizzy", "http://html5demos.com/assets/dizzy.mp4", - DemoUtil.TYPE_MP4), + PlayerActivity.TYPE_MP4), new Sample("Apple AAC 10s", "https://devimages.apple.com.edgekey.net/" + "streaming/examples/bipbop_4x3/gear0/fileSequence0.aac", - DemoUtil.TYPE_AAC), + PlayerActivity.TYPE_AAC), new Sample("Apple TS 10s", "https://devimages.apple.com.edgekey.net/streaming/examples/" + "bipbop_4x3/gear1/fileSequence0.ts", - DemoUtil.TYPE_TS), + PlayerActivity.TYPE_TS), new Sample("Big Buck Bunny (MP4 Video)", "http://redirector.c.youtube.com/videoplayback?id=604ed5ce52eda7ee&itag=22&source=youtube&" + "sparams=ip,ipbits,expire,source,id&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=" + "513F28C7FDCBEC60A66C86C9A393556C99DC47FB.04C88036EEE12565A1ED864A875A58F15D8B5300" + "&key=ik0", - DemoUtil.TYPE_MP4), + PlayerActivity.TYPE_MP4), new Sample("Google Play (MP3 Audio)", "http://storage.googleapis.com/exoplayer-test-media-0/play.mp3", - DemoUtil.TYPE_MP3), + PlayerActivity.TYPE_MP3), new Sample("Google Glass (WebM Video with Vorbis Audio)", "http://demos.webmproject.org/exoplayer/glass_vp9_vorbis.webm", - DemoUtil.TYPE_WEBM), + PlayerActivity.TYPE_WEBM), }; private Samples() {} diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java b/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java index ec4b0c231d..257b0cac2e 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer.demo; import com.google.android.exoplayer.drm.MediaDrmCallback; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; +import com.google.android.exoplayer.util.Util; import android.annotation.TargetApi; import android.media.MediaDrm.KeyRequest; @@ -48,7 +49,7 @@ public class SmoothStreamingTestMediaDrmCallback implements MediaDrmCallback { @Override public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException { String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); - return DemoUtil.executePost(url, null, null); + return Util.executePost(url, null, null); } @Override @@ -57,7 +58,7 @@ public class SmoothStreamingTestMediaDrmCallback implements MediaDrmCallback { if (TextUtils.isEmpty(url)) { url = PLAYREADY_TEST_DEFAULT_URI; } - return DemoUtil.executePost(url, request.getData(), KEY_REQUEST_PROPERTIES); + return Util.executePost(url, request.getData(), KEY_REQUEST_PROPERTIES); } } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java b/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java index 1f29f6b7a1..bffd5fcb1f 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer.demo; import com.google.android.exoplayer.drm.MediaDrmCallback; +import com.google.android.exoplayer.util.Util; import android.annotation.TargetApi; import android.media.MediaDrm.KeyRequest; @@ -43,7 +44,7 @@ public class WidevineTestMediaDrmCallback implements MediaDrmCallback { @Override public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException { String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); - return DemoUtil.executePost(url, null, null); + return Util.executePost(url, null, null); } @Override @@ -52,7 +53,7 @@ public class WidevineTestMediaDrmCallback implements MediaDrmCallback { if (TextUtils.isEmpty(url)) { url = defaultUri; } - return DemoUtil.executePost(url, request.getData(), null); + return Util.executePost(url, request.getData(), null); } } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java index b875ac9ab7..149ed96e56 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java @@ -42,9 +42,9 @@ import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver; import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver.UtcTimingCallback; import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder; import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallback; -import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.MediaDrmCallback; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; +import com.google.android.exoplayer.drm.UnsupportedDrmException; import com.google.android.exoplayer.text.TextTrackRenderer; import com.google.android.exoplayer.text.ttml.TtmlParser; import com.google.android.exoplayer.text.webvtt.WebvttParser; @@ -57,14 +57,10 @@ import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback; import com.google.android.exoplayer.util.Util; -import android.annotation.TargetApi; import android.content.Context; import android.media.MediaCodec; -import android.media.UnsupportedSchemeException; import android.os.Handler; import android.util.Log; -import android.util.Pair; -import android.widget.TextView; import java.io.IOException; import java.util.ArrayList; @@ -104,7 +100,6 @@ public class DashRendererBuilder implements RendererBuilder, private final String userAgent; private final String url; private final MediaDrmCallback drmCallback; - private final TextView debugTextView; private final AudioCapabilities audioCapabilities; private DemoPlayer player; @@ -116,12 +111,11 @@ public class DashRendererBuilder implements RendererBuilder, private long elapsedRealtimeOffset; public DashRendererBuilder(Context context, String userAgent, String url, - MediaDrmCallback drmCallback, TextView debugTextView, AudioCapabilities audioCapabilities) { + MediaDrmCallback drmCallback, AudioCapabilities audioCapabilities) { this.context = context; this.userAgent = userAgent; this.url = url; this.drmCallback = drmCallback; - this.debugTextView = debugTextView; this.audioCapabilities = audioCapabilities; } @@ -192,20 +186,18 @@ public class DashRendererBuilder implements RendererBuilder, // Check drm support if necessary. boolean filterHdContent = false; - DrmSessionManager drmSessionManager = null; + StreamingDrmSessionManager drmSessionManager = null; if (hasContentProtection) { if (Util.SDK_INT < 18) { callback.onRenderersError( - new UnsupportedDrmException(UnsupportedDrmException.REASON_NO_DRM)); + new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); return; } try { - Pair drmSessionManagerData = - V18Compat.getDrmSessionManagerData(player, drmCallback); - drmSessionManager = drmSessionManagerData.first; - // HD streams require L1 security. + drmSessionManager = StreamingDrmSessionManager.newWidevineInstance( + player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); filterHdContent = videoAdaptationSet != null && videoAdaptationSet.hasContentProtection() - && !drmSessionManagerData.second; + && getWidevineSecurityLevel(drmSessionManager) != SECURITY_LEVEL_1; } catch (UnsupportedDrmException e) { callback.onRenderersError(e); return; @@ -226,10 +218,8 @@ public class DashRendererBuilder implements RendererBuilder, // Build the video renderer. final MediaCodecVideoTrackRenderer videoRenderer; - final TrackRenderer debugRenderer; if (videoRepresentationIndices == null || videoRepresentationIndices.length == 0) { videoRenderer = null; - debugRenderer = null; } else { DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, @@ -240,8 +230,6 @@ public class DashRendererBuilder implements RendererBuilder, DemoPlayer.TYPE_VIDEO); videoRenderer = new MediaCodecVideoTrackRenderer(videoSampleSource, drmSessionManager, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, null, mainHandler, player, 50); - debugRenderer = debugTextView != null - ? new DebugTrackRenderer(debugTextView, player, videoRenderer, bandwidthMeter) : null; } // Build the audio chunk sources. @@ -353,34 +341,13 @@ public class DashRendererBuilder implements RendererBuilder, renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; renderers[DemoPlayer.TYPE_TEXT] = textRenderer; - renderers[DemoPlayer.TYPE_DEBUG] = debugRenderer; - callback.onRenderers(trackNames, multiTrackChunkSources, renderers); + callback.onRenderers(trackNames, multiTrackChunkSources, renderers, bandwidthMeter); } - @TargetApi(18) - private static class V18Compat { - - public static Pair getDrmSessionManagerData(DemoPlayer player, - MediaDrmCallback drmCallback) throws UnsupportedDrmException { - try { - StreamingDrmSessionManager streamingDrmSessionManager = - StreamingDrmSessionManager.newWidevineInstance(player.getPlaybackLooper(), drmCallback, - null, player.getMainHandler(), player); - return Pair.create((DrmSessionManager) streamingDrmSessionManager, - getWidevineSecurityLevel(streamingDrmSessionManager) == SECURITY_LEVEL_1); - } catch (UnsupportedSchemeException e) { - throw new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME); - } catch (Exception e) { - throw new UnsupportedDrmException(UnsupportedDrmException.REASON_UNKNOWN, e); - } - } - - private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) { - String securityLevelProperty = sessionManager.getPropertyString("securityLevel"); - return securityLevelProperty.equals("L1") ? SECURITY_LEVEL_1 : securityLevelProperty - .equals("L3") ? SECURITY_LEVEL_3 : SECURITY_LEVEL_UNKNOWN; - } - + private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) { + String securityLevelProperty = sessionManager.getPropertyString("securityLevel"); + return securityLevelProperty.equals("L1") ? SECURITY_LEVEL_1 : securityLevelProperty + .equals("L3") ? SECURITY_LEVEL_3 : SECURITY_LEVEL_UNKNOWN; } } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DebugTrackRenderer.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DebugTrackRenderer.java deleted file mode 100644 index 7f7f4aefd3..0000000000 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DebugTrackRenderer.java +++ /dev/null @@ -1,138 +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.demo.player; - -import com.google.android.exoplayer.ExoPlaybackException; -import com.google.android.exoplayer.MediaCodecTrackRenderer; -import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer.chunk.Format; -import com.google.android.exoplayer.upstream.BandwidthMeter; - -import android.widget.TextView; - -/** - * A {@link TrackRenderer} that periodically updates debugging information displayed by a - * {@link TextView}. - */ -/* package */ class DebugTrackRenderer extends TrackRenderer implements Runnable { - - private final TextView textView; - private final DemoPlayer player; - private final MediaCodecTrackRenderer renderer; - private final BandwidthMeter bandwidthMeter; - - private volatile boolean pendingFailure; - private volatile long currentPositionUs; - - public DebugTrackRenderer(TextView textView, DemoPlayer player, - MediaCodecTrackRenderer renderer) { - this(textView, player, renderer, null); - } - - public DebugTrackRenderer(TextView textView, DemoPlayer player, MediaCodecTrackRenderer renderer, - BandwidthMeter bandwidthMeter) { - this.textView = textView; - this.player = player; - this.renderer = renderer; - this.bandwidthMeter = bandwidthMeter; - } - - public void injectFailure() { - pendingFailure = true; - } - - @Override - protected boolean isEnded() { - return true; - } - - @Override - protected boolean isReady() { - return true; - } - - @Override - protected int doPrepare(long positionUs) throws ExoPlaybackException { - maybeFail(); - return STATE_PREPARED; - } - - @Override - protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - maybeFail(); - if (positionUs < currentPositionUs || positionUs > currentPositionUs + 1000000) { - currentPositionUs = positionUs; - textView.post(this); - } - } - - @Override - public void run() { - textView.setText(getRenderString()); - } - - private String getRenderString() { - return getTimeString() + " " + getQualityString() + " " + getBandwidthString() + " " - + renderer.codecCounters.getDebugString(); - } - - private String getTimeString() { - return "ms(" + (currentPositionUs / 1000) + ")"; - } - - private String getQualityString() { - Format format = player.getVideoFormat(); - return format == null ? "id:? br:? h:?" - : "id:" + format.id + " br:" + format.bitrate + " h:" + format.height; - } - - private String getBandwidthString() { - if (bandwidthMeter == null - || bandwidthMeter.getBitrateEstimate() == BandwidthMeter.NO_ESTIMATE) { - return "bw:?"; - } else { - return "bw:" + (bandwidthMeter.getBitrateEstimate() / 1000); - } - } - - @Override - protected long getCurrentPositionUs() { - return currentPositionUs; - } - - @Override - protected long getDurationUs() { - return TrackRenderer.MATCH_LONGEST_US; - } - - @Override - protected long getBufferedPositionUs() { - return TrackRenderer.END_OF_TRACK_US; - } - - @Override - protected void seekTo(long timeUs) { - currentPositionUs = timeUs; - } - - private void maybeFail() throws ExoPlaybackException { - if (pendingFailure) { - pendingFailure = false; - throw new ExoPlaybackException("fail() was called on DebugTrackRenderer"); - } - } - -} diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java index b475915d6a..c7e639699b 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java @@ -15,10 +15,12 @@ */ package com.google.android.exoplayer.demo.player; +import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.DummyTrackRenderer; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; +import com.google.android.exoplayer.MediaCodecTrackRenderer; import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.TimeRange; @@ -29,10 +31,12 @@ import com.google.android.exoplayer.chunk.Format; import com.google.android.exoplayer.chunk.MultiTrackChunkSource; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.hls.HlsSampleSource; -import com.google.android.exoplayer.metadata.MetadataTrackRenderer; +import com.google.android.exoplayer.metadata.MetadataTrackRenderer.MetadataRenderer; import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.TextRenderer; +import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; +import com.google.android.exoplayer.util.DebugTextViewHelper; import com.google.android.exoplayer.util.PlayerControl; import android.media.MediaCodec.CryptoException; @@ -54,7 +58,8 @@ import java.util.concurrent.CopyOnWriteArrayList; public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener, HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener, MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener, - StreamingDrmSessionManager.EventListener, TextRenderer { + StreamingDrmSessionManager.EventListener, TextRenderer, + MetadataRenderer>, DebugTextViewHelper.Provider { /** * Builds renderers for the player. @@ -84,9 +89,10 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi * multiple tracks. An individual element may be null if it does not have multiple tracks. * @param renderers Renderers indexed by {@link DemoPlayer} TYPE_* constants. An individual * element may be null if there do not exist tracks of the corresponding type. + * @param bandwidthMeter Provides an estimate of the currently available bandwidth. May be null. */ void onRenderers(String[][] trackNames, MultiTrackChunkSource[] multiTrackSources, - TrackRenderer[] renderers); + TrackRenderer[] renderers, BandwidthMeter bandwidthMeter); /** * Invoked if a {@link RendererBuilder} encounters an error. * @@ -163,12 +169,11 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi public static final int DISABLED_TRACK = -1; public static final int PRIMARY_TRACK = 0; - public static final int RENDERER_COUNT = 5; + public static final int RENDERER_COUNT = 4; public static final int TYPE_VIDEO = 0; public static final int TYPE_AUDIO = 1; public static final int TYPE_TEXT = 2; - public static final int TYPE_TIMED_METADATA = 3; - public static final int TYPE_DEBUG = 4; + public static final int TYPE_METADATA = 3; private static final int RENDERER_BUILDING_STATE_IDLE = 1; private static final int RENDERER_BUILDING_STATE_BUILDING = 2; @@ -187,9 +192,11 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi private Surface surface; private InternalRendererBuilderCallback builderCallback; private TrackRenderer videoRenderer; + private CodecCounters codecCounters; private Format videoFormat; private int videoTrackToRestore; + private BandwidthMeter bandwidthMeter; private MultiTrackChunkSource[] multiTrackSources; private String[][] trackNames; private int[] selectedTracks; @@ -275,10 +282,6 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi } } - public Format getVideoFormat() { - return videoFormat; - } - public void setBackgrounded(boolean backgrounded) { if (this.backgrounded == backgrounded) { return; @@ -310,7 +313,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi } /* package */ void onRenderers(String[][] trackNames, - MultiTrackChunkSource[] multiTrackSources, TrackRenderer[] renderers) { + MultiTrackChunkSource[] multiTrackSources, TrackRenderer[] renderers, + BandwidthMeter bandwidthMeter) { builderCallback = null; // Normalize the results. if (trackNames == null) { @@ -333,7 +337,12 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi // Complete preparation. this.trackNames = trackNames; this.videoRenderer = renderers[TYPE_VIDEO]; + this.codecCounters = videoRenderer instanceof MediaCodecTrackRenderer + ? ((MediaCodecTrackRenderer) videoRenderer).codecCounters + : renderers[TYPE_AUDIO] instanceof MediaCodecTrackRenderer + ? ((MediaCodecTrackRenderer) renderers[TYPE_AUDIO]).codecCounters : null; this.multiTrackSources = multiTrackSources; + this.bandwidthMeter = bandwidthMeter; pushSurface(false); pushTrackSelection(TYPE_VIDEO, true); pushTrackSelection(TYPE_AUDIO, true); @@ -387,6 +396,22 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi return playerState; } + @Override + public Format getFormat() { + return videoFormat; + } + + @Override + public BandwidthMeter getBandwidthMeter() { + return bandwidthMeter; + } + + @Override + public CodecCounters getCodecCounters() { + return codecCounters; + } + + @Override public long getCurrentPosition() { return player.getCurrentPosition(); } @@ -494,9 +519,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi } @Override - public void onDecoderInitialized( - String decoderName, - long elapsedRealtimeMs, + public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, long initializationDurationMs) { if (infoListener != null) { infoListener.onDecoderInitialized(decoderName, elapsedRealtimeMs, initializationDurationMs); @@ -512,19 +535,16 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi @Override public void onCues(List cues) { - processCues(cues); + if (captionListener != null && selectedTracks[TYPE_TEXT] != DISABLED_TRACK) { + captionListener.onCues(cues); + } } - /* package */ MetadataTrackRenderer.MetadataRenderer> - getId3MetadataRenderer() { - return new MetadataTrackRenderer.MetadataRenderer>() { - @Override - public void onMetadata(Map metadata) { - if (id3MetadataListener != null) { - id3MetadataListener.onId3Metadata(metadata); - } - } - }; + @Override + public void onMetadata(Map metadata) { + if (id3MetadataListener != null && selectedTracks[TYPE_METADATA] != DISABLED_TRACK) { + id3MetadataListener.onId3Metadata(metadata); + } } @Override @@ -612,13 +632,6 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi } } - /* package */ void processCues(List cues) { - if (captionListener == null || selectedTracks[TYPE_TEXT] == DISABLED_TRACK) { - return; - } - captionListener.onCues(cues); - } - private class InternalRendererBuilderCallback implements RendererBuilderCallback { private boolean canceled; @@ -629,9 +642,9 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi @Override public void onRenderers(String[][] trackNames, MultiTrackChunkSource[] multiTrackSources, - TrackRenderer[] renderers) { + TrackRenderer[] renderers, BandwidthMeter bandwidthMeter) { if (!canceled) { - DemoPlayer.this.onRenderers(trackNames, multiTrackSources, renderers); + DemoPlayer.this.onRenderers(trackNames, multiTrackSources, renderers, bandwidthMeter); } } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java index d2c0f8bee9..6cab32227d 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java @@ -29,7 +29,6 @@ import com.google.android.exoplayer.upstream.DefaultUriDataSource; import android.content.Context; import android.media.MediaCodec; import android.net.Uri; -import android.widget.TextView; /** * A {@link RendererBuilder} for streams that can be read using an {@link Extractor}. @@ -41,15 +40,12 @@ public class ExtractorRendererBuilder implements RendererBuilder { private final Context context; private final String userAgent; private final Uri uri; - private final TextView debugTextView; private final Extractor extractor; - public ExtractorRendererBuilder(Context context, String userAgent, Uri uri, - TextView debugTextView, Extractor extractor) { + public ExtractorRendererBuilder(Context context, String userAgent, Uri uri, Extractor extractor) { this.context = context; this.userAgent = userAgent; this.uri = uri; - this.debugTextView = debugTextView; this.extractor = extractor; } @@ -67,16 +63,11 @@ public class ExtractorRendererBuilder implements RendererBuilder { MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, null, true, player.getMainHandler(), player); - // Build the debug renderer. - TrackRenderer debugRenderer = debugTextView != null - ? new DebugTrackRenderer(debugTextView, player, videoRenderer, bandwidthMeter) : null; - // Invoke the callback. TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; - renderers[DemoPlayer.TYPE_DEBUG] = debugRenderer; - callback.onRenderers(null, null, renderers); + callback.onRenderers(null, null, renderers, bandwidthMeter); } } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java index 39b8393059..115f42c1da 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java @@ -43,7 +43,6 @@ import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback; import android.content.Context; import android.media.MediaCodec; import android.os.Handler; -import android.widget.TextView; import java.io.IOException; import java.util.Map; @@ -59,18 +58,16 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback> id3Renderer = - new MetadataTrackRenderer<>(sampleSource, new Id3Parser(), - player.getId3MetadataRenderer(), mainHandler.getLooper()); + new MetadataTrackRenderer<>(sampleSource, new Id3Parser(), player, mainHandler.getLooper()); Eia608TrackRenderer closedCaptionRenderer = new Eia608TrackRenderer(sampleSource, player, mainHandler.getLooper()); - // Build the debug renderer. - TrackRenderer debugRenderer = debugTextView != null - ? new DebugTrackRenderer(debugTextView, player, videoRenderer, bandwidthMeter) : null; - TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; - renderers[DemoPlayer.TYPE_TIMED_METADATA] = id3Renderer; + renderers[DemoPlayer.TYPE_METADATA] = id3Renderer; renderers[DemoPlayer.TYPE_TEXT] = closedCaptionRenderer; - renderers[DemoPlayer.TYPE_DEBUG] = debugRenderer; - callback.onRenderers(null, null, renderers); + callback.onRenderers(null, null, renderers, bandwidthMeter); } } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java index 9aa80915e4..faea15473a 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java @@ -32,6 +32,7 @@ import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallba import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.MediaDrmCallback; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; +import com.google.android.exoplayer.drm.UnsupportedDrmException; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingChunkSource; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement; @@ -46,16 +47,12 @@ import com.google.android.exoplayer.upstream.DefaultUriDataSource; import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.Util; -import android.annotation.TargetApi; import android.content.Context; import android.media.MediaCodec; -import android.media.UnsupportedSchemeException; import android.os.Handler; -import android.widget.TextView; import java.io.IOException; import java.util.Arrays; -import java.util.UUID; /** * A {@link RendererBuilder} for SmoothStreaming. @@ -73,19 +70,17 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, private final String userAgent; private final String url; private final MediaDrmCallback drmCallback; - private final TextView debugTextView; private DemoPlayer player; private RendererBuilderCallback callback; private ManifestFetcher manifestFetcher; public SmoothStreamingRendererBuilder(Context context, String userAgent, String url, - MediaDrmCallback drmCallback, TextView debugTextView) { + MediaDrmCallback drmCallback) { this.context = context; this.userAgent = userAgent; this.url = url; this.drmCallback = drmCallback; - this.debugTextView = debugTextView; } @Override @@ -118,12 +113,12 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, if (manifest.protectionElement != null) { if (Util.SDK_INT < 18) { callback.onRenderersError( - new UnsupportedDrmException(UnsupportedDrmException.REASON_NO_DRM)); + new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); return; } try { - drmSessionManager = V18Compat.getDrmSessionManager(manifest.protectionElement.uuid, player, - drmCallback); + drmSessionManager = new StreamingDrmSessionManager(manifest.protectionElement.uuid, + player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); } catch (UnsupportedDrmException e) { callback.onRenderersError(e); return; @@ -159,10 +154,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, // Build the video renderer. final MediaCodecVideoTrackRenderer videoRenderer; - final TrackRenderer debugRenderer; if (videoTrackIndices == null || videoTrackIndices.length == 0) { videoRenderer = null; - debugRenderer = null; } else { DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher, @@ -173,8 +166,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, DemoPlayer.TYPE_VIDEO); videoRenderer = new MediaCodecVideoTrackRenderer(videoSampleSource, drmSessionManager, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, null, mainHandler, player, 50); - debugRenderer = debugTextView != null - ? new DebugTrackRenderer(debugTextView, player, videoRenderer, bandwidthMeter) : null; } // Build the audio renderer. @@ -252,25 +243,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; renderers[DemoPlayer.TYPE_TEXT] = textRenderer; - renderers[DemoPlayer.TYPE_DEBUG] = debugRenderer; - callback.onRenderers(trackNames, multiTrackChunkSources, renderers); - } - - @TargetApi(18) - private static class V18Compat { - - public static DrmSessionManager getDrmSessionManager(UUID uuid, DemoPlayer player, - MediaDrmCallback drmCallback) throws UnsupportedDrmException { - try { - return new StreamingDrmSessionManager(uuid, player.getPlaybackLooper(), drmCallback, null, - player.getMainHandler(), player); - } catch (UnsupportedSchemeException e) { - throw new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME); - } catch (Exception e) { - throw new UnsupportedDrmException(UnsupportedDrmException.REASON_UNKNOWN, e); - } - } - + callback.onRenderers(trackNames, multiTrackChunkSources, renderers, bandwidthMeter); } } diff --git a/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java b/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java index 3705e5bb25..2ee73b05e6 100644 --- a/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java +++ b/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java @@ -110,11 +110,11 @@ public class StreamingDrmSessionManager implements DrmSessionManager { * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. - * @throws UnsupportedSchemeException If the specified DRM scheme is not supported. + * @throws UnsupportedDrmException If the specified DRM scheme is not supported. */ public static StreamingDrmSessionManager newWidevineInstance(Looper playbackLooper, MediaDrmCallback callback, HashMap optionalKeyRequestParameters, - Handler eventHandler, EventListener eventListener) throws UnsupportedSchemeException { + Handler eventHandler, EventListener eventListener) throws UnsupportedDrmException { return new StreamingDrmSessionManager(WIDEVINE_UUID, playbackLooper, callback, optionalKeyRequestParameters, eventHandler, eventListener); } @@ -132,11 +132,11 @@ public class StreamingDrmSessionManager implements DrmSessionManager { * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. - * @throws UnsupportedSchemeException If the specified DRM scheme is not supported. + * @throws UnsupportedDrmException If the specified DRM scheme is not supported. */ public static StreamingDrmSessionManager newPlayReadyInstance(Looper playbackLooper, MediaDrmCallback callback, String customData, Handler eventHandler, - EventListener eventListener) throws UnsupportedSchemeException { + EventListener eventListener) throws UnsupportedDrmException { HashMap optionalKeyRequestParameters; if (!TextUtils.isEmpty(customData)) { optionalKeyRequestParameters = new HashMap<>(); @@ -158,17 +158,23 @@ public class StreamingDrmSessionManager implements DrmSessionManager { * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. - * @throws UnsupportedSchemeException If the specified DRM scheme is not supported. + * @throws UnsupportedDrmException If the specified DRM scheme is not supported. */ public StreamingDrmSessionManager(UUID uuid, Looper playbackLooper, MediaDrmCallback callback, HashMap optionalKeyRequestParameters, Handler eventHandler, - EventListener eventListener) throws UnsupportedSchemeException { + EventListener eventListener) throws UnsupportedDrmException { this.uuid = uuid; this.callback = callback; this.optionalKeyRequestParameters = optionalKeyRequestParameters; this.eventHandler = eventHandler; this.eventListener = eventListener; - mediaDrm = new MediaDrm(uuid); + try { + mediaDrm = new MediaDrm(uuid); + } catch (UnsupportedSchemeException e) { + throw new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME, e); + } catch (Exception e) { + throw new UnsupportedDrmException(UnsupportedDrmException.REASON_INSTANTIATION_ERROR, e); + } mediaDrm.setOnEventListener(new MediaDrmEventListener()); mediaDrmHandler = new MediaDrmHandler(playbackLooper); postResponseHandler = new PostResponseHandler(playbackLooper); @@ -176,12 +182,12 @@ public class StreamingDrmSessionManager implements DrmSessionManager { } @Override - public int getState() { + public final int getState() { return state; } @Override - public MediaCrypto getMediaCrypto() { + public final MediaCrypto getMediaCrypto() { if (state != STATE_OPENED && state != STATE_OPENED_WITH_KEYS) { throw new IllegalStateException(); } @@ -197,7 +203,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager { } @Override - public Exception getError() { + public final Exception getError() { return state == STATE_ERROR ? lastException : null; } @@ -250,7 +256,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager { } @Override - public void open(DrmInitData drmInitData) { + public final void open(DrmInitData drmInitData) { if (++openCount != 1) { return; } @@ -272,7 +278,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager { } @Override - public void close() { + public final void close() { if (--openCount != 0) { return; } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/UnsupportedDrmException.java b/library/src/main/java/com/google/android/exoplayer/drm/UnsupportedDrmException.java similarity index 70% rename from demo/src/main/java/com/google/android/exoplayer/demo/player/UnsupportedDrmException.java rename to library/src/main/java/com/google/android/exoplayer/drm/UnsupportedDrmException.java index e71e1b04d3..b5f2d12211 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/UnsupportedDrmException.java +++ b/library/src/main/java/com/google/android/exoplayer/drm/UnsupportedDrmException.java @@ -13,16 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer.demo.player; +package com.google.android.exoplayer.drm; /** - * Exception thrown when the required level of DRM is not supported. + * Thrown when the requested DRM scheme is not supported. */ public final class UnsupportedDrmException extends Exception { - public static final int REASON_NO_DRM = 0; + /** + * The requested DRM scheme is unsupported by the device. + */ public static final int REASON_UNSUPPORTED_SCHEME = 1; - public static final int REASON_UNKNOWN = 2; + /** + * There device advertises support for the requested DRM scheme, but there was an error + * instantiating it. The cause can be retrieved using {@link #getCause()}. + */ + public static final int REASON_INSTANTIATION_ERROR = 2; public final int reason; diff --git a/library/src/main/java/com/google/android/exoplayer/util/DebugTextViewHelper.java b/library/src/main/java/com/google/android/exoplayer/util/DebugTextViewHelper.java new file mode 100644 index 0000000000..134b40e744 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/util/DebugTextViewHelper.java @@ -0,0 +1,125 @@ +/* + * 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 com.google.android.exoplayer.CodecCounters; +import com.google.android.exoplayer.chunk.Format; +import com.google.android.exoplayer.upstream.BandwidthMeter; + +import android.widget.TextView; + +/** + * A helper class for periodically updating debug information displayed by a {@link TextView}. + */ +public final class DebugTextViewHelper implements Runnable { + + /** + * Provides debug information about an ongoing playback. + */ + public interface Provider { + + /** + * Returns the current playback position, in milliseconds. + */ + long getCurrentPosition(); + + /** + * Returns a format whose information should be displayed, or null. + */ + Format getFormat(); + + /** + * Returns a {@link BandwidthMeter} whose estimate should be displayed, or null. + */ + BandwidthMeter getBandwidthMeter(); + + /** + * Returns a {@link CodecCounters} whose information should be displayed, or null. + */ + CodecCounters getCodecCounters(); + + } + + private static final int REFRESH_INTERVAL_MS = 1000; + + private final TextView textView; + private final Provider debuggable; + + /** + * @param debuggable The {@link Provider} from which debug information should be obtained. + * @param textView The {@link TextView} that should be updated to display the information. + */ + public DebugTextViewHelper(Provider debuggable, TextView textView) { + this.debuggable = debuggable; + this.textView = textView; + } + + /** + * Starts periodic updates of the {@link TextView}. + *

+ * Should be called from the application's main thread. + */ + public void start() { + stop(); + run(); + } + + /** + * Stops periodic updates of the {@link TextView}. + *

+ * Should be called from the application's main thread. + */ + public void stop() { + textView.removeCallbacks(this); + } + + @Override + public void run() { + textView.setText(getRenderString()); + textView.postDelayed(this, REFRESH_INTERVAL_MS); + } + + private String getRenderString() { + return getTimeString() + " " + getQualityString() + " " + getBandwidthString() + " " + + getVideoCodecCountersString(); + } + + private String getTimeString() { + return "ms(" + debuggable.getCurrentPosition() + ")"; + } + + private String getQualityString() { + Format format = debuggable.getFormat(); + return format == null ? "id:? br:? h:?" + : "id:" + format.id + " br:" + format.bitrate + " h:" + format.height; + } + + private String getBandwidthString() { + BandwidthMeter bandwidthMeter = debuggable.getBandwidthMeter(); + if (bandwidthMeter == null + || bandwidthMeter.getBitrateEstimate() == BandwidthMeter.NO_ESTIMATE) { + return "bw:?"; + } else { + return "bw:" + (bandwidthMeter.getBitrateEstimate() / 1000); + } + } + + private String getVideoCodecCountersString() { + CodecCounters codecCounters = debuggable.getCodecCounters(); + return codecCounters == null ? "" : codecCounters.getDebugString(); + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/util/Util.java b/library/src/main/java/com/google/android/exoplayer/util/Util.java index 5dc2993ef6..4bfab023e5 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/Util.java +++ b/library/src/main/java/com/google/android/exoplayer/util/Util.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.text.TextUtils; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -40,6 +41,7 @@ import java.util.Collections; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.TimeZone; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -583,4 +585,56 @@ public final class Util { + ") " + "ExoPlayerLib/" + ExoPlayerLibraryInfo.VERSION; } + /** + * Executes a post request using {@link HttpURLConnection}. + * + * @param url The request URL. + * @param data The request body, or null. + * @param requestProperties Request properties, or null. + * @return The response body. + * @throws IOException If an error occurred making the request. + */ + // TODO: Remove this and use HttpDataSource once DataSpec supports inclusion of a POST body. + public static byte[] executePost(String url, byte[] data, Map requestProperties) + throws IOException { + HttpURLConnection urlConnection = null; + try { + urlConnection = (HttpURLConnection) new URL(url).openConnection(); + urlConnection.setRequestMethod("POST"); + urlConnection.setDoOutput(data != null); + urlConnection.setDoInput(true); + if (requestProperties != null) { + for (Map.Entry requestProperty : requestProperties.entrySet()) { + urlConnection.setRequestProperty(requestProperty.getKey(), requestProperty.getValue()); + } + } + // Write the request body, if there is one. + if (data != null) { + OutputStream out = urlConnection.getOutputStream(); + try { + out.write(data); + } finally { + out.close(); + } + } + // Read and return the response body. + InputStream inputStream = urlConnection.getInputStream(); + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byte scratch[] = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(scratch)) != -1) { + byteArrayOutputStream.write(scratch, 0, bytesRead); + } + return byteArrayOutputStream.toByteArray(); + } finally { + inputStream.close(); + } + } finally { + if (urlConnection != null) { + urlConnection.disconnect(); + } + } + } + }