├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── VideoPlayaer.iml ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── gujarat │ │ └── videoplayaer │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── ic_launcher.svg │ ├── java │ │ └── gujarat │ │ │ └── videoplayaer │ │ │ ├── EventLogger.java │ │ │ ├── PlayerActivity.java │ │ │ ├── SampleChooserActivity.java │ │ │ ├── Samples.java │ │ │ ├── SmoothStreamingTestMediaDrmCallback.java │ │ │ ├── TestPlayerActivity.java │ │ │ ├── WidevineTestMediaDrmCallback.java │ │ │ ├── controller │ │ │ └── TestPlayerController.java │ │ │ └── player │ │ │ ├── DashRendererBuilder.java │ │ │ ├── DemoPlayer.java │ │ │ ├── ExtractorRendererBuilder.java │ │ │ ├── HlsRendererBuilder.java │ │ │ └── SmoothStreamingRendererBuilder.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxxhdpi │ │ └── ic_launcher.png │ │ ├── layout │ │ ├── player_activity.xml │ │ ├── sample_chooser_activity.xml │ │ ├── sample_chooser_inline_header.xml │ │ └── test_player.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── constants.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── gujarat │ └── videoplayaer │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | VideoPlayaer -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidVideoPlayer 2 | This is sample project [From Exoplayer demo](https://google.github.io/ExoPlayer/demo-application.html). 3 | 4 | you can download and test it to your device and emulator. 5 | 6 | You need to see these files for understanding : 7 | 8 | - Layout : test_player.xml 9 | - TestPlayerActivity 10 | - TestPlayerController 11 | 12 | I've tested it and play video .mp4 file over HTTP. 13 | -------------------------------------------------------------------------------- /VideoPlayaer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "gujarat.videoplayaer" 9 | minSdkVersion 16 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.1.0' 26 | compile 'com.android.support:design:23.1.0' 27 | compile 'com.google.android.exoplayer:exoplayer:r1.5.2' 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/gujarat/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/gujarat/videoplayaer/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package gujarat.videoplayaer; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/assets/ic_launcher.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 39 | 41 | 44 | 48 | 52 | 53 | 56 | 60 | 64 | 65 | 67 | 71 | 75 | 76 | 80 | 85 | 91 | 96 | 101 | 107 | 108 | 112 | 117 | 123 | 128 | 133 | 139 | 144 | 150 | 156 | 161 | 166 | 172 | 173 | 177 | 182 | 188 | 193 | 198 | 204 | 205 | 209 | 214 | 220 | 225 | 230 | 236 | 237 | 241 | 246 | 252 | 257 | 262 | 268 | 269 | 273 | 278 | 284 | 289 | 294 | 300 | 301 | 305 | 310 | 316 | 320 | 325 | 331 | 332 | 335 | 340 | 341 | 345 | 350 | 356 | 361 | 366 | 372 | 373 | 382 | 391 | 394 | 398 | 402 | 403 | 413 | 424 | 432 | 437 | 443 | 448 | 453 | 459 | 460 | 468 | 473 | 479 | 484 | 489 | 495 | 496 | 500 | 505 | 511 | 516 | 521 | 527 | 528 | 529 | 531 | 532 | 534 | image/svg+xml 535 | 537 | 538 | 539 | 540 | 541 | 556 | 561 | 564 | 569 | 574 | 579 | 586 | 587 | 588 | 594 | 599 | 604 | 608 | 613 | 618 | 623 | 624 | 628 | 630 | 635 | 640 | 645 | 646 | 647 | 648 | 654 | 659 | 660 | 661 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/EventLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer; 17 | 18 | import android.media.MediaCodec.CryptoException; 19 | import android.os.SystemClock; 20 | import android.util.Log; 21 | 22 | import com.google.android.exoplayer.ExoPlayer; 23 | import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; 24 | import com.google.android.exoplayer.TimeRange; 25 | import com.google.android.exoplayer.audio.AudioTrack; 26 | import com.google.android.exoplayer.chunk.Format; 27 | import com.google.android.exoplayer.util.VerboseLogUtil; 28 | 29 | import java.io.IOException; 30 | import java.text.NumberFormat; 31 | import java.util.Locale; 32 | 33 | import gujarat.videoplayaer.player.DemoPlayer; 34 | 35 | /** 36 | * Logs player events using {@link android.util.Log}. 37 | */ 38 | public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener, 39 | DemoPlayer.InternalErrorListener{ 40 | 41 | private static final String TAG = "EventLogger"; 42 | private static final NumberFormat TIME_FORMAT; 43 | static { 44 | TIME_FORMAT = NumberFormat.getInstance(Locale.US); 45 | TIME_FORMAT.setMinimumFractionDigits(2); 46 | TIME_FORMAT.setMaximumFractionDigits(2); 47 | } 48 | 49 | private long sessionStartTimeMs; 50 | private long[] loadStartTimeMs; 51 | private long[] availableRangeValuesUs; 52 | 53 | public EventLogger() { 54 | loadStartTimeMs = new long[DemoPlayer.RENDERER_COUNT]; 55 | } 56 | 57 | public void startSession() { 58 | sessionStartTimeMs = SystemClock.elapsedRealtime(); 59 | Log.d(TAG, "start [0]"); 60 | } 61 | 62 | public void endSession() { 63 | Log.d(TAG, "end [" + getSessionTimeString() + "]"); 64 | } 65 | 66 | // DemoPlayer.Listener 67 | 68 | @Override 69 | public void onStateChanged(boolean playWhenReady, int state) { 70 | Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", " 71 | + getStateString(state) + "]"); 72 | } 73 | 74 | @Override 75 | public void onError(Exception e) { 76 | Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e); 77 | } 78 | 79 | @Override 80 | public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, 81 | float pixelWidthHeightRatio) { 82 | Log.d(TAG, "videoSizeChanged [" + width + ", " + height + ", " + unappliedRotationDegrees 83 | + ", " + pixelWidthHeightRatio + "]"); 84 | } 85 | 86 | // DemoPlayer.InfoListener 87 | 88 | @Override 89 | public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) { 90 | Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + ", " 91 | + getTimeString(elapsedMs) + ", " + bitrateEstimate + "]"); 92 | } 93 | 94 | @Override 95 | public void onDroppedFrames(int count, long elapsed) { 96 | Log.d(TAG, "droppedFrames [" + getSessionTimeString() + ", " + count + "]"); 97 | } 98 | 99 | @Override 100 | public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, 101 | long mediaStartTimeMs, long mediaEndTimeMs) { 102 | loadStartTimeMs[sourceId] = SystemClock.elapsedRealtime(); 103 | if (VerboseLogUtil.isTagEnabled(TAG)) { 104 | Log.v(TAG, "loadStart [" + getSessionTimeString() + ", " + sourceId + ", " + type 105 | + ", " + mediaStartTimeMs + ", " + mediaEndTimeMs + "]"); 106 | } 107 | } 108 | 109 | @Override 110 | public void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, 111 | long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs) { 112 | if (VerboseLogUtil.isTagEnabled(TAG)) { 113 | long downloadTime = SystemClock.elapsedRealtime() - loadStartTimeMs[sourceId]; 114 | Log.v(TAG, "loadEnd [" + getSessionTimeString() + ", " + sourceId + ", " + downloadTime 115 | + "]"); 116 | } 117 | } 118 | 119 | @Override 120 | public void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs) { 121 | Log.d(TAG, "videoFormat [" + getSessionTimeString() + ", " + format.id + ", " 122 | + Integer.toString(trigger) + "]"); 123 | } 124 | 125 | @Override 126 | public void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs) { 127 | Log.d(TAG, "audioFormat [" + getSessionTimeString() + ", " + format.id + ", " 128 | + Integer.toString(trigger) + "]"); 129 | } 130 | 131 | // DemoPlayer.InternalErrorListener 132 | 133 | @Override 134 | public void onLoadError(int sourceId, IOException e) { 135 | printInternalError("loadError", e); 136 | } 137 | 138 | @Override 139 | public void onRendererInitializationError(Exception e) { 140 | printInternalError("rendererInitError", e); 141 | } 142 | 143 | @Override 144 | public void onDrmSessionManagerError(Exception e) { 145 | printInternalError("drmSessionManagerError", e); 146 | } 147 | 148 | @Override 149 | public void onDecoderInitializationError(DecoderInitializationException e) { 150 | printInternalError("decoderInitializationError", e); 151 | } 152 | 153 | @Override 154 | public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { 155 | printInternalError("audioTrackInitializationError", e); 156 | } 157 | 158 | @Override 159 | public void onAudioTrackWriteError(AudioTrack.WriteException e) { 160 | printInternalError("audioTrackWriteError", e); 161 | } 162 | 163 | @Override 164 | public void onCryptoError(CryptoException e) { 165 | printInternalError("cryptoError", e); 166 | } 167 | 168 | @Override 169 | public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, 170 | long initializationDurationMs) { 171 | Log.d(TAG, "decoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]"); 172 | } 173 | 174 | @Override 175 | public void onAvailableRangeChanged(TimeRange availableRange) { 176 | availableRangeValuesUs = availableRange.getCurrentBoundsUs(availableRangeValuesUs); 177 | Log.d(TAG, "availableRange [" + availableRange.isStatic() + ", " + availableRangeValuesUs[0] 178 | + ", " + availableRangeValuesUs[1] + "]"); 179 | } 180 | 181 | private void printInternalError(String type, Exception e) { 182 | Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e); 183 | } 184 | 185 | private String getStateString(int state) { 186 | switch (state) { 187 | case ExoPlayer.STATE_BUFFERING: 188 | return "B"; 189 | case ExoPlayer.STATE_ENDED: 190 | return "E"; 191 | case ExoPlayer.STATE_IDLE: 192 | return "I"; 193 | case ExoPlayer.STATE_PREPARING: 194 | return "P"; 195 | case ExoPlayer.STATE_READY: 196 | return "R"; 197 | default: 198 | return "?"; 199 | } 200 | } 201 | 202 | private String getSessionTimeString() { 203 | return getTimeString(SystemClock.elapsedRealtime() - sessionStartTimeMs); 204 | } 205 | 206 | private String getTimeString(long timeMs) { 207 | return TIME_FORMAT.format((timeMs) / 1000f); 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/PlayerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer; 17 | 18 | import android.annotation.TargetApi; 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.net.Uri; 23 | import android.os.Bundle; 24 | import android.text.TextUtils; 25 | import android.util.Log; 26 | import android.view.KeyEvent; 27 | import android.view.Menu; 28 | import android.view.MenuItem; 29 | import android.view.MotionEvent; 30 | import android.view.SurfaceHolder; 31 | import android.view.SurfaceView; 32 | import android.view.View; 33 | import android.view.View.OnClickListener; 34 | import android.view.View.OnKeyListener; 35 | import android.view.View.OnTouchListener; 36 | import android.view.accessibility.CaptioningManager; 37 | import android.widget.Button; 38 | import android.widget.MediaController; 39 | import android.widget.PopupMenu; 40 | import android.widget.PopupMenu.OnMenuItemClickListener; 41 | import android.widget.TextView; 42 | import android.widget.Toast; 43 | 44 | import com.google.android.exoplayer.AspectRatioFrameLayout; 45 | import com.google.android.exoplayer.ExoPlayer; 46 | import com.google.android.exoplayer.MediaFormat; 47 | import com.google.android.exoplayer.audio.AudioCapabilities; 48 | import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver; 49 | import com.google.android.exoplayer.drm.UnsupportedDrmException; 50 | import com.google.android.exoplayer.metadata.GeobMetadata; 51 | import com.google.android.exoplayer.metadata.PrivMetadata; 52 | import com.google.android.exoplayer.metadata.TxxxMetadata; 53 | import com.google.android.exoplayer.text.CaptionStyleCompat; 54 | import com.google.android.exoplayer.text.Cue; 55 | import com.google.android.exoplayer.text.SubtitleLayout; 56 | import com.google.android.exoplayer.util.DebugTextViewHelper; 57 | import com.google.android.exoplayer.util.MimeTypes; 58 | import com.google.android.exoplayer.util.Util; 59 | import com.google.android.exoplayer.util.VerboseLogUtil; 60 | 61 | import java.net.CookieHandler; 62 | import java.net.CookieManager; 63 | import java.net.CookiePolicy; 64 | import java.util.List; 65 | import java.util.Locale; 66 | import java.util.Map; 67 | 68 | import gujarat.videoplayaer.player.DashRendererBuilder; 69 | import gujarat.videoplayaer.player.DemoPlayer; 70 | import gujarat.videoplayaer.player.ExtractorRendererBuilder; 71 | import gujarat.videoplayaer.player.HlsRendererBuilder; 72 | import gujarat.videoplayaer.player.SmoothStreamingRendererBuilder; 73 | 74 | /** 75 | * An activity that plays media using {@link DemoPlayer}. 76 | */ 77 | public class PlayerActivity extends Activity implements SurfaceHolder.Callback, OnClickListener, 78 | DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener, 79 | AudioCapabilitiesReceiver.Listener { 80 | 81 | // For use within demo app code. 82 | public static final String CONTENT_ID_EXTRA = "content_id"; 83 | public static final String CONTENT_TYPE_EXTRA = "content_type"; 84 | public static final int TYPE_DASH = 0; 85 | public static final int TYPE_SS = 1; 86 | public static final int TYPE_HLS = 2; 87 | public static final int TYPE_OTHER = 3; 88 | 89 | // For use when launching the demo app using adb. 90 | private static final String CONTENT_EXT_EXTRA = "type"; 91 | private static final String EXT_DASH = ".mpd"; 92 | private static final String EXT_SS = ".ism"; 93 | private static final String EXT_HLS = ".m3u8"; 94 | 95 | private static final String TAG = "PlayerActivity"; 96 | private static final int MENU_GROUP_TRACKS = 1; 97 | private static final int ID_OFFSET = 2; 98 | 99 | private static final CookieManager defaultCookieManager; 100 | static { 101 | defaultCookieManager = new CookieManager(); 102 | defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); 103 | } 104 | 105 | private EventLogger eventLogger; 106 | private MediaController mediaController; 107 | private View debugRootView; 108 | private View shutterView; 109 | private AspectRatioFrameLayout videoFrame; 110 | private SurfaceView surfaceView; 111 | private TextView debugTextView; 112 | private TextView playerStateTextView; 113 | private SubtitleLayout subtitleLayout; 114 | private Button videoButton; 115 | private Button audioButton; 116 | private Button textButton; 117 | private Button retryButton; 118 | 119 | private DemoPlayer player; 120 | private DebugTextViewHelper debugViewHelper; 121 | private boolean playerNeedsPrepare; 122 | 123 | private long playerPosition; 124 | private boolean enableBackgroundAudio; 125 | 126 | private Uri contentUri; 127 | private int contentType; 128 | private String contentId; 129 | 130 | private AudioCapabilitiesReceiver audioCapabilitiesReceiver; 131 | 132 | // Activity lifecycle 133 | 134 | private static String buildTrackName(MediaFormat format) { 135 | if (format.adaptive) { 136 | return "auto"; 137 | } 138 | String trackName; 139 | if (MimeTypes.isVideo(format.mimeType)) { 140 | trackName = joinWithSeparator(joinWithSeparator(buildResolutionString(format), 141 | buildBitrateString(format)), buildTrackIdString(format)); 142 | } else if (MimeTypes.isAudio(format.mimeType)) { 143 | trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(buildLanguageString(format), 144 | buildAudioPropertyString(format)), buildBitrateString(format)), 145 | buildTrackIdString(format)); 146 | } else { 147 | trackName = joinWithSeparator(joinWithSeparator(buildLanguageString(format), 148 | buildBitrateString(format)), buildTrackIdString(format)); 149 | } 150 | return trackName.length() == 0 ? "unknown" : trackName; 151 | } 152 | 153 | private static String buildResolutionString(MediaFormat format) { 154 | return format.width == MediaFormat.NO_VALUE || format.height == MediaFormat.NO_VALUE 155 | ? "" : format.width + "x" + format.height; 156 | } 157 | 158 | private static String buildAudioPropertyString(MediaFormat format) { 159 | return format.channelCount == MediaFormat.NO_VALUE || format.sampleRate == MediaFormat.NO_VALUE 160 | ? "" : format.channelCount + "ch, " + format.sampleRate + "Hz"; 161 | } 162 | 163 | private static String buildLanguageString(MediaFormat format) { 164 | return TextUtils.isEmpty(format.language) || "und".equals(format.language) ? "" 165 | : format.language; 166 | } 167 | 168 | private static String buildBitrateString(MediaFormat format) { 169 | return format.bitrate == MediaFormat.NO_VALUE ? "" 170 | : String.format(Locale.US, "%.2fMbit", format.bitrate / 1000000f); 171 | } 172 | 173 | // OnClickListener methods 174 | 175 | private static String joinWithSeparator(String first, String second) { 176 | return first.length() == 0 ? second : (second.length() == 0 ? first : first + ", " + second); 177 | } 178 | 179 | // AudioCapabilitiesReceiver.Listener methods 180 | 181 | private static String buildTrackIdString(MediaFormat format) { 182 | return format.trackId == MediaFormat.NO_VALUE ? "" 183 | : String.format(Locale.US, " (%d)", format.trackId); 184 | } 185 | 186 | /** 187 | * Makes a best guess to infer the type from a media {@link android.net.Uri} and an optional overriding file 188 | * extension. 189 | * 190 | * @param uri The {@link android.net.Uri} of the media. 191 | * @param fileExtension An overriding file extension. 192 | * @return The inferred type. 193 | */ 194 | private static int inferContentType(Uri uri, String fileExtension) { 195 | String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension 196 | : uri.getLastPathSegment(); 197 | if (lastPathSegment == null) { 198 | return TYPE_OTHER; 199 | } else if (lastPathSegment.endsWith(EXT_DASH)) { 200 | return TYPE_DASH; 201 | } else if (lastPathSegment.endsWith(EXT_SS)) { 202 | return TYPE_SS; 203 | } else if (lastPathSegment.endsWith(EXT_HLS)) { 204 | return TYPE_HLS; 205 | } else { 206 | return TYPE_OTHER; 207 | } 208 | } 209 | 210 | @Override 211 | public void onCreate(Bundle savedInstanceState) { 212 | super.onCreate(savedInstanceState); 213 | 214 | setContentView(R.layout.player_activity); 215 | View root = findViewById(R.id.root); 216 | root.setOnTouchListener(new OnTouchListener() { 217 | @Override 218 | public boolean onTouch(View view, MotionEvent motionEvent) { 219 | if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { 220 | toggleControlsVisibility(); 221 | } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { 222 | view.performClick(); 223 | } 224 | return true; 225 | } 226 | }); 227 | root.setOnKeyListener(new OnKeyListener() { 228 | @Override 229 | public boolean onKey(View v, int keyCode, KeyEvent event) { 230 | if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE 231 | || keyCode == KeyEvent.KEYCODE_MENU) { 232 | return false; 233 | } 234 | return mediaController.dispatchKeyEvent(event); 235 | } 236 | }); 237 | 238 | shutterView = findViewById(R.id.shutter); 239 | debugRootView = findViewById(R.id.controls_root); 240 | 241 | videoFrame = (AspectRatioFrameLayout) findViewById(R.id.video_frame); 242 | surfaceView = (SurfaceView) findViewById(R.id.surface_view); 243 | surfaceView.getHolder().addCallback(this); 244 | debugTextView = (TextView) findViewById(R.id.debug_text_view); 245 | 246 | playerStateTextView = (TextView) findViewById(R.id.player_state_view); 247 | subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles); 248 | 249 | mediaController = new MediaController(this); 250 | mediaController.setAnchorView(root); 251 | retryButton = (Button) findViewById(R.id.retry_button); 252 | retryButton.setOnClickListener(this); 253 | videoButton = (Button) findViewById(R.id.video_controls); 254 | audioButton = (Button) findViewById(R.id.audio_controls); 255 | textButton = (Button) findViewById(R.id.text_controls); 256 | 257 | CookieHandler currentHandler = CookieHandler.getDefault(); 258 | if (currentHandler != defaultCookieManager) { 259 | CookieHandler.setDefault(defaultCookieManager); 260 | } 261 | 262 | audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(this, this); 263 | audioCapabilitiesReceiver.register(); 264 | } 265 | 266 | @Override 267 | public void onNewIntent(Intent intent) { 268 | releasePlayer(); 269 | playerPosition = 0; 270 | setIntent(intent); 271 | } 272 | 273 | @Override 274 | public void onResume() { 275 | super.onResume(); 276 | Intent intent = getIntent(); 277 | contentUri = intent.getData(); 278 | contentType = intent.getIntExtra(CONTENT_TYPE_EXTRA, 279 | inferContentType(contentUri, intent.getStringExtra(CONTENT_EXT_EXTRA))); 280 | contentId = intent.getStringExtra(CONTENT_ID_EXTRA); 281 | configureSubtitleView(); 282 | if (player == null) { 283 | preparePlayer(true); 284 | } else { 285 | player.setBackgrounded(false); 286 | } 287 | } 288 | 289 | @Override 290 | public void onPause() { 291 | super.onPause(); 292 | if (!enableBackgroundAudio) { 293 | releasePlayer(); 294 | } else { 295 | player.setBackgrounded(true); 296 | } 297 | shutterView.setVisibility(View.VISIBLE); 298 | } 299 | 300 | @Override 301 | public void onDestroy() { 302 | super.onDestroy(); 303 | audioCapabilitiesReceiver.unregister(); 304 | releasePlayer(); 305 | } 306 | 307 | @Override 308 | public void onClick(View view) { 309 | if (view == retryButton) { 310 | preparePlayer(true); 311 | } 312 | } 313 | 314 | @Override 315 | public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) { 316 | if (player == null) { 317 | return; 318 | } 319 | boolean backgrounded = player.getBackgrounded(); 320 | boolean playWhenReady = player.getPlayWhenReady(); 321 | releasePlayer(); 322 | preparePlayer(playWhenReady); 323 | player.setBackgrounded(backgrounded); 324 | } 325 | private DemoPlayer.RendererBuilder getRendererBuilder() { 326 | String userAgent = Util.getUserAgent(this, "ExoPlayerDemo"); 327 | switch (contentType) { 328 | case TYPE_SS: 329 | return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(), 330 | new SmoothStreamingTestMediaDrmCallback()); 331 | case TYPE_DASH: 332 | return new DashRendererBuilder(this, userAgent, contentUri.toString(), 333 | new WidevineTestMediaDrmCallback(contentId)); 334 | case TYPE_HLS: 335 | return new HlsRendererBuilder(this, userAgent, contentUri.toString()); 336 | case TYPE_OTHER: 337 | return new ExtractorRendererBuilder(this, userAgent, contentUri); 338 | default: 339 | throw new IllegalStateException("Unsupported type: " + contentType); 340 | } 341 | } 342 | 343 | private void preparePlayer(boolean playWhenReady) { 344 | if (player == null) { 345 | player = new DemoPlayer(getRendererBuilder()); 346 | player.addListener(this); 347 | player.setCaptionListener(this); 348 | player.setMetadataListener(this); 349 | player.seekTo(playerPosition); 350 | playerNeedsPrepare = true; 351 | mediaController.setMediaPlayer(player.getPlayerControl()); 352 | mediaController.setEnabled(true); 353 | eventLogger = new EventLogger(); 354 | eventLogger.startSession(); 355 | player.addListener(eventLogger); 356 | player.setInfoListener(eventLogger); 357 | player.setInternalErrorListener(eventLogger); 358 | debugViewHelper = new DebugTextViewHelper(player, debugTextView); 359 | debugViewHelper.start(); 360 | } 361 | if (playerNeedsPrepare) { 362 | player.prepare(); 363 | playerNeedsPrepare = false; 364 | updateButtonVisibilities(); 365 | } 366 | player.setSurface(surfaceView.getHolder().getSurface()); 367 | player.setPlayWhenReady(playWhenReady); 368 | } 369 | private void releasePlayer() { 370 | if (player != null) { 371 | debugViewHelper.stop(); 372 | debugViewHelper = null; 373 | playerPosition = player.getCurrentPosition(); 374 | player.release(); 375 | player = null; 376 | eventLogger.endSession(); 377 | eventLogger = null; 378 | } 379 | } 380 | 381 | @Override 382 | public void onStateChanged(boolean playWhenReady, int playbackState) { 383 | if (playbackState == ExoPlayer.STATE_ENDED) { 384 | showControls(); 385 | } 386 | String text = "playWhenReady=" + playWhenReady + ", playbackState="; 387 | switch(playbackState) { 388 | case ExoPlayer.STATE_BUFFERING: 389 | text += "buffering"; 390 | break; 391 | case ExoPlayer.STATE_ENDED: 392 | text += "ended"; 393 | break; 394 | case ExoPlayer.STATE_IDLE: 395 | text += "idle"; 396 | break; 397 | case ExoPlayer.STATE_PREPARING: 398 | text += "preparing"; 399 | break; 400 | case ExoPlayer.STATE_READY: 401 | text += "ready"; 402 | break; 403 | default: 404 | text += "unknown"; 405 | break; 406 | } 407 | playerStateTextView.setText(text); 408 | updateButtonVisibilities(); 409 | } 410 | @Override 411 | public void onError(Exception e) { 412 | if (e instanceof UnsupportedDrmException) { 413 | // Special case DRM failures. 414 | UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e; 415 | int stringId = Util.SDK_INT < 18 ? R.string.drm_error_not_supported 416 | : unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME 417 | ? R.string.drm_error_unsupported_scheme : R.string.drm_error_unknown; 418 | Toast.makeText(getApplicationContext(), stringId, Toast.LENGTH_LONG).show(); 419 | } 420 | playerNeedsPrepare = true; 421 | updateButtonVisibilities(); 422 | showControls(); 423 | } 424 | 425 | @Override 426 | public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, 427 | float pixelWidthAspectRatio) { 428 | shutterView.setVisibility(View.GONE); 429 | videoFrame.setAspectRatio( 430 | height == 0 ? 1 : (width * pixelWidthAspectRatio) / height); 431 | } 432 | private void updateButtonVisibilities() { 433 | retryButton.setVisibility(playerNeedsPrepare ? View.VISIBLE : View.GONE); 434 | videoButton.setVisibility(haveTracks(DemoPlayer.TYPE_VIDEO) ? View.VISIBLE : View.GONE); 435 | audioButton.setVisibility(haveTracks(DemoPlayer.TYPE_AUDIO) ? View.VISIBLE : View.GONE); 436 | textButton.setVisibility(haveTracks(DemoPlayer.TYPE_TEXT) ? View.VISIBLE : View.GONE); 437 | } 438 | 439 | private boolean haveTracks(int type) { 440 | return player != null && player.getTrackCount(type) > 0; 441 | } 442 | public void showVideoPopup(View v) { 443 | PopupMenu popup = new PopupMenu(this, v); 444 | configurePopupWithTracks(popup, null, DemoPlayer.TYPE_VIDEO); 445 | popup.show(); 446 | } 447 | 448 | public void showAudioPopup(View v) { 449 | PopupMenu popup = new PopupMenu(this, v); 450 | Menu menu = popup.getMenu(); 451 | menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.enable_background_audio); 452 | final MenuItem backgroundAudioItem = menu.findItem(0); 453 | backgroundAudioItem.setCheckable(true); 454 | backgroundAudioItem.setChecked(enableBackgroundAudio); 455 | OnMenuItemClickListener clickListener = new OnMenuItemClickListener() { 456 | @Override 457 | public boolean onMenuItemClick(MenuItem item) { 458 | if (item == backgroundAudioItem) { 459 | enableBackgroundAudio = !item.isChecked(); 460 | return true; 461 | } 462 | return false; 463 | } 464 | }; 465 | configurePopupWithTracks(popup, clickListener, DemoPlayer.TYPE_AUDIO); 466 | popup.show(); 467 | } 468 | public void showTextPopup(View v) { 469 | PopupMenu popup = new PopupMenu(this, v); 470 | configurePopupWithTracks(popup, null, DemoPlayer.TYPE_TEXT); 471 | popup.show(); 472 | } 473 | 474 | public void showVerboseLogPopup(View v) { 475 | PopupMenu popup = new PopupMenu(this, v); 476 | Menu menu = popup.getMenu(); 477 | menu.add(Menu.NONE, 0, Menu.NONE, R.string.logging_normal); 478 | menu.add(Menu.NONE, 1, Menu.NONE, R.string.logging_verbose); 479 | menu.setGroupCheckable(Menu.NONE, true, true); 480 | menu.findItem((VerboseLogUtil.areAllTagsEnabled()) ? 1 : 0).setChecked(true); 481 | popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { 482 | @Override 483 | public boolean onMenuItemClick(MenuItem item) { 484 | if (item.getItemId() == 0) { 485 | VerboseLogUtil.setEnableAllTags(false); 486 | } else { 487 | VerboseLogUtil.setEnableAllTags(true); 488 | } 489 | return true; 490 | } 491 | }); 492 | popup.show(); 493 | } 494 | 495 | private void configurePopupWithTracks(PopupMenu popup, 496 | final OnMenuItemClickListener customActionClickListener, 497 | final int trackType) { 498 | if (player == null) { 499 | return; 500 | } 501 | int trackCount = player.getTrackCount(trackType); 502 | if (trackCount == 0) { 503 | return; 504 | } 505 | popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { 506 | @Override 507 | public boolean onMenuItemClick(MenuItem item) { 508 | return (customActionClickListener != null 509 | && customActionClickListener.onMenuItemClick(item)) 510 | || onTrackItemClick(item, trackType); 511 | } 512 | }); 513 | Menu menu = popup.getMenu(); 514 | // ID_OFFSET ensures we avoid clashing with Menu.NONE (which equals 0) 515 | menu.add(MENU_GROUP_TRACKS, DemoPlayer.TRACK_DISABLED + ID_OFFSET, Menu.NONE, R.string.off); 516 | for (int i = 0; i < trackCount; i++) { 517 | menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE, 518 | buildTrackName(player.getTrackFormat(trackType, i))); 519 | } 520 | menu.setGroupCheckable(MENU_GROUP_TRACKS, true, true); 521 | menu.findItem(player.getSelectedTrack(trackType) + ID_OFFSET).setChecked(true); 522 | } 523 | 524 | private boolean onTrackItemClick(MenuItem item, int type) { 525 | if (player == null || item.getGroupId() != MENU_GROUP_TRACKS) { 526 | return false; 527 | } 528 | player.setSelectedTrack(type, item.getItemId() - ID_OFFSET); 529 | return true; 530 | } 531 | 532 | // DemoPlayer.CaptionListener implementation 533 | 534 | private void toggleControlsVisibility() { 535 | if (mediaController.isShowing()) { 536 | mediaController.hide(); 537 | debugRootView.setVisibility(View.GONE); 538 | } else { 539 | showControls(); 540 | } 541 | } 542 | 543 | // DemoPlayer.MetadataListener implementation 544 | 545 | private void showControls() { 546 | mediaController.show(0); 547 | debugRootView.setVisibility(View.VISIBLE); 548 | } 549 | 550 | // SurfaceHolder.Callback implementation 551 | 552 | @Override 553 | public void onCues(List cues) { 554 | subtitleLayout.setCues(cues); 555 | } 556 | 557 | @Override 558 | public void onId3Metadata(Map metadata) { 559 | for (Map.Entry entry : metadata.entrySet()) { 560 | if (TxxxMetadata.TYPE.equals(entry.getKey())) { 561 | TxxxMetadata txxxMetadata = (TxxxMetadata) entry.getValue(); 562 | Log.i(TAG, String.format("ID3 TimedMetadata %s: description=%s, value=%s", 563 | TxxxMetadata.TYPE, txxxMetadata.description, txxxMetadata.value)); 564 | } else if (PrivMetadata.TYPE.equals(entry.getKey())) { 565 | PrivMetadata privMetadata = (PrivMetadata) entry.getValue(); 566 | Log.i(TAG, String.format("ID3 TimedMetadata %s: owner=%s", 567 | PrivMetadata.TYPE, privMetadata.owner)); 568 | } else if (GeobMetadata.TYPE.equals(entry.getKey())) { 569 | GeobMetadata geobMetadata = (GeobMetadata) entry.getValue(); 570 | Log.i(TAG, String.format("ID3 TimedMetadata %s: mimeType=%s, filename=%s, description=%s", 571 | GeobMetadata.TYPE, geobMetadata.mimeType, geobMetadata.filename, 572 | geobMetadata.description)); 573 | } else { 574 | Log.i(TAG, String.format("ID3 TimedMetadata %s", entry.getKey())); 575 | } 576 | } 577 | } 578 | 579 | @Override 580 | public void surfaceCreated(SurfaceHolder holder) { 581 | if (player != null) { 582 | player.setSurface(holder.getSurface()); 583 | } 584 | } 585 | 586 | @Override 587 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 588 | // Do nothing. 589 | } 590 | 591 | @Override 592 | public void surfaceDestroyed(SurfaceHolder holder) { 593 | if (player != null) { 594 | player.blockingClearSurface(); 595 | } 596 | } 597 | 598 | private void configureSubtitleView() { 599 | CaptionStyleCompat style; 600 | float fontScale; 601 | if (Util.SDK_INT >= 19) { 602 | style = getUserCaptionStyleV19(); 603 | fontScale = getUserCaptionFontScaleV19(); 604 | } else { 605 | style = CaptionStyleCompat.DEFAULT; 606 | fontScale = 1.0f; 607 | } 608 | subtitleLayout.setStyle(style); 609 | subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale); 610 | } 611 | 612 | @TargetApi(19) 613 | private float getUserCaptionFontScaleV19() { 614 | CaptioningManager captioningManager = 615 | (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); 616 | return captioningManager.getFontScale(); 617 | } 618 | @TargetApi(19) 619 | private CaptionStyleCompat getUserCaptionStyleV19() { 620 | CaptioningManager captioningManager = 621 | (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); 622 | return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); 623 | } 624 | 625 | } 626 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/SampleChooserActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer; 17 | 18 | 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.net.Uri; 23 | import android.os.Bundle; 24 | import android.util.Log; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | import android.widget.AdapterView; 29 | import android.widget.AdapterView.OnItemClickListener; 30 | import android.widget.ArrayAdapter; 31 | import android.widget.ListView; 32 | import android.widget.TextView; 33 | 34 | import com.google.android.exoplayer.MediaCodecUtil; 35 | import com.google.android.exoplayer.util.MimeTypes; 36 | 37 | /** 38 | * An activity for selecting from a number of samples. 39 | */ 40 | public class SampleChooserActivity extends Activity { 41 | 42 | private static final String TAG = "SampleChooserActivity"; 43 | 44 | @Override 45 | public void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | setContentView(R.layout.sample_chooser_activity); 48 | 49 | ListView sampleList = (ListView) findViewById(R.id.sample_list); 50 | final SampleAdapter sampleAdapter = new SampleAdapter(this); 51 | 52 | sampleAdapter.add(new Header("YouTube DASH")); 53 | sampleAdapter.addAll((Object[]) Samples.YOUTUBE_DASH_MP4); 54 | sampleAdapter.add(new Header("Widevine GTS DASH")); 55 | sampleAdapter.addAll((Object[]) Samples.WIDEVINE_GTS); 56 | sampleAdapter.add(new Header("SmoothStreaming")); 57 | sampleAdapter.addAll((Object[]) Samples.SMOOTHSTREAMING); 58 | sampleAdapter.add(new Header("HLS")); 59 | sampleAdapter.addAll((Object[]) Samples.HLS); 60 | sampleAdapter.add(new Header("Misc")); 61 | sampleAdapter.addAll((Object[]) Samples.MISC); 62 | 63 | // Add WebM samples if the device has a VP9 decoder. 64 | try { 65 | if (MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_VP9, false) != null) { 66 | sampleAdapter.add(new Header("YouTube WebM DASH (Experimental)")); 67 | sampleAdapter.addAll((Object[]) Samples.YOUTUBE_DASH_WEBM); 68 | } 69 | } catch (MediaCodecUtil.DecoderQueryException e) { 70 | Log.e(TAG, "Failed to query vp9 decoder", e); 71 | } 72 | 73 | sampleList.setAdapter(sampleAdapter); 74 | sampleList.setOnItemClickListener(new OnItemClickListener() { 75 | @Override 76 | public void onItemClick(AdapterView parent, View view, int position, long id) { 77 | Object item = sampleAdapter.getItem(position); 78 | if (item instanceof Samples.Sample) { 79 | onSampleSelected((Samples.Sample) item); 80 | } 81 | } 82 | }); 83 | } 84 | 85 | private void onSampleSelected(Samples.Sample sample) { 86 | Intent mpdIntent = new Intent(this, TestPlayerActivity.class) 87 | .setData(Uri.parse(sample.uri)) 88 | .putExtra(PlayerActivity.CONTENT_ID_EXTRA, sample.contentId) 89 | .putExtra(PlayerActivity.CONTENT_TYPE_EXTRA, sample.type); 90 | startActivity(mpdIntent); 91 | } 92 | 93 | private static class SampleAdapter extends ArrayAdapter { 94 | 95 | public SampleAdapter(Context context) { 96 | super(context, 0); 97 | } 98 | 99 | @Override 100 | public View getView(int position, View convertView, ViewGroup parent) { 101 | View view = convertView; 102 | if (view == null) { 103 | int layoutId = getItemViewType(position) == 1 ? android.R.layout.simple_list_item_1 104 | : R.layout.sample_chooser_inline_header; 105 | view = LayoutInflater.from(getContext()).inflate(layoutId, null, false); 106 | } 107 | Object item = getItem(position); 108 | String name = null; 109 | if (item instanceof Samples.Sample) { 110 | name = ((Samples.Sample) item).name; 111 | } else if (item instanceof Header) { 112 | name = ((Header) item).name; 113 | } 114 | ((TextView) view).setText(name); 115 | return view; 116 | } 117 | 118 | @Override 119 | public int getItemViewType(int position) { 120 | return (getItem(position) instanceof Samples.Sample) ? 1 : 0; 121 | } 122 | 123 | @Override 124 | public int getViewTypeCount() { 125 | return 2; 126 | } 127 | 128 | } 129 | 130 | private static class Header { 131 | 132 | public final String name; 133 | 134 | public Header(String name) { 135 | this.name = name; 136 | } 137 | 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/Samples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer; 17 | 18 | import java.util.Locale; 19 | 20 | /** 21 | * Holds statically defined sample definitions. 22 | */ 23 | /* package */ class Samples { 24 | 25 | public static final Sample[] YOUTUBE_DASH_MP4 = new Sample[] { 26 | new Sample("Google Glass", 27 | "http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?" 28 | + "as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" 29 | + "ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7." 30 | + "8506521BFC350652163895D4C26DEE124209AA9E&key=ik0", PlayerActivity.TYPE_DASH), 31 | new Sample("Google Play", 32 | "http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?" 33 | + "as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" 34 | + "ipbits=0&expire=19000000000&signature=A2716F75795F5D2AF0E88962FFCD10DB79384F29." 35 | + "84308FF04844498CE6FBCE4731507882B8307798&key=ik0", PlayerActivity.TYPE_DASH), 36 | }; 37 | public static final Sample[] YOUTUBE_DASH_WEBM = new Sample[] { 38 | new Sample("Google Glass", 39 | "http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?" 40 | + "as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" 41 | + "ipbits=0&expire=19000000000&signature=249B04F79E984D7F86B4D8DB48AE6FAF41C17AB3." 42 | + "7B9F0EC0505E1566E59B8E488E9419F253DDF413&key=ik0", PlayerActivity.TYPE_DASH), 43 | new Sample("Google Play", 44 | "http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?" 45 | + "as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" 46 | + "ipbits=0&expire=19000000000&signature=B1C2A74783AC1CC4865EB312D7DD2D48230CC9FD." 47 | + "BD153B9882175F1F94BFE5141A5482313EA38E8D&key=ik0", PlayerActivity.TYPE_DASH), 48 | }; 49 | public static final Sample[] SMOOTHSTREAMING = new Sample[] { 50 | new Sample("Super speed", 51 | "http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism", 52 | PlayerActivity.TYPE_SS), 53 | new Sample("Super speed (PlayReady)", 54 | "http://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism", 55 | PlayerActivity.TYPE_SS), 56 | }; 57 | public static final Sample[] WIDEVINE_GTS = new Sample[] { 58 | new Sample("WV: HDCP not specified", "d286538032258a1c", 59 | "http://www.youtube.com/api/manifest/dash/id/d286538032258a1c/source/youtube?" 60 | + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" 61 | + "&ipbits=0&expire=19000000000&signature=477CF7D478BE26C205045D507E9358F85F84C065." 62 | + "8971631EB657BC33EC2F48A2FF4211956760C3E9&key=ik0", PlayerActivity.TYPE_DASH), 63 | new Sample("WV: HDCP not required", "48fcc369939ac96c", 64 | "http://www.youtube.com/api/manifest/dash/id/48fcc369939ac96c/source/youtube?" 65 | + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" 66 | + "&ipbits=0&expire=19000000000&signature=171DAE48D00B5BE7434BC1A9F84DAE0463C7EA7A." 67 | + "0925B4DBB5605BEE9F5D088C48F25F5108E96191&key=ik0", PlayerActivity.TYPE_DASH), 68 | new Sample("WV: HDCP required", "e06c39f1151da3df", 69 | "http://www.youtube.com/api/manifest/dash/id/e06c39f1151da3df/source/youtube?" 70 | + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" 71 | + "&ipbits=0&expire=19000000000&signature=8D3B8AF4E3F72B7F127C8D0D39B7AFCF37B30519." 72 | + "A118BADEBF3582AD2CC257B0EE6E579C6955D8AA&key=ik0", PlayerActivity.TYPE_DASH), 73 | new Sample("WV: Secure video path required", "0894c7c8719b28a0", 74 | "http://www.youtube.com/api/manifest/dash/id/0894c7c8719b28a0/source/youtube?" 75 | + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" 76 | + "&ipbits=0&expire=19000000000&signature=A41D835C7387885A4A820628F57E481E00095931." 77 | + "9D50DBEEB5E37344647EE11BDA129A7FCDE8B7B9&key=ik0", PlayerActivity.TYPE_DASH), 78 | new Sample("WV: HDCP + secure video path required", "efd045b1eb61888a", 79 | "http://www.youtube.com/api/manifest/dash/id/efd045b1eb61888a/source/youtube?" 80 | + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" 81 | + "&ipbits=0&expire=19000000000&signature=A97C9032C9D0C74F1643DB17C178873887C229E4." 82 | + "0A657BF6F23C8BC1538F276137383478330B76DE&key=ik0", PlayerActivity.TYPE_DASH), 83 | new Sample("WV: 30s license duration (fails at ~30s)", "f9a34cab7b05881a", 84 | "http://www.youtube.com/api/manifest/dash/id/f9a34cab7b05881a/source/youtube?" 85 | + "as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0" 86 | + "&ipbits=0&expire=19000000000&signature=80648A12A7D5FC1FA02B52B4250E4EB74CF0C5FD." 87 | + "66A261130CA137AA5C541EA9CED2DBF240829EE6&key=ik0", PlayerActivity.TYPE_DASH), 88 | }; 89 | public static final Sample[] HLS = new Sample[] { 90 | new Sample("Apple master playlist", 91 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/" 92 | + "bipbop_4x3_variant.m3u8", PlayerActivity.TYPE_HLS), 93 | new Sample("Apple master playlist advanced", 94 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/" 95 | + "bipbop_16x9_variant.m3u8", PlayerActivity.TYPE_HLS), 96 | new Sample("Apple TS media playlist", 97 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/" 98 | + "prog_index.m3u8", PlayerActivity.TYPE_HLS), 99 | new Sample("Apple AAC media playlist", 100 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear0/" 101 | + "prog_index.m3u8", PlayerActivity.TYPE_HLS), 102 | new Sample("Apple ID3 metadata", "http://devimages.apple.com/samplecode/adDemo/ad.m3u8", 103 | PlayerActivity.TYPE_HLS), 104 | }; 105 | public static final Sample[] MISC = new Sample[] { 106 | // new Sample("Dizzy", "http://html5demos.com/assets/dizzy.mp4", PlayerActivity.TYPE_OTHER), 107 | new Sample("Dizzy", "http://e-blusukan.velotek.co.id/uploads/post/296DuWgnFWbhr06pyZO5oxsP.mp4", PlayerActivity.TYPE_OTHER), 108 | new Sample("Apple AAC 10s", "https://devimages.apple.com.edgekey.net/" 109 | + "streaming/examples/bipbop_4x3/gear0/fileSequence0.aac", PlayerActivity.TYPE_OTHER), 110 | new Sample("Apple TS 10s", "https://devimages.apple.com.edgekey.net/streaming/examples/" 111 | + "bipbop_4x3/gear1/fileSequence0.ts", PlayerActivity.TYPE_OTHER), 112 | new Sample("Android screens (Matroska)", "http://storage.googleapis.com/exoplayer-test-media-1/" 113 | + "mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv", 114 | PlayerActivity.TYPE_OTHER), 115 | new Sample("Big Buck Bunny (MP4 Video)", 116 | "http://redirector.c.youtube.com/videoplayback?id=604ed5ce52eda7ee&itag=22&source=youtube&" 117 | + "sparams=ip,ipbits,expire,source,id&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=" 118 | + "513F28C7FDCBEC60A66C86C9A393556C99DC47FB.04C88036EEE12565A1ED864A875A58F15D8B5300" 119 | + "&key=ik0", PlayerActivity.TYPE_OTHER), 120 | new Sample("Google Play (MP3 Audio)", 121 | "http://storage.googleapis.com/exoplayer-test-media-0/play.mp3", PlayerActivity.TYPE_OTHER), 122 | new Sample("Google Glass (WebM Video with Vorbis Audio)", 123 | "http://demos.webmproject.org/exoplayer/glass_vp9_vorbis.webm", PlayerActivity.TYPE_OTHER), 124 | }; 125 | 126 | private Samples() {} 127 | 128 | public static class Sample { 129 | 130 | public final String name; 131 | public final String contentId; 132 | public final String uri; 133 | public final int type; 134 | 135 | public Sample(String name, String uri, int type) { 136 | this(name, name.toLowerCase(Locale.US).replaceAll("\\s", ""), uri, type); 137 | } 138 | 139 | public Sample(String name, String contentId, String uri, int type) { 140 | this.name = name; 141 | this.contentId = contentId; 142 | this.uri = uri; 143 | this.type = type; 144 | } 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/SmoothStreamingTestMediaDrmCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer; 17 | 18 | import android.annotation.TargetApi; 19 | import android.media.MediaDrm.KeyRequest; 20 | import android.media.MediaDrm.ProvisionRequest; 21 | import android.text.TextUtils; 22 | 23 | import com.google.android.exoplayer.drm.MediaDrmCallback; 24 | import com.google.android.exoplayer.util.Util; 25 | 26 | import java.io.IOException; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | import java.util.UUID; 30 | 31 | /** 32 | * Demo {@link com.google.android.exoplayer.drm.StreamingDrmSessionManager} for smooth streaming test content. 33 | */ 34 | @TargetApi(18) 35 | public class SmoothStreamingTestMediaDrmCallback implements MediaDrmCallback { 36 | 37 | private static final String PLAYREADY_TEST_DEFAULT_URI = 38 | "http://playready.directtaps.net/pr/svc/rightsmanager.asmx"; 39 | private static final Map KEY_REQUEST_PROPERTIES; 40 | static { 41 | HashMap keyRequestProperties = new HashMap<>(); 42 | keyRequestProperties.put("Content-Type", "text/xml"); 43 | keyRequestProperties.put("SOAPAction", 44 | "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense"); 45 | KEY_REQUEST_PROPERTIES = keyRequestProperties; 46 | } 47 | 48 | @Override 49 | public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException { 50 | String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); 51 | return Util.executePost(url, null, null); 52 | } 53 | 54 | @Override 55 | public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception { 56 | String url = request.getDefaultUrl(); 57 | if (TextUtils.isEmpty(url)) { 58 | url = PLAYREADY_TEST_DEFAULT_URI; 59 | } 60 | return Util.executePost(url, request.getData(), KEY_REQUEST_PROPERTIES); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/TestPlayerActivity.java: -------------------------------------------------------------------------------- 1 | package gujarat.videoplayaer; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.KeyEvent; 7 | import android.view.MotionEvent; 8 | import android.view.SurfaceView; 9 | import android.view.View; 10 | import android.widget.Toast; 11 | 12 | import com.google.android.exoplayer.AspectRatioFrameLayout; 13 | import com.google.android.exoplayer.text.SubtitleLayout; 14 | 15 | import java.net.CookieHandler; 16 | import java.net.CookieManager; 17 | import java.net.CookiePolicy; 18 | 19 | import gujarat.videoplayaer.controller.TestPlayerController; 20 | 21 | /** 22 | * Created by Gujarat Santana on 12/11/15. 23 | */ 24 | public class TestPlayerActivity extends AppCompatActivity { 25 | 26 | /** 27 | * 28 | * Cooki handler 29 | */ 30 | private static final CookieManager defaultCookieManager; 31 | 32 | /////////////////////////////////////// 33 | 34 | static { 35 | defaultCookieManager = new CookieManager(); 36 | defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); 37 | } 38 | 39 | private String urlVideo ="http://e-blusukan.velotek.co.id/uploads/post/296DuWgnFWbhr06pyZO5oxsP.mp4"; 40 | /** 41 | * variable lib and engine 42 | */ 43 | // private MediaController mediaController; 44 | private SurfaceView surfaceView; 45 | 46 | 47 | 48 | ////////////////////////////////////// 49 | private AspectRatioFrameLayout videoFrame; 50 | private SubtitleLayout subtitleLayout; 51 | /** 52 | * variable UI / layout 53 | */ 54 | private View debugRootView; 55 | 56 | ///////////////////////////////////// 57 | private View shutterView; 58 | private View root; 59 | 60 | @Override 61 | protected void onCreate(Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | setContentView(R.layout.test_player); 64 | 65 | initUI(); 66 | initAction(); 67 | } 68 | 69 | private void initUI(){ 70 | //init ui variable 71 | root = findViewById(R.id.root); 72 | 73 | 74 | shutterView = findViewById(R.id.shutter); 75 | debugRootView = findViewById(R.id.controls_root); 76 | 77 | videoFrame = (AspectRatioFrameLayout) findViewById(R.id.video_frame); 78 | surfaceView = (SurfaceView) findViewById(R.id.surface_view); 79 | subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles); 80 | 81 | CookieHandler currentHandler = CookieHandler.getDefault(); 82 | if (currentHandler != defaultCookieManager) { 83 | CookieHandler.setDefault(defaultCookieManager); 84 | } 85 | } 86 | 87 | private void initAction(){ 88 | //setup Video Engine 89 | TestPlayerController.getInstance().setEngineVideo(TestPlayerActivity.this,root,shutterView,surfaceView,videoFrame,subtitleLayout); 90 | //set root onTouchListener 91 | root.setOnTouchListener(new View.OnTouchListener() { 92 | @Override 93 | public boolean onTouch(View view, MotionEvent motionEvent) { 94 | if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { 95 | TestPlayerController.getInstance().toggleControlsVisibility(); 96 | Toast.makeText(TestPlayerActivity.this, "ControlVisible", Toast.LENGTH_SHORT).show(); 97 | } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { 98 | view.performClick(); 99 | } 100 | return true; 101 | } 102 | }); 103 | root.setOnKeyListener(new View.OnKeyListener() { 104 | @Override 105 | public boolean onKey(View v, int keyCode, KeyEvent event) { 106 | if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE 107 | || keyCode == KeyEvent.KEYCODE_MENU) { 108 | return false; 109 | } 110 | return TestPlayerController.getInstance().getMediaController().dispatchKeyEvent(event); 111 | } 112 | }); 113 | } 114 | 115 | @Override 116 | protected void onResume() { 117 | super.onResume(); 118 | TestPlayerController.getInstance().setOnResumeVideo(TestPlayerActivity.this,urlVideo); 119 | } 120 | 121 | @Override 122 | protected void onPause() { 123 | super.onPause(); 124 | TestPlayerController.getInstance().setOnPauseVideo(shutterView); 125 | } 126 | 127 | @Override 128 | protected void onDestroy() { 129 | super.onDestroy(); 130 | TestPlayerController.getInstance().setOnDestroyVideo(); 131 | } 132 | 133 | @Override 134 | protected void onNewIntent(Intent intent) { 135 | super.onNewIntent(intent); 136 | TestPlayerController.getInstance().onNewIntent(this,intent); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/WidevineTestMediaDrmCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer; 17 | 18 | import android.annotation.TargetApi; 19 | import android.media.MediaDrm.KeyRequest; 20 | import android.media.MediaDrm.ProvisionRequest; 21 | import android.text.TextUtils; 22 | 23 | import com.google.android.exoplayer.drm.MediaDrmCallback; 24 | import com.google.android.exoplayer.util.Util; 25 | 26 | import java.io.IOException; 27 | import java.util.UUID; 28 | 29 | /** 30 | * A {@link com.google.android.exoplayer.drm.MediaDrmCallback} for Widevine test content. 31 | */ 32 | @TargetApi(18) 33 | public class WidevineTestMediaDrmCallback implements MediaDrmCallback { 34 | 35 | private static final String WIDEVINE_GTS_DEFAULT_BASE_URI = 36 | "http://wv-staging-proxy.appspot.com/proxy?provider=YouTube&video_id="; 37 | 38 | private final String defaultUri; 39 | 40 | public WidevineTestMediaDrmCallback(String videoId) { 41 | defaultUri = WIDEVINE_GTS_DEFAULT_BASE_URI + videoId; 42 | } 43 | 44 | @Override 45 | public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException { 46 | String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); 47 | return Util.executePost(url, null, null); 48 | } 49 | 50 | @Override 51 | public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws IOException { 52 | String url = request.getDefaultUrl(); 53 | if (TextUtils.isEmpty(url)) { 54 | url = defaultUri; 55 | } 56 | return Util.executePost(url, request.getData(), null); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/controller/TestPlayerController.java: -------------------------------------------------------------------------------- 1 | package gujarat.videoplayaer.controller; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.view.SurfaceHolder; 9 | import android.view.SurfaceView; 10 | import android.view.View; 11 | import android.view.accessibility.CaptioningManager; 12 | import android.widget.MediaController; 13 | import android.widget.Toast; 14 | 15 | import com.google.android.exoplayer.AspectRatioFrameLayout; 16 | import com.google.android.exoplayer.ExoPlayer; 17 | import com.google.android.exoplayer.audio.AudioCapabilities; 18 | import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver; 19 | import com.google.android.exoplayer.drm.UnsupportedDrmException; 20 | import com.google.android.exoplayer.text.CaptionStyleCompat; 21 | import com.google.android.exoplayer.text.Cue; 22 | import com.google.android.exoplayer.text.SubtitleLayout; 23 | import com.google.android.exoplayer.util.Util; 24 | 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | import gujarat.videoplayaer.R; 29 | import gujarat.videoplayaer.player.DemoPlayer; 30 | import gujarat.videoplayaer.player.ExtractorRendererBuilder; 31 | 32 | /** 33 | * Created by Gujarat Santana on 12/11/15. 34 | */ 35 | public class TestPlayerController implements DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener{ 36 | 37 | public static TestPlayerController instance; 38 | private MediaController mediaController; 39 | private SurfaceView surfaceView; 40 | private DemoPlayer player; 41 | private AspectRatioFrameLayout videoFrame; 42 | private SubtitleLayout subtitleLayout; 43 | private Uri contentUri; 44 | private String urlVideo; 45 | private boolean playerNeedsPrepare; 46 | private long playerPosition; 47 | private boolean enableBackgroundAudio; 48 | private Context context; 49 | private View shutterView; 50 | private AudioCapabilitiesReceiver audioCapabilitiesReceiver; 51 | 52 | public TestPlayerController(){ 53 | } 54 | 55 | public static TestPlayerController getInstance(){ 56 | if(instance==null) 57 | instance = new TestPlayerController(); 58 | return instance; 59 | } 60 | 61 | public void setEngineVideo(final Context context,View root, View shutterView,SurfaceView surfaceView,AspectRatioFrameLayout videoFrame,SubtitleLayout subtitleLayout){ 62 | //ini variable 63 | this.context =context; 64 | this.videoFrame = videoFrame; 65 | this.subtitleLayout = subtitleLayout; 66 | this.surfaceView = surfaceView; 67 | this.shutterView = shutterView; 68 | 69 | // init engine video 70 | surfaceView.getHolder().addCallback(new SurfaceHolder.Callback(){ 71 | 72 | @Override 73 | public void surfaceCreated(SurfaceHolder surfaceHolder) { 74 | if (player != null) { 75 | player.setSurface(surfaceHolder.getSurface()); 76 | } 77 | } 78 | 79 | @Override 80 | public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { 81 | 82 | } 83 | 84 | @Override 85 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 86 | if (player != null) { 87 | player.blockingClearSurface(); 88 | } 89 | } 90 | }); 91 | mediaController = new MediaController(context); 92 | mediaController.setAnchorView(root); 93 | audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(context, new AudioCapabilitiesReceiver.Listener(){ 94 | 95 | @Override 96 | public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) { 97 | if (player == null) { 98 | return; 99 | } 100 | boolean backgrounded = player.getBackgrounded(); 101 | boolean playWhenReady = player.getPlayWhenReady(); 102 | releasePlayer(); 103 | preparePlayer(context,playWhenReady); 104 | player.setBackgrounded(backgrounded); 105 | } 106 | }); 107 | audioCapabilitiesReceiver.register(); 108 | 109 | } 110 | 111 | 112 | public MediaController getMediaController(){ 113 | return mediaController; 114 | } 115 | 116 | public void setOnResumeVideo(Context context,String urlVideo){ 117 | contentUri = Uri.parse(urlVideo); 118 | this.urlVideo = urlVideo; 119 | // contentType = intent.getIntExtra(CONTENT_TYPE_EXTRA, 120 | // inferContentType(contentUri, intent.getStringExtra(CONTENT_EXT_EXTRA))); 121 | // contentId = intent.getStringExtra(CONTENT_ID_EXTRA); 122 | configureSubtitleView(context); 123 | if (player == null) { 124 | preparePlayer(context,true); 125 | } else { 126 | player.setBackgrounded(false); 127 | } 128 | } 129 | 130 | public void setOnPauseVideo(View shutterView){ 131 | if (!enableBackgroundAudio) { 132 | releasePlayer(); 133 | } else { 134 | player.setBackgrounded(true); 135 | } 136 | 137 | shutterView.setVisibility(View.VISIBLE); 138 | } 139 | 140 | public void setOnDestroyVideo(){ 141 | audioCapabilitiesReceiver.unregister(); 142 | releasePlayer(); 143 | } 144 | 145 | private void releasePlayer() { 146 | if (player != null) { 147 | // debugViewHelper.stop(); 148 | // debugViewHelper = null; 149 | playerPosition = player.getCurrentPosition(); 150 | player.release(); 151 | player = null; 152 | // eventLogger.endSession(); 153 | // eventLogger = null; 154 | } 155 | } 156 | 157 | private void configureSubtitleView(Context context) { 158 | CaptionStyleCompat style; 159 | float fontScale; 160 | if (Util.SDK_INT >= 19) { 161 | style = getUserCaptionStyleV19(context); 162 | fontScale = getUserCaptionFontScaleV19(context); 163 | } else { 164 | style = CaptionStyleCompat.DEFAULT; 165 | fontScale = 1.0f; 166 | } 167 | subtitleLayout.setStyle(style); 168 | subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale); 169 | } 170 | 171 | public void onNewIntent(Activity activity,Intent intent){ 172 | releasePlayer(); 173 | playerPosition = 0; 174 | activity.setIntent(intent); 175 | } 176 | 177 | private void preparePlayer(Context context,boolean playWhenReady) { 178 | if (player == null) { 179 | player = new DemoPlayer(getRendererBuilder(context)); 180 | player.addListener(this); 181 | player.setCaptionListener(this); 182 | player.setMetadataListener(this); 183 | player.seekTo(playerPosition); 184 | playerNeedsPrepare = true; 185 | mediaController.setMediaPlayer(player.getPlayerControl()); 186 | mediaController.setEnabled(true); 187 | // eventLogger = new EventLogger(); 188 | // eventLogger.startSession(); 189 | // player.addListener(eventLogger); 190 | // player.setInfoListener(eventLogger); 191 | // player.setInternalErrorListener(eventLogger); 192 | // debugViewHelper = new DebugTextViewHelper(player, debugTextView); 193 | // debugViewHelper.start(); 194 | } 195 | if (playerNeedsPrepare) { 196 | player.prepare(); 197 | playerNeedsPrepare = false; 198 | // updateButtonVisibilities(); 199 | } 200 | player.setSurface(surfaceView.getHolder().getSurface()); 201 | player.setPlayWhenReady(playWhenReady); 202 | } 203 | 204 | private void showControls() { 205 | mediaController.show(0); 206 | // debugRootView.setVisibility(View.VISIBLE); 207 | } 208 | 209 | 210 | @TargetApi(19) 211 | private float getUserCaptionFontScaleV19(Context context) { 212 | CaptioningManager captioningManager = 213 | (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); 214 | return captioningManager.getFontScale(); 215 | } 216 | 217 | @TargetApi(19) 218 | private CaptionStyleCompat getUserCaptionStyleV19(Context context) { 219 | CaptioningManager captioningManager = 220 | (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); 221 | return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); 222 | } 223 | 224 | private DemoPlayer.RendererBuilder getRendererBuilder(Context context) { 225 | String userAgent = Util.getUserAgent(context, "ExoPlayerDemo"); 226 | return new ExtractorRendererBuilder(context, userAgent, contentUri); 227 | // return new SmoothStreamingRendererBuilder(context, userAgent, contentUri.toString(),new SmoothStreamingTestMediaDrmCallback()); 228 | 229 | } 230 | 231 | public void toggleControlsVisibility() { 232 | if (mediaController.isShowing()) { 233 | mediaController.hide(); 234 | } else { 235 | showControls(); 236 | } 237 | } 238 | 239 | @Override 240 | public void onCues(List cues) { 241 | subtitleLayout.setCues(cues); 242 | } 243 | 244 | @Override 245 | public void onId3Metadata(Map metadata) { 246 | 247 | } 248 | 249 | @Override 250 | public void onStateChanged(boolean playWhenReady, int playbackState) { 251 | if (playbackState == ExoPlayer.STATE_ENDED) { 252 | showControls(); 253 | } 254 | } 255 | 256 | @Override 257 | public void onError(Exception e) { 258 | if (e instanceof UnsupportedDrmException) { 259 | // Special case DRM failures. 260 | UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e; 261 | int stringId = Util.SDK_INT < 18 ? R.string.drm_error_not_supported 262 | : unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME 263 | ? R.string.drm_error_unsupported_scheme : R.string.drm_error_unknown; 264 | Toast.makeText(context, stringId, Toast.LENGTH_LONG).show(); 265 | } 266 | playerNeedsPrepare = true; 267 | showControls(); 268 | } 269 | 270 | @Override 271 | public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { 272 | shutterView.setVisibility(View.GONE); 273 | videoFrame.setAspectRatio( 274 | height == 0 ? 1 : (width * pixelWidthHeightRatio) / height); 275 | } 276 | 277 | 278 | // @Override 279 | // public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) { 280 | // if (player == null) { 281 | // return; 282 | // } 283 | // boolean backgrounded = player.getBackgrounded(); 284 | // boolean playWhenReady = player.getPlayWhenReady(); 285 | // releasePlayer(); 286 | // preparePlayer(context,playWhenReady); 287 | // player.setBackgrounded(backgrounded); 288 | // } 289 | } 290 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/player/DashRendererBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer.player; 17 | 18 | import android.content.Context; 19 | import android.media.MediaCodec; 20 | import android.os.Handler; 21 | import android.util.Log; 22 | 23 | import com.google.android.exoplayer.DefaultLoadControl; 24 | import com.google.android.exoplayer.LoadControl; 25 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 26 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 27 | import com.google.android.exoplayer.TrackRenderer; 28 | import com.google.android.exoplayer.audio.AudioCapabilities; 29 | import com.google.android.exoplayer.chunk.ChunkSampleSource; 30 | import com.google.android.exoplayer.chunk.ChunkSource; 31 | import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; 32 | import com.google.android.exoplayer.dash.DashChunkSource; 33 | import com.google.android.exoplayer.dash.DefaultDashTrackSelector; 34 | import com.google.android.exoplayer.dash.mpd.AdaptationSet; 35 | import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription; 36 | import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser; 37 | import com.google.android.exoplayer.dash.mpd.Period; 38 | import com.google.android.exoplayer.dash.mpd.UtcTimingElement; 39 | import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver; 40 | import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver.UtcTimingCallback; 41 | import com.google.android.exoplayer.drm.MediaDrmCallback; 42 | import com.google.android.exoplayer.drm.StreamingDrmSessionManager; 43 | import com.google.android.exoplayer.drm.UnsupportedDrmException; 44 | import com.google.android.exoplayer.text.TextTrackRenderer; 45 | import com.google.android.exoplayer.upstream.DataSource; 46 | import com.google.android.exoplayer.upstream.DefaultAllocator; 47 | import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; 48 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 49 | import com.google.android.exoplayer.upstream.UriDataSource; 50 | import com.google.android.exoplayer.util.ManifestFetcher; 51 | import com.google.android.exoplayer.util.Util; 52 | 53 | import java.io.IOException; 54 | 55 | /** 56 | * A {@link RendererBuilder} for DASH. 57 | */ 58 | public class DashRendererBuilder implements DemoPlayer.RendererBuilder { 59 | 60 | private static final String TAG = "DashRendererBuilder"; 61 | 62 | private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; 63 | private static final int VIDEO_BUFFER_SEGMENTS = 200; 64 | private static final int AUDIO_BUFFER_SEGMENTS = 54; 65 | private static final int TEXT_BUFFER_SEGMENTS = 2; 66 | private static final int LIVE_EDGE_LATENCY_MS = 30000; 67 | 68 | private static final int SECURITY_LEVEL_UNKNOWN = -1; 69 | private static final int SECURITY_LEVEL_1 = 1; 70 | private static final int SECURITY_LEVEL_3 = 3; 71 | 72 | private final Context context; 73 | private final String userAgent; 74 | private final String url; 75 | private final MediaDrmCallback drmCallback; 76 | 77 | private AsyncRendererBuilder currentAsyncBuilder; 78 | 79 | public DashRendererBuilder(Context context, String userAgent, String url, 80 | MediaDrmCallback drmCallback) { 81 | this.context = context; 82 | this.userAgent = userAgent; 83 | this.url = url; 84 | this.drmCallback = drmCallback; 85 | } 86 | 87 | @Override 88 | public void buildRenderers(DemoPlayer player) { 89 | currentAsyncBuilder = new AsyncRendererBuilder(context, userAgent, url, drmCallback, player); 90 | currentAsyncBuilder.init(); 91 | } 92 | 93 | @Override 94 | public void cancel() { 95 | if (currentAsyncBuilder != null) { 96 | currentAsyncBuilder.cancel(); 97 | currentAsyncBuilder = null; 98 | } 99 | } 100 | 101 | private static final class AsyncRendererBuilder 102 | implements ManifestFetcher.ManifestCallback, UtcTimingCallback { 103 | 104 | private final Context context; 105 | private final String userAgent; 106 | private final MediaDrmCallback drmCallback; 107 | private final DemoPlayer player; 108 | private final ManifestFetcher manifestFetcher; 109 | private final UriDataSource manifestDataSource; 110 | 111 | private boolean canceled; 112 | private MediaPresentationDescription manifest; 113 | private long elapsedRealtimeOffset; 114 | 115 | public AsyncRendererBuilder(Context context, String userAgent, String url, 116 | MediaDrmCallback drmCallback, DemoPlayer player) { 117 | this.context = context; 118 | this.userAgent = userAgent; 119 | this.drmCallback = drmCallback; 120 | this.player = player; 121 | MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser(); 122 | manifestDataSource = new DefaultUriDataSource(context, userAgent); 123 | manifestFetcher = new ManifestFetcher<>(url, manifestDataSource, parser); 124 | } 125 | 126 | private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) { 127 | String securityLevelProperty = sessionManager.getPropertyString("securityLevel"); 128 | return securityLevelProperty.equals("L1") ? SECURITY_LEVEL_1 : securityLevelProperty 129 | .equals("L3") ? SECURITY_LEVEL_3 : SECURITY_LEVEL_UNKNOWN; 130 | } 131 | 132 | public void init() { 133 | manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this); 134 | } 135 | 136 | public void cancel() { 137 | canceled = true; 138 | } 139 | 140 | @Override 141 | public void onSingleManifest(MediaPresentationDescription manifest) { 142 | if (canceled) { 143 | return; 144 | } 145 | 146 | this.manifest = manifest; 147 | if (manifest.dynamic && manifest.utcTiming != null) { 148 | UtcTimingElementResolver.resolveTimingElement(manifestDataSource, manifest.utcTiming, 149 | manifestFetcher.getManifestLoadCompleteTimestamp(), this); 150 | } else { 151 | buildRenderers(); 152 | } 153 | } 154 | 155 | @Override 156 | public void onSingleManifestError(IOException e) { 157 | if (canceled) { 158 | return; 159 | } 160 | 161 | player.onRenderersError(e); 162 | } 163 | 164 | @Override 165 | public void onTimestampResolved(UtcTimingElement utcTiming, long elapsedRealtimeOffset) { 166 | if (canceled) { 167 | return; 168 | } 169 | 170 | this.elapsedRealtimeOffset = elapsedRealtimeOffset; 171 | buildRenderers(); 172 | } 173 | 174 | @Override 175 | public void onTimestampError(UtcTimingElement utcTiming, IOException e) { 176 | if (canceled) { 177 | return; 178 | } 179 | 180 | Log.e(TAG, "Failed to resolve UtcTiming element [" + utcTiming + "]", e); 181 | // Be optimistic and continue in the hope that the device clock is correct. 182 | buildRenderers(); 183 | } 184 | 185 | private void buildRenderers() { 186 | Period period = manifest.getPeriod(0); 187 | Handler mainHandler = player.getMainHandler(); 188 | LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); 189 | DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player); 190 | 191 | boolean hasContentProtection = false; 192 | for (int i = 0; i < period.adaptationSets.size(); i++) { 193 | AdaptationSet adaptationSet = period.adaptationSets.get(i); 194 | if (adaptationSet.type != AdaptationSet.TYPE_UNKNOWN) { 195 | hasContentProtection |= adaptationSet.hasContentProtection(); 196 | } 197 | } 198 | 199 | // Check drm support if necessary. 200 | boolean filterHdContent = false; 201 | StreamingDrmSessionManager drmSessionManager = null; 202 | if (hasContentProtection) { 203 | if (Util.SDK_INT < 18) { 204 | player.onRenderersError( 205 | new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); 206 | return; 207 | } 208 | try { 209 | drmSessionManager = StreamingDrmSessionManager.newWidevineInstance( 210 | player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); 211 | filterHdContent = getWidevineSecurityLevel(drmSessionManager) != SECURITY_LEVEL_1; 212 | } catch (UnsupportedDrmException e) { 213 | player.onRenderersError(e); 214 | return; 215 | } 216 | } 217 | 218 | // Build the video renderer. 219 | DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 220 | ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, 221 | DefaultDashTrackSelector.newVideoInstance(context, true, filterHdContent), 222 | videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS, 223 | elapsedRealtimeOffset, mainHandler, player); 224 | ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, 225 | VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, 226 | DemoPlayer.TYPE_VIDEO); 227 | TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource, 228 | MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, drmSessionManager, true, 229 | mainHandler, player, 50); 230 | 231 | // Build the audio renderer. 232 | DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 233 | ChunkSource audioChunkSource = new DashChunkSource(manifestFetcher, 234 | DefaultDashTrackSelector.newAudioInstance(), audioDataSource, null, LIVE_EDGE_LATENCY_MS, 235 | elapsedRealtimeOffset, mainHandler, player); 236 | ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, 237 | AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, 238 | DemoPlayer.TYPE_AUDIO); 239 | TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, 240 | drmSessionManager, true, mainHandler, player, AudioCapabilities.getCapabilities(context)); 241 | 242 | // Build the text renderer. 243 | DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 244 | ChunkSource textChunkSource = new DashChunkSource(manifestFetcher, 245 | DefaultDashTrackSelector.newTextInstance(), textDataSource, null, LIVE_EDGE_LATENCY_MS, 246 | elapsedRealtimeOffset, mainHandler, player); 247 | ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, 248 | TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, 249 | DemoPlayer.TYPE_TEXT); 250 | TrackRenderer textRenderer = new TextTrackRenderer(textSampleSource, player, 251 | mainHandler.getLooper()); 252 | 253 | // Invoke the callback. 254 | TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; 255 | renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; 256 | renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; 257 | renderers[DemoPlayer.TYPE_TEXT] = textRenderer; 258 | player.onRenderers(renderers, bandwidthMeter); 259 | } 260 | 261 | } 262 | 263 | } 264 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/player/DemoPlayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer.player; 17 | 18 | import android.media.MediaCodec.CryptoException; 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | import android.view.Surface; 22 | 23 | import com.google.android.exoplayer.CodecCounters; 24 | import com.google.android.exoplayer.DummyTrackRenderer; 25 | import com.google.android.exoplayer.ExoPlaybackException; 26 | import com.google.android.exoplayer.ExoPlayer; 27 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 28 | import com.google.android.exoplayer.MediaCodecTrackRenderer; 29 | import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; 30 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 31 | import com.google.android.exoplayer.MediaFormat; 32 | import com.google.android.exoplayer.TimeRange; 33 | import com.google.android.exoplayer.TrackRenderer; 34 | import com.google.android.exoplayer.audio.AudioTrack; 35 | import com.google.android.exoplayer.chunk.ChunkSampleSource; 36 | import com.google.android.exoplayer.chunk.Format; 37 | import com.google.android.exoplayer.dash.DashChunkSource; 38 | import com.google.android.exoplayer.drm.StreamingDrmSessionManager; 39 | import com.google.android.exoplayer.hls.HlsSampleSource; 40 | import com.google.android.exoplayer.metadata.MetadataTrackRenderer.MetadataRenderer; 41 | import com.google.android.exoplayer.text.Cue; 42 | import com.google.android.exoplayer.text.TextRenderer; 43 | import com.google.android.exoplayer.upstream.BandwidthMeter; 44 | import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; 45 | import com.google.android.exoplayer.util.DebugTextViewHelper; 46 | import com.google.android.exoplayer.util.PlayerControl; 47 | 48 | import java.io.IOException; 49 | import java.util.Collections; 50 | import java.util.List; 51 | import java.util.Map; 52 | import java.util.concurrent.CopyOnWriteArrayList; 53 | 54 | /** 55 | * A wrapper around {@link com.google.android.exoplayer.ExoPlayer} that provides a higher level interface. It can be prepared 56 | * with one of a number of {@link gujarat.videoplayaer.player.DemoPlayer.RendererBuilder} classes to suit different use cases (e.g. DASH, 57 | * SmoothStreaming and so on). 58 | */ 59 | public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener, 60 | HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener, 61 | MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener, 62 | StreamingDrmSessionManager.EventListener, DashChunkSource.EventListener, TextRenderer, 63 | MetadataRenderer>, DebugTextViewHelper.Provider { 64 | 65 | // Constants pulled into this class for convenience. 66 | public static final int STATE_IDLE = ExoPlayer.STATE_IDLE; 67 | public static final int STATE_PREPARING = ExoPlayer.STATE_PREPARING; 68 | public static final int STATE_BUFFERING = ExoPlayer.STATE_BUFFERING; 69 | public static final int STATE_READY = ExoPlayer.STATE_READY; 70 | public static final int STATE_ENDED = ExoPlayer.STATE_ENDED; 71 | public static final int TRACK_DISABLED = ExoPlayer.TRACK_DISABLED; 72 | public static final int TRACK_DEFAULT = ExoPlayer.TRACK_DEFAULT; 73 | public static final int RENDERER_COUNT = 4; 74 | public static final int TYPE_VIDEO = 0; 75 | public static final int TYPE_AUDIO = 1; 76 | public static final int TYPE_TEXT = 2; 77 | public static final int TYPE_METADATA = 3; 78 | private static final int RENDERER_BUILDING_STATE_IDLE = 1; 79 | private static final int RENDERER_BUILDING_STATE_BUILDING = 2; 80 | private static final int RENDERER_BUILDING_STATE_BUILT = 3; 81 | private final RendererBuilder rendererBuilder; 82 | private final ExoPlayer player; 83 | private final PlayerControl playerControl; 84 | private final Handler mainHandler; 85 | private final CopyOnWriteArrayList listeners; 86 | private int rendererBuildingState; 87 | private int lastReportedPlaybackState; 88 | private boolean lastReportedPlayWhenReady; 89 | private Surface surface; 90 | private TrackRenderer videoRenderer; 91 | private CodecCounters codecCounters; 92 | private Format videoFormat; 93 | private int videoTrackToRestore; 94 | private BandwidthMeter bandwidthMeter; 95 | private boolean backgrounded; 96 | private CaptionListener captionListener; 97 | private Id3MetadataListener id3MetadataListener; 98 | private InternalErrorListener internalErrorListener; 99 | private InfoListener infoListener; 100 | 101 | public DemoPlayer(RendererBuilder rendererBuilder) { 102 | this.rendererBuilder = rendererBuilder; 103 | player = ExoPlayer.Factory.newInstance(RENDERER_COUNT, 1000, 5000); 104 | player.addListener(this); 105 | playerControl = new PlayerControl(player); 106 | mainHandler = new Handler(); 107 | listeners = new CopyOnWriteArrayList<>(); 108 | lastReportedPlaybackState = STATE_IDLE; 109 | rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 110 | // Disable text initially. 111 | player.setSelectedTrack(TYPE_TEXT, TRACK_DISABLED); 112 | } 113 | 114 | public PlayerControl getPlayerControl() { 115 | return playerControl; 116 | } 117 | 118 | public void addListener(Listener listener) { 119 | listeners.add(listener); 120 | } 121 | 122 | public void removeListener(Listener listener) { 123 | listeners.remove(listener); 124 | } 125 | 126 | public void setInternalErrorListener(InternalErrorListener listener) { 127 | internalErrorListener = listener; 128 | } 129 | 130 | public void setInfoListener(InfoListener listener) { 131 | infoListener = listener; 132 | } 133 | 134 | public void setCaptionListener(CaptionListener listener) { 135 | captionListener = listener; 136 | } 137 | 138 | public void setMetadataListener(Id3MetadataListener listener) { 139 | id3MetadataListener = listener; 140 | } 141 | 142 | public Surface getSurface() { 143 | return surface; 144 | } 145 | 146 | public void setSurface(Surface surface) { 147 | this.surface = surface; 148 | pushSurface(false); 149 | } 150 | 151 | public void blockingClearSurface() { 152 | surface = null; 153 | pushSurface(true); 154 | } 155 | 156 | public int getTrackCount(int type) { 157 | return player.getTrackCount(type); 158 | } 159 | 160 | public MediaFormat getTrackFormat(int type, int index) { 161 | return player.getTrackFormat(type, index); 162 | } 163 | 164 | public int getSelectedTrack(int type) { 165 | return player.getSelectedTrack(type); 166 | } 167 | 168 | public void setSelectedTrack(int type, int index) { 169 | player.setSelectedTrack(type, index); 170 | if (type == TYPE_TEXT && index < 0 && captionListener != null) { 171 | captionListener.onCues(Collections.emptyList()); 172 | } 173 | } 174 | 175 | public boolean getBackgrounded() { 176 | return backgrounded; 177 | } 178 | 179 | public void setBackgrounded(boolean backgrounded) { 180 | if (this.backgrounded == backgrounded) { 181 | return; 182 | } 183 | this.backgrounded = backgrounded; 184 | if (backgrounded) { 185 | videoTrackToRestore = getSelectedTrack(TYPE_VIDEO); 186 | setSelectedTrack(TYPE_VIDEO, TRACK_DISABLED); 187 | blockingClearSurface(); 188 | } else { 189 | setSelectedTrack(TYPE_VIDEO, videoTrackToRestore); 190 | } 191 | } 192 | 193 | public void prepare() { 194 | if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILT) { 195 | player.stop(); 196 | } 197 | rendererBuilder.cancel(); 198 | videoFormat = null; 199 | videoRenderer = null; 200 | rendererBuildingState = RENDERER_BUILDING_STATE_BUILDING; 201 | maybeReportPlayerState(); 202 | rendererBuilder.buildRenderers(this); 203 | } 204 | 205 | /** 206 | * Invoked with the results from a {@link gujarat.videoplayaer.player.DemoPlayer.RendererBuilder}. 207 | * 208 | * @param renderers Renderers indexed by {@link gujarat.videoplayaer.player.DemoPlayer} TYPE_* constants. An individual 209 | * element may be null if there do not exist tracks of the corresponding type. 210 | * @param bandwidthMeter Provides an estimate of the currently available bandwidth. May be null. 211 | */ 212 | /* package */ void onRenderers(TrackRenderer[] renderers, BandwidthMeter bandwidthMeter) { 213 | for (int i = 0; i < RENDERER_COUNT; i++) { 214 | if (renderers[i] == null) { 215 | // Convert a null renderer to a dummy renderer. 216 | renderers[i] = new DummyTrackRenderer(); 217 | } 218 | } 219 | // Complete preparation. 220 | this.videoRenderer = renderers[TYPE_VIDEO]; 221 | this.codecCounters = videoRenderer instanceof MediaCodecTrackRenderer 222 | ? ((MediaCodecTrackRenderer) videoRenderer).codecCounters 223 | : renderers[TYPE_AUDIO] instanceof MediaCodecTrackRenderer 224 | ? ((MediaCodecTrackRenderer) renderers[TYPE_AUDIO]).codecCounters : null; 225 | this.bandwidthMeter = bandwidthMeter; 226 | pushSurface(false); 227 | player.prepare(renderers); 228 | rendererBuildingState = RENDERER_BUILDING_STATE_BUILT; 229 | } 230 | 231 | /** 232 | * Invoked if a {@link gujarat.videoplayaer.player.DemoPlayer.RendererBuilder} encounters an error. 233 | * 234 | * @param e Describes the error. 235 | */ 236 | /* package */ void onRenderersError(Exception e) { 237 | if (internalErrorListener != null) { 238 | internalErrorListener.onRendererInitializationError(e); 239 | } 240 | for (Listener listener : listeners) { 241 | listener.onError(e); 242 | } 243 | rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 244 | maybeReportPlayerState(); 245 | } 246 | 247 | public void seekTo(long positionMs) { 248 | player.seekTo(positionMs); 249 | } 250 | 251 | public void release() { 252 | rendererBuilder.cancel(); 253 | rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 254 | surface = null; 255 | player.release(); 256 | } 257 | 258 | public int getPlaybackState() { 259 | if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILDING) { 260 | return STATE_PREPARING; 261 | } 262 | int playerState = player.getPlaybackState(); 263 | if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILT && playerState == STATE_IDLE) { 264 | // This is an edge case where the renderers are built, but are still being passed to the 265 | // player's playback thread. 266 | return STATE_PREPARING; 267 | } 268 | return playerState; 269 | } 270 | 271 | @Override 272 | public Format getFormat() { 273 | return videoFormat; 274 | } 275 | 276 | @Override 277 | public BandwidthMeter getBandwidthMeter() { 278 | return bandwidthMeter; 279 | } 280 | 281 | @Override 282 | public CodecCounters getCodecCounters() { 283 | return codecCounters; 284 | } 285 | 286 | @Override 287 | public long getCurrentPosition() { 288 | return player.getCurrentPosition(); 289 | } 290 | 291 | public long getDuration() { 292 | return player.getDuration(); 293 | } 294 | 295 | public int getBufferedPercentage() { 296 | return player.getBufferedPercentage(); 297 | } 298 | 299 | public boolean getPlayWhenReady() { 300 | return player.getPlayWhenReady(); 301 | } 302 | 303 | public void setPlayWhenReady(boolean playWhenReady) { 304 | player.setPlayWhenReady(playWhenReady); 305 | } 306 | 307 | /* package */ Looper getPlaybackLooper() { 308 | return player.getPlaybackLooper(); 309 | } 310 | 311 | /* package */ Handler getMainHandler() { 312 | return mainHandler; 313 | } 314 | 315 | @Override 316 | public void onPlayerStateChanged(boolean playWhenReady, int state) { 317 | maybeReportPlayerState(); 318 | } 319 | 320 | @Override 321 | public void onPlayerError(ExoPlaybackException exception) { 322 | rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 323 | for (Listener listener : listeners) { 324 | listener.onError(exception); 325 | } 326 | } 327 | 328 | @Override 329 | public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, 330 | float pixelWidthHeightRatio) { 331 | for (Listener listener : listeners) { 332 | listener.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio); 333 | } 334 | } 335 | 336 | @Override 337 | public void onDroppedFrames(int count, long elapsed) { 338 | if (infoListener != null) { 339 | infoListener.onDroppedFrames(count, elapsed); 340 | } 341 | } 342 | 343 | @Override 344 | public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) { 345 | if (infoListener != null) { 346 | infoListener.onBandwidthSample(elapsedMs, bytes, bitrateEstimate); 347 | } 348 | } 349 | 350 | @Override 351 | public void onDownstreamFormatChanged(int sourceId, Format format, int trigger, 352 | long mediaTimeMs) { 353 | if (infoListener == null) { 354 | return; 355 | } 356 | if (sourceId == TYPE_VIDEO) { 357 | videoFormat = format; 358 | infoListener.onVideoFormatEnabled(format, trigger, mediaTimeMs); 359 | } else if (sourceId == TYPE_AUDIO) { 360 | infoListener.onAudioFormatEnabled(format, trigger, mediaTimeMs); 361 | } 362 | } 363 | 364 | @Override 365 | public void onDrmKeysLoaded() { 366 | // Do nothing. 367 | } 368 | 369 | @Override 370 | public void onDrmSessionManagerError(Exception e) { 371 | if (internalErrorListener != null) { 372 | internalErrorListener.onDrmSessionManagerError(e); 373 | } 374 | } 375 | 376 | @Override 377 | public void onDecoderInitializationError(DecoderInitializationException e) { 378 | if (internalErrorListener != null) { 379 | internalErrorListener.onDecoderInitializationError(e); 380 | } 381 | } 382 | 383 | @Override 384 | public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { 385 | if (internalErrorListener != null) { 386 | internalErrorListener.onAudioTrackInitializationError(e); 387 | } 388 | } 389 | 390 | @Override 391 | public void onAudioTrackWriteError(AudioTrack.WriteException e) { 392 | if (internalErrorListener != null) { 393 | internalErrorListener.onAudioTrackWriteError(e); 394 | } 395 | } 396 | 397 | @Override 398 | public void onCryptoError(CryptoException e) { 399 | if (internalErrorListener != null) { 400 | internalErrorListener.onCryptoError(e); 401 | } 402 | } 403 | 404 | @Override 405 | public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, 406 | long initializationDurationMs) { 407 | if (infoListener != null) { 408 | infoListener.onDecoderInitialized(decoderName, elapsedRealtimeMs, initializationDurationMs); 409 | } 410 | } 411 | 412 | @Override 413 | public void onLoadError(int sourceId, IOException e) { 414 | if (internalErrorListener != null) { 415 | internalErrorListener.onLoadError(sourceId, e); 416 | } 417 | } 418 | 419 | @Override 420 | public void onCues(List cues) { 421 | if (captionListener != null && getSelectedTrack(TYPE_TEXT) != TRACK_DISABLED) { 422 | captionListener.onCues(cues); 423 | } 424 | } 425 | 426 | @Override 427 | public void onMetadata(Map metadata) { 428 | if (id3MetadataListener != null && getSelectedTrack(TYPE_METADATA) != TRACK_DISABLED) { 429 | id3MetadataListener.onId3Metadata(metadata); 430 | } 431 | } 432 | 433 | @Override 434 | public void onAvailableRangeChanged(TimeRange availableRange) { 435 | if (infoListener != null) { 436 | infoListener.onAvailableRangeChanged(availableRange); 437 | } 438 | } 439 | 440 | @Override 441 | public void onPlayWhenReadyCommitted() { 442 | // Do nothing. 443 | } 444 | 445 | @Override 446 | public void onDrawnToSurface(Surface surface) { 447 | // Do nothing. 448 | } 449 | 450 | @Override 451 | public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, 452 | long mediaStartTimeMs, long mediaEndTimeMs) { 453 | if (infoListener != null) { 454 | infoListener.onLoadStarted(sourceId, length, type, trigger, format, mediaStartTimeMs, 455 | mediaEndTimeMs); 456 | } 457 | } 458 | 459 | @Override 460 | public void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, 461 | long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs) { 462 | if (infoListener != null) { 463 | infoListener.onLoadCompleted(sourceId, bytesLoaded, type, trigger, format, mediaStartTimeMs, 464 | mediaEndTimeMs, elapsedRealtimeMs, loadDurationMs); 465 | } 466 | } 467 | 468 | @Override 469 | public void onLoadCanceled(int sourceId, long bytesLoaded) { 470 | // Do nothing. 471 | } 472 | 473 | @Override 474 | public void onUpstreamDiscarded(int sourceId, long mediaStartTimeMs, long mediaEndTimeMs) { 475 | // Do nothing. 476 | } 477 | 478 | private void maybeReportPlayerState() { 479 | boolean playWhenReady = player.getPlayWhenReady(); 480 | int playbackState = getPlaybackState(); 481 | if (lastReportedPlayWhenReady != playWhenReady || lastReportedPlaybackState != playbackState) { 482 | for (Listener listener : listeners) { 483 | listener.onStateChanged(playWhenReady, playbackState); 484 | } 485 | lastReportedPlayWhenReady = playWhenReady; 486 | lastReportedPlaybackState = playbackState; 487 | } 488 | } 489 | 490 | private void pushSurface(boolean blockForSurfacePush) { 491 | if (videoRenderer == null) { 492 | return; 493 | } 494 | 495 | if (blockForSurfacePush) { 496 | player.blockingSendMessage( 497 | videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); 498 | } else { 499 | player.sendMessage( 500 | videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); 501 | } 502 | } 503 | 504 | /** 505 | * Builds renderers for the player. 506 | */ 507 | public interface RendererBuilder { 508 | /** 509 | * Builds renderers for playback. 510 | * 511 | * @param player The player for which renderers are being built. {@link gujarat.videoplayaer.player.DemoPlayer#onRenderers} 512 | * should be invoked once the renderers have been built. If building fails, 513 | * {@link gujarat.videoplayaer.player.DemoPlayer#onRenderersError} should be invoked. 514 | */ 515 | void buildRenderers(DemoPlayer player); 516 | /** 517 | * Cancels the current build operation, if there is one. Else does nothing. 518 | *

519 | * A canceled build operation must not invoke {@link gujarat.videoplayaer.player.DemoPlayer#onRenderers} or 520 | * {@link gujarat.videoplayaer.player.DemoPlayer#onRenderersError} on the player, which may have been released. 521 | */ 522 | void cancel(); 523 | } 524 | 525 | /** 526 | * A listener for core events. 527 | */ 528 | public interface Listener { 529 | void onStateChanged(boolean playWhenReady, int playbackState); 530 | void onError(Exception e); 531 | void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, 532 | float pixelWidthHeightRatio); 533 | } 534 | 535 | /** 536 | * A listener for internal errors. 537 | *

538 | * These errors are not visible to the user, and hence this listener is provided for 539 | * informational purposes only. Note however that an internal error may cause a fatal 540 | * error if the player fails to recover. If this happens, {@link gujarat.videoplayaer.player.DemoPlayer.Listener#onError(Exception)} 541 | * will be invoked. 542 | */ 543 | public interface InternalErrorListener { 544 | void onRendererInitializationError(Exception e); 545 | void onAudioTrackInitializationError(AudioTrack.InitializationException e); 546 | void onAudioTrackWriteError(AudioTrack.WriteException e); 547 | void onDecoderInitializationError(DecoderInitializationException e); 548 | void onCryptoError(CryptoException e); 549 | void onLoadError(int sourceId, IOException e); 550 | void onDrmSessionManagerError(Exception e); 551 | } 552 | 553 | /** 554 | * A listener for debugging information. 555 | */ 556 | public interface InfoListener { 557 | void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs); 558 | void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs); 559 | void onDroppedFrames(int count, long elapsed); 560 | void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate); 561 | void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, 562 | long mediaStartTimeMs, long mediaEndTimeMs); 563 | void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, 564 | long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs); 565 | void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, 566 | long initializationDurationMs); 567 | void onAvailableRangeChanged(TimeRange availableRange); 568 | } 569 | 570 | /** 571 | * A listener for receiving notifications of timed text. 572 | */ 573 | public interface CaptionListener { 574 | void onCues(List cues); 575 | } 576 | 577 | /** 578 | * A listener for receiving ID3 metadata parsed from the media stream. 579 | */ 580 | public interface Id3MetadataListener { 581 | void onId3Metadata(Map metadata); 582 | } 583 | 584 | } 585 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/player/ExtractorRendererBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer.player; 17 | 18 | import android.content.Context; 19 | import android.media.MediaCodec; 20 | import android.net.Uri; 21 | 22 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 23 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 24 | import com.google.android.exoplayer.TrackRenderer; 25 | import com.google.android.exoplayer.audio.AudioCapabilities; 26 | import com.google.android.exoplayer.extractor.ExtractorSampleSource; 27 | import com.google.android.exoplayer.text.TextTrackRenderer; 28 | import com.google.android.exoplayer.upstream.Allocator; 29 | import com.google.android.exoplayer.upstream.DataSource; 30 | import com.google.android.exoplayer.upstream.DefaultAllocator; 31 | import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; 32 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 33 | 34 | /** 35 | * A {@link RendererBuilder} for streams that can be read using an {@link com.google.android.exoplayer.extractor.Extractor}. 36 | */ 37 | public class ExtractorRendererBuilder implements DemoPlayer.RendererBuilder { 38 | 39 | private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; 40 | private static final int BUFFER_SEGMENT_COUNT = 256; 41 | 42 | private final Context context; 43 | private final String userAgent; 44 | private final Uri uri; 45 | 46 | public ExtractorRendererBuilder(Context context, String userAgent, Uri uri) { 47 | this.context = context; 48 | this.userAgent = userAgent; 49 | this.uri = uri; 50 | } 51 | 52 | @Override 53 | public void buildRenderers(DemoPlayer player) { 54 | Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE); 55 | 56 | // Build the video and audio renderers. 57 | DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(player.getMainHandler(), 58 | null); 59 | DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 60 | ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator, 61 | BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE); 62 | MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, 63 | sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, player.getMainHandler(), 64 | player, 50); 65 | MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, 66 | null, true, player.getMainHandler(), player, AudioCapabilities.getCapabilities(context)); 67 | TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player, 68 | player.getMainHandler().getLooper()); 69 | 70 | // Invoke the callback. 71 | TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; 72 | renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; 73 | renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; 74 | renderers[DemoPlayer.TYPE_TEXT] = textRenderer; 75 | player.onRenderers(renderers, bandwidthMeter); 76 | } 77 | 78 | @Override 79 | public void cancel() { 80 | // Do nothing. 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/player/HlsRendererBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer.player; 17 | 18 | import android.content.Context; 19 | import android.media.MediaCodec; 20 | import android.os.Handler; 21 | 22 | import com.google.android.exoplayer.DefaultLoadControl; 23 | import com.google.android.exoplayer.LoadControl; 24 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 25 | import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; 26 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 27 | import com.google.android.exoplayer.TrackRenderer; 28 | import com.google.android.exoplayer.audio.AudioCapabilities; 29 | import com.google.android.exoplayer.chunk.VideoFormatSelectorUtil; 30 | import com.google.android.exoplayer.hls.HlsChunkSource; 31 | import com.google.android.exoplayer.hls.HlsMasterPlaylist; 32 | import com.google.android.exoplayer.hls.HlsPlaylist; 33 | import com.google.android.exoplayer.hls.HlsPlaylistParser; 34 | import com.google.android.exoplayer.hls.HlsSampleSource; 35 | import com.google.android.exoplayer.metadata.Id3Parser; 36 | import com.google.android.exoplayer.metadata.MetadataTrackRenderer; 37 | import com.google.android.exoplayer.text.eia608.Eia608TrackRenderer; 38 | import com.google.android.exoplayer.upstream.DataSource; 39 | import com.google.android.exoplayer.upstream.DefaultAllocator; 40 | import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; 41 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 42 | import com.google.android.exoplayer.util.ManifestFetcher; 43 | import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback; 44 | 45 | import java.io.IOException; 46 | import java.util.Map; 47 | 48 | /** 49 | * A {@link RendererBuilder} for HLS. 50 | */ 51 | public class HlsRendererBuilder implements DemoPlayer.RendererBuilder { 52 | 53 | private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; 54 | private static final int BUFFER_SEGMENTS = 256; 55 | 56 | private final Context context; 57 | private final String userAgent; 58 | private final String url; 59 | 60 | private AsyncRendererBuilder currentAsyncBuilder; 61 | 62 | public HlsRendererBuilder(Context context, String userAgent, String url) { 63 | this.context = context; 64 | this.userAgent = userAgent; 65 | this.url = url; 66 | } 67 | 68 | @Override 69 | public void buildRenderers(DemoPlayer player) { 70 | currentAsyncBuilder = new AsyncRendererBuilder(context, userAgent, url, player); 71 | currentAsyncBuilder.init(); 72 | } 73 | 74 | @Override 75 | public void cancel() { 76 | if (currentAsyncBuilder != null) { 77 | currentAsyncBuilder.cancel(); 78 | currentAsyncBuilder = null; 79 | } 80 | } 81 | 82 | private static final class AsyncRendererBuilder implements ManifestCallback { 83 | 84 | private final Context context; 85 | private final String userAgent; 86 | private final String url; 87 | private final DemoPlayer player; 88 | private final ManifestFetcher playlistFetcher; 89 | 90 | private boolean canceled; 91 | 92 | public AsyncRendererBuilder(Context context, String userAgent, String url, DemoPlayer player) { 93 | this.context = context; 94 | this.userAgent = userAgent; 95 | this.url = url; 96 | this.player = player; 97 | HlsPlaylistParser parser = new HlsPlaylistParser(); 98 | playlistFetcher = new ManifestFetcher<>(url, new DefaultUriDataSource(context, userAgent), 99 | parser); 100 | } 101 | 102 | public void init() { 103 | playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this); 104 | } 105 | 106 | public void cancel() { 107 | canceled = true; 108 | } 109 | 110 | @Override 111 | public void onSingleManifestError(IOException e) { 112 | if (canceled) { 113 | return; 114 | } 115 | 116 | player.onRenderersError(e); 117 | } 118 | 119 | @Override 120 | public void onSingleManifest(HlsPlaylist manifest) { 121 | if (canceled) { 122 | return; 123 | } 124 | 125 | Handler mainHandler = player.getMainHandler(); 126 | LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); 127 | DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); 128 | 129 | int[] variantIndices = null; 130 | if (manifest instanceof HlsMasterPlaylist) { 131 | HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) manifest; 132 | try { 133 | variantIndices = VideoFormatSelectorUtil.selectVideoFormatsForDefaultDisplay( 134 | context, masterPlaylist.variants, null, false); 135 | } catch (DecoderQueryException e) { 136 | player.onRenderersError(e); 137 | return; 138 | } 139 | if (variantIndices.length == 0) { 140 | player.onRenderersError(new IllegalStateException("No variants selected.")); 141 | return; 142 | } 143 | } 144 | 145 | DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 146 | HlsChunkSource chunkSource = new HlsChunkSource(dataSource, url, manifest, bandwidthMeter, 147 | variantIndices, HlsChunkSource.ADAPTIVE_MODE_SPLICE); 148 | HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl, 149 | BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_VIDEO); 150 | MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, 151 | sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, player, 50); 152 | MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, 153 | null, true, player.getMainHandler(), player, AudioCapabilities.getCapabilities(context)); 154 | MetadataTrackRenderer> id3Renderer = new MetadataTrackRenderer<>( 155 | sampleSource, new Id3Parser(), player, mainHandler.getLooper()); 156 | Eia608TrackRenderer closedCaptionRenderer = new Eia608TrackRenderer(sampleSource, player, 157 | mainHandler.getLooper()); 158 | 159 | TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; 160 | renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; 161 | renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; 162 | renderers[DemoPlayer.TYPE_METADATA] = id3Renderer; 163 | renderers[DemoPlayer.TYPE_TEXT] = closedCaptionRenderer; 164 | player.onRenderers(renderers, bandwidthMeter); 165 | } 166 | 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /app/src/main/java/gujarat/videoplayaer/player/SmoothStreamingRendererBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package gujarat.videoplayaer.player; 17 | 18 | import android.content.Context; 19 | import android.media.MediaCodec; 20 | import android.os.Handler; 21 | 22 | import com.google.android.exoplayer.DefaultLoadControl; 23 | import com.google.android.exoplayer.LoadControl; 24 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 25 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 26 | import com.google.android.exoplayer.TrackRenderer; 27 | import com.google.android.exoplayer.audio.AudioCapabilities; 28 | import com.google.android.exoplayer.chunk.ChunkSampleSource; 29 | import com.google.android.exoplayer.chunk.ChunkSource; 30 | import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; 31 | import com.google.android.exoplayer.drm.DrmSessionManager; 32 | import com.google.android.exoplayer.drm.MediaDrmCallback; 33 | import com.google.android.exoplayer.drm.StreamingDrmSessionManager; 34 | import com.google.android.exoplayer.drm.UnsupportedDrmException; 35 | import com.google.android.exoplayer.smoothstreaming.DefaultSmoothStreamingTrackSelector; 36 | import com.google.android.exoplayer.smoothstreaming.SmoothStreamingChunkSource; 37 | import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest; 38 | import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement; 39 | import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser; 40 | import com.google.android.exoplayer.text.TextTrackRenderer; 41 | import com.google.android.exoplayer.upstream.DataSource; 42 | import com.google.android.exoplayer.upstream.DefaultAllocator; 43 | import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; 44 | import com.google.android.exoplayer.upstream.DefaultHttpDataSource; 45 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 46 | import com.google.android.exoplayer.util.ManifestFetcher; 47 | import com.google.android.exoplayer.util.Util; 48 | 49 | import java.io.IOException; 50 | 51 | /** 52 | * A {@link RendererBuilder} for SmoothStreaming. 53 | */ 54 | public class SmoothStreamingRendererBuilder implements DemoPlayer.RendererBuilder { 55 | 56 | private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; 57 | private static final int VIDEO_BUFFER_SEGMENTS = 200; 58 | private static final int AUDIO_BUFFER_SEGMENTS = 54; 59 | private static final int TEXT_BUFFER_SEGMENTS = 2; 60 | private static final int LIVE_EDGE_LATENCY_MS = 30000; 61 | 62 | private final Context context; 63 | private final String userAgent; 64 | private final String url; 65 | private final MediaDrmCallback drmCallback; 66 | 67 | private AsyncRendererBuilder currentAsyncBuilder; 68 | 69 | public SmoothStreamingRendererBuilder(Context context, String userAgent, String url, 70 | MediaDrmCallback drmCallback) { 71 | this.context = context; 72 | this.userAgent = userAgent; 73 | this.url = Util.toLowerInvariant(url).endsWith("/manifest") ? url : url + "/Manifest"; 74 | this.drmCallback = drmCallback; 75 | } 76 | 77 | @Override 78 | public void buildRenderers(DemoPlayer player) { 79 | currentAsyncBuilder = new AsyncRendererBuilder(context, userAgent, url, drmCallback, player); 80 | currentAsyncBuilder.init(); 81 | } 82 | 83 | @Override 84 | public void cancel() { 85 | if (currentAsyncBuilder != null) { 86 | currentAsyncBuilder.cancel(); 87 | currentAsyncBuilder = null; 88 | } 89 | } 90 | 91 | private static final class AsyncRendererBuilder 92 | implements ManifestFetcher.ManifestCallback { 93 | 94 | private final Context context; 95 | private final String userAgent; 96 | private final MediaDrmCallback drmCallback; 97 | private final DemoPlayer player; 98 | private final ManifestFetcher manifestFetcher; 99 | 100 | private boolean canceled; 101 | 102 | public AsyncRendererBuilder(Context context, String userAgent, String url, 103 | MediaDrmCallback drmCallback, DemoPlayer player) { 104 | this.context = context; 105 | this.userAgent = userAgent; 106 | this.drmCallback = drmCallback; 107 | this.player = player; 108 | SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser(); 109 | manifestFetcher = new ManifestFetcher<>(url, new DefaultHttpDataSource(userAgent, null), 110 | parser); 111 | } 112 | 113 | public void init() { 114 | manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this); 115 | } 116 | 117 | public void cancel() { 118 | canceled = true; 119 | } 120 | 121 | @Override 122 | public void onSingleManifestError(IOException exception) { 123 | if (canceled) { 124 | return; 125 | } 126 | 127 | player.onRenderersError(exception); 128 | } 129 | 130 | @Override 131 | public void onSingleManifest(SmoothStreamingManifest manifest) { 132 | if (canceled) { 133 | return; 134 | } 135 | 136 | Handler mainHandler = player.getMainHandler(); 137 | LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); 138 | DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player); 139 | 140 | // Check drm support if necessary. 141 | DrmSessionManager drmSessionManager = null; 142 | if (manifest.protectionElement != null) { 143 | if (Util.SDK_INT < 18) { 144 | player.onRenderersError( 145 | new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); 146 | return; 147 | } 148 | try { 149 | drmSessionManager = new StreamingDrmSessionManager(manifest.protectionElement.uuid, 150 | player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); 151 | } catch (UnsupportedDrmException e) { 152 | player.onRenderersError(e); 153 | return; 154 | } 155 | } 156 | 157 | // Build the video renderer. 158 | DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 159 | ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher, 160 | new DefaultSmoothStreamingTrackSelector(context, StreamElement.TYPE_VIDEO), 161 | videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS); 162 | ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, 163 | VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, 164 | DemoPlayer.TYPE_VIDEO); 165 | TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource, 166 | MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, drmSessionManager, true, mainHandler, 167 | player, 50); 168 | 169 | // Build the audio renderer. 170 | DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 171 | ChunkSource audioChunkSource = new SmoothStreamingChunkSource(manifestFetcher, 172 | new DefaultSmoothStreamingTrackSelector(context, StreamElement.TYPE_AUDIO), 173 | audioDataSource, null, LIVE_EDGE_LATENCY_MS); 174 | ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, 175 | AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, 176 | DemoPlayer.TYPE_AUDIO); 177 | TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, 178 | drmSessionManager, true, mainHandler, player, AudioCapabilities.getCapabilities(context)); 179 | 180 | // Build the text renderer. 181 | DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); 182 | ChunkSource textChunkSource = new SmoothStreamingChunkSource(manifestFetcher, 183 | new DefaultSmoothStreamingTrackSelector(context, StreamElement.TYPE_TEXT), 184 | textDataSource, null, LIVE_EDGE_LATENCY_MS); 185 | ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, 186 | TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, 187 | DemoPlayer.TYPE_TEXT); 188 | TrackRenderer textRenderer = new TextTrackRenderer(textSampleSource, player, 189 | mainHandler.getLooper()); 190 | 191 | // Invoke the callback. 192 | TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; 193 | renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; 194 | renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; 195 | renderers[DemoPlayer.TYPE_TEXT] = textRenderer; 196 | player.onRenderers(renderers, bandwidthMeter); 197 | } 198 | 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gujarats/AndroidVideoPlayer/a9a42703449ab78e861dbb5ba287a2a439801098/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gujarats/AndroidVideoPlayer/a9a42703449ab78e861dbb5ba287a2a439801098/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gujarats/AndroidVideoPlayer/a9a42703449ab78e861dbb5ba287a2a439801098/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gujarats/AndroidVideoPlayer/a9a42703449ab78e861dbb5ba287a2a439801098/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gujarats/AndroidVideoPlayer/a9a42703449ab78e861dbb5ba287a2a439801098/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/player_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 23 | 24 | 29 | 30 | 35 | 36 | 40 | 41 | 45 | 46 | 47 | 48 | 53 | 54 | 61 | 62 | 69 | 70 | 75 | 76 |