├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── SimpleExoPlayer.iml ├── SimpleExoPlayer ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── simpleexoplayer.iml └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jfowler │ │ └── onramp │ │ └── simpleexoplayer │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── jfowler │ │ └── onramp │ │ └── simpleexoplayer │ │ ├── MediaModels │ │ ├── AdaptiveMedia.java │ │ ├── AudioMedia.java │ │ ├── DashMedia.java │ │ ├── HlsMedia.java │ │ ├── Media.java │ │ ├── SmoothStreamingMedia.java │ │ └── VideoMedia.java │ │ ├── Renderers │ │ ├── DashRenderer.java │ │ ├── HlsRenderer.java │ │ ├── Renderer.java │ │ ├── RendererInterfaces │ │ │ ├── MediaListener.java │ │ │ └── RendererListener.java │ │ └── SmoothStreamingRenderer.java │ │ └── SimpleExoPlayer.java │ └── res │ └── values │ └── strings.xml ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jfowler │ │ └── onramp │ │ └── simpleexoplayer │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── jfowler │ │ └── onramp │ │ └── simpleexoplayer │ │ ├── Utils │ │ ├── MediaFactory.java │ │ └── Samples.java │ │ ├── activities │ │ ├── AudioPlayerActivity.java │ │ ├── MainActivity.java │ │ └── VideoPlayerActivity.java │ │ └── fragments │ │ ├── MediaSelectorFragment.java │ │ └── NavigationDrawerFragment.java │ └── res │ ├── drawable-hdpi │ └── drawer_shadow.9.png │ ├── drawable-mdpi │ └── drawer_shadow.9.png │ ├── drawable-xhdpi │ └── drawer_shadow.9.png │ ├── drawable-xxhdpi │ └── drawer_shadow.9.png │ ├── layout │ ├── activity_main.xml │ ├── activity_player_audio.xml │ ├── activity_player_video.xml │ ├── fragment_media_selector.xml │ ├── fragment_navigation_drawer.xml │ ├── fragment_video.xml │ └── listview_media_item.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── 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 | /.idea 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | SimpleExoPlayer -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Android 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 62 | 63 | 64 | 65 | 66 | 1.8 67 | 68 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleExoPlayer 2 | 3 | ## Google has just released an easier to use SimpleExoPlayer [here](https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/SimpleExoPlayer.html) 4 | -------------------------------------------------------------------------------- /SimpleExoPlayer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /SimpleExoPlayer/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /SimpleExoPlayer/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | bintrayRepo = 'maven' 5 | bintrayName = 'simpleexoplayer' 6 | 7 | publishedGroupId = 'com.srmarlins.simpleexoplayer' 8 | libraryName = 'SimpleExoPlayer' 9 | artifact = 'simpleexoplayer' 10 | 11 | libraryDescription = 'An easy to use wrapper for Google\'s ExoPlayer class (A better replacement for MediaPlayer).' 12 | 13 | siteUrl = 'https://github.com/srmarlins/SimpleExoPlayer' 14 | gitUrl = 'https://github.com/srmarlins/SimpleExoPlayer.git' 15 | 16 | libraryVersion = '0.9.5' 17 | 18 | developerId = 'srmarlins' 19 | developerName = 'Jared Fowler' 20 | developerEmail = 'jtfowler93@gmail.com' 21 | 22 | licenseName = 'The Apache Software License, Version 2.0' 23 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 24 | allLicenses = ["Apache-2.0"] 25 | } 26 | 27 | android { 28 | compileSdkVersion 23 29 | buildToolsVersion "23.0.0" 30 | defaultConfig { 31 | minSdkVersion 16 32 | targetSdkVersion 23 33 | versionCode 1 34 | versionName '0.1' 35 | } 36 | buildTypes { 37 | release { 38 | minifyEnabled false 39 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 40 | } 41 | } 42 | productFlavors { 43 | } 44 | } 45 | 46 | dependencies { 47 | compile fileTree(include: ['*.jar'], dir: 'libs') 48 | compile 'com.android.support:appcompat-v7:23.0.0' 49 | compile 'com.google.android.exoplayer:exoplayer:r1.4.2' 50 | } 51 | 52 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 53 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' 54 | -------------------------------------------------------------------------------- /SimpleExoPlayer/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 /Users/jfowler/Library/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 | -------------------------------------------------------------------------------- /SimpleExoPlayer/simpleexoplayer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/androidTest/java/com/jfowler/onramp/simpleexoplayer/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer; 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 | } -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/MediaModels/AdaptiveMedia.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.MediaModels; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 7 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 8 | import com.google.android.exoplayer.TrackRenderer; 9 | import com.google.android.exoplayer.text.TextTrackRenderer; 10 | import com.jfowler.onramp.simpleexoplayer.Renderers.Renderer; 11 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.MediaListener; 12 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.RendererListener; 13 | 14 | 15 | 16 | /** 17 | * Created by jfowler on 8/27/15. 18 | */ 19 | public abstract class AdaptiveMedia extends Media implements RendererListener{ 20 | 21 | public static final int VIDEO_BUFFER_SEGMENTS = 200; 22 | public static final int AUDIO_BUFFER_SEGMENTS = 60; 23 | 24 | private MediaCodecVideoTrackRenderer videoTrackRenderer; 25 | private MediaCodecAudioTrackRenderer audioTrackRenderer; 26 | private TextTrackRenderer textTrackRenderer; 27 | private MediaListener mediaListener; 28 | 29 | public AdaptiveMedia(Context context, MediaListener mediaListener, Uri uri, String userAgent) { 30 | super(context, uri, userAgent); 31 | this.mediaListener = mediaListener; 32 | } 33 | 34 | protected abstract void prepareRender(); 35 | 36 | @Override 37 | public TrackRenderer[] buildRenderer(Context context) { 38 | return buildRenderer(context, BUFFER_SEGMENT_SIZE, BUFFER_SEGMENT_COUNT); 39 | } 40 | 41 | @Override 42 | public final TrackRenderer[] buildRenderer(Context context, int bufferSegmentSize, int bufferSegmentCount) { 43 | TrackRenderer[] renderers = new TrackRenderer[3]; 44 | renderers[Renderer.TYPE_VIDEO] = videoTrackRenderer; 45 | renderers[Renderer.TYPE_AUDIO] = audioTrackRenderer; 46 | renderers[Renderer.TYPE_TEXT] = textTrackRenderer; 47 | 48 | return renderers; 49 | } 50 | 51 | @Override 52 | public void onPrepared(TrackRenderer[] renderers) { 53 | videoTrackRenderer = (MediaCodecVideoTrackRenderer)renderers[Renderer.TYPE_VIDEO]; 54 | audioTrackRenderer = (MediaCodecAudioTrackRenderer)renderers[Renderer.TYPE_AUDIO]; 55 | textTrackRenderer = (TextTrackRenderer)renderers[Renderer.TYPE_TEXT]; 56 | mediaListener.mediaPrepared(this); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/MediaModels/AudioMedia.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.MediaModels; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 7 | import com.google.android.exoplayer.TrackRenderer; 8 | import com.google.android.exoplayer.extractor.ExtractorSampleSource; 9 | import com.google.android.exoplayer.upstream.Allocator; 10 | import com.google.android.exoplayer.upstream.DataSource; 11 | import com.google.android.exoplayer.upstream.DefaultAllocator; 12 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 13 | 14 | /** 15 | * Created by jfowler on 8/27/15. 16 | */ 17 | public class AudioMedia extends Media { 18 | 19 | public AudioMedia(Context context, Uri uri, String userAgent) { 20 | super(context, uri, userAgent); 21 | } 22 | 23 | @Override 24 | public TrackRenderer[] buildRenderer(Context context) { 25 | return buildRenderer(context, BUFFER_SEGMENT_SIZE, BUFFER_SEGMENT_COUNT); 26 | } 27 | 28 | @Override 29 | public TrackRenderer[] buildRenderer(Context context, int bufferSegmentSize, int bufferSegmentCount){ 30 | Allocator allocator = new DefaultAllocator(bufferSegmentSize); 31 | DataSource dataSource = new DefaultUriDataSource(context, getUserAgent()); 32 | ExtractorSampleSource sampleSource = new ExtractorSampleSource( 33 | getUri(), dataSource, allocator, 34 | bufferSegmentSize*bufferSegmentCount); 35 | MediaCodecAudioTrackRenderer[] array = new MediaCodecAudioTrackRenderer[1]; 36 | array[0] = new MediaCodecAudioTrackRenderer(sampleSource, null, true); 37 | return array; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/MediaModels/DashMedia.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.MediaModels; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.jfowler.onramp.simpleexoplayer.Renderers.DashRenderer; 7 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.MediaListener; 8 | 9 | 10 | /** 11 | * Created by jfowler on 8/27/15. 12 | */ 13 | public class DashMedia extends AdaptiveMedia{ 14 | 15 | private DashRenderer dashRenderer; 16 | 17 | public DashMedia(Context context, MediaListener listener, Uri uri, String userAgent){ 18 | super(context, listener, uri, userAgent); 19 | dashRenderer = new DashRenderer(this); 20 | } 21 | 22 | @Override 23 | protected void prepareRender() { 24 | dashRenderer.prepareRender(getContext(), this); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/MediaModels/HlsMedia.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.MediaModels; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.jfowler.onramp.simpleexoplayer.Renderers.HlsRenderer; 7 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.MediaListener; 8 | 9 | 10 | /** 11 | * Created by jfowler on 9/1/15. 12 | */ 13 | public class HlsMedia extends AdaptiveMedia { 14 | 15 | private HlsRenderer hlsRenderer; 16 | 17 | public HlsMedia(Context context, MediaListener mediaListener, Uri uri, String userAgent) { 18 | super(context, mediaListener, uri, userAgent); 19 | hlsRenderer = new HlsRenderer(this); 20 | } 21 | 22 | @Override 23 | protected void prepareRender() { 24 | hlsRenderer.prepareRender(getContext(), this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/MediaModels/Media.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.MediaModels; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.google.android.exoplayer.TrackRenderer; 7 | 8 | /** 9 | * Created by jfowler on 8/27/15. 10 | */ 11 | public abstract class Media { 12 | public static final int BUFFER_SEGMENT_SIZE = 64 * 1024; 13 | public static final int BUFFER_SEGMENT_COUNT = 160; 14 | 15 | private String userAgent; 16 | private Uri uri; 17 | private Context context; 18 | 19 | public Media(Context context, Uri uri, String userAgent){ 20 | this.context = context; 21 | this.uri = uri; 22 | this.userAgent = userAgent; 23 | } 24 | 25 | public Uri getUri(){ 26 | return this.uri; 27 | } 28 | 29 | public void setUri(Uri uri){ 30 | if(uri != null && uri.getPath() != null) { 31 | this.uri = uri; 32 | }else{ 33 | throw new IllegalArgumentException("You must pass in a Uri containing a valid path"); 34 | } 35 | } 36 | 37 | public Context getContext(){ 38 | return this.context; 39 | } 40 | 41 | public void setContext(Context context){ 42 | this.context = context; 43 | } 44 | 45 | public String getUserAgent(){ 46 | return this.userAgent; 47 | } 48 | 49 | public void setUserAgent(String userAgent){ 50 | this.userAgent = userAgent; 51 | } 52 | 53 | public abstract TrackRenderer[] buildRenderer(Context context); 54 | public abstract TrackRenderer[] buildRenderer(Context context, int bufferSegmentSize, int bufferSegmentCount); 55 | } 56 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/MediaModels/SmoothStreamingMedia.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.MediaModels; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.MediaListener; 7 | import com.jfowler.onramp.simpleexoplayer.Renderers.SmoothStreamingRenderer; 8 | 9 | 10 | /** 11 | * Created by jfowler on 8/31/15. 12 | */ 13 | public class SmoothStreamingMedia extends AdaptiveMedia { 14 | private SmoothStreamingRenderer smoothRenderer; 15 | 16 | public SmoothStreamingMedia(Context context, MediaListener listener, Uri uri, String userAgent){ 17 | super(context, listener, uri, userAgent); 18 | smoothRenderer = new SmoothStreamingRenderer(this); 19 | } 20 | 21 | @Override 22 | protected void prepareRender() { 23 | smoothRenderer.prepareRender(getContext(), this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/MediaModels/VideoMedia.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.MediaModels; 2 | 3 | import android.content.Context; 4 | import android.media.MediaCodec; 5 | import android.net.Uri; 6 | 7 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 8 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 9 | import com.google.android.exoplayer.TrackRenderer; 10 | import com.google.android.exoplayer.extractor.ExtractorSampleSource; 11 | import com.google.android.exoplayer.upstream.Allocator; 12 | import com.google.android.exoplayer.upstream.DataSource; 13 | import com.google.android.exoplayer.upstream.DefaultAllocator; 14 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 15 | import com.jfowler.onramp.simpleexoplayer.Renderers.Renderer; 16 | 17 | /** 18 | * Created by jfowler on 8/27/15. 19 | */ 20 | public class VideoMedia extends Media { 21 | 22 | public VideoMedia(Context context, Uri uri, String userAgent) { 23 | super(context, uri, userAgent); 24 | } 25 | 26 | @Override 27 | public TrackRenderer[] buildRenderer(Context context) { 28 | return buildRenderer(context, BUFFER_SEGMENT_SIZE, BUFFER_SEGMENT_COUNT); 29 | } 30 | 31 | @Override 32 | public TrackRenderer[] buildRenderer(Context context, int bufferSegmentSize, int bufferSegmentCount) { 33 | TrackRenderer[] array = new TrackRenderer[2]; 34 | Allocator allocator = new DefaultAllocator(bufferSegmentSize); 35 | DataSource dataSource = new DefaultUriDataSource(context, getUserAgent()); 36 | ExtractorSampleSource sampleSource = new ExtractorSampleSource( 37 | getUri(), dataSource, allocator, bufferSegmentSize * bufferSegmentCount); 38 | array[Renderer.TYPE_VIDEO] = new MediaCodecVideoTrackRenderer( 39 | sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); 40 | array[Renderer.TYPE_AUDIO] = new MediaCodecAudioTrackRenderer(sampleSource); 41 | 42 | return array; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/Renderers/DashRenderer.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.Renderers; 2 | 3 | import android.content.Context; 4 | import android.media.MediaCodec; 5 | import android.os.Handler; 6 | 7 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 8 | import com.google.android.exoplayer.MediaCodecUtil; 9 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 10 | import com.google.android.exoplayer.SampleSource; 11 | import com.google.android.exoplayer.TrackRenderer; 12 | import com.google.android.exoplayer.chunk.ChunkSampleSource; 13 | import com.google.android.exoplayer.chunk.ChunkSource; 14 | import com.google.android.exoplayer.chunk.Format; 15 | import com.google.android.exoplayer.chunk.FormatEvaluator; 16 | import com.google.android.exoplayer.chunk.MultiTrackChunkSource; 17 | import com.google.android.exoplayer.chunk.VideoFormatSelectorUtil; 18 | import com.google.android.exoplayer.dash.DashChunkSource; 19 | import com.google.android.exoplayer.dash.mpd.AdaptationSet; 20 | import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription; 21 | import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser; 22 | import com.google.android.exoplayer.dash.mpd.Period; 23 | import com.google.android.exoplayer.dash.mpd.Representation; 24 | import com.google.android.exoplayer.dash.mpd.UtcTimingElement; 25 | import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver; 26 | import com.google.android.exoplayer.drm.UnsupportedDrmException; 27 | import com.google.android.exoplayer.text.TextTrackRenderer; 28 | import com.google.android.exoplayer.text.ttml.TtmlParser; 29 | import com.google.android.exoplayer.text.webvtt.WebvttParser; 30 | import com.google.android.exoplayer.upstream.DataSource; 31 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 32 | import com.google.android.exoplayer.util.ManifestFetcher; 33 | import com.jfowler.onramp.simpleexoplayer.MediaModels.AdaptiveMedia; 34 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 35 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.RendererListener; 36 | 37 | import java.io.IOException; 38 | import java.util.ArrayList; 39 | import java.util.List; 40 | 41 | /** 42 | * Created by jfowler on 8/28/15. 43 | */ 44 | public class DashRenderer extends Renderer implements UtcTimingElementResolver.UtcTimingCallback, ManifestFetcher.ManifestCallback { 45 | 46 | private MediaPresentationDescription manifest; 47 | private Long elapsedRealtimeOffset; 48 | 49 | public DashRenderer(Media media) { 50 | super(media); 51 | this.elapsedRealtimeOffset = 0l; 52 | this.manifestFetcher = new ManifestFetcher<>(this.media.getUri().toString(), manifestDataSource, new MediaPresentationDescriptionParser()); 53 | this.manifestFetcher.singleLoad(handler.getLooper(), this); 54 | } 55 | 56 | public MediaCodecAudioTrackRenderer getAudioTrackRenderer() { 57 | return audioTrackRenderer; 58 | } 59 | 60 | public MediaCodecVideoTrackRenderer getVideoTrackRenderer() { 61 | return videoTrackRenderer; 62 | } 63 | 64 | @Override 65 | public void prepareRender(Context context, RendererListener rendererListener) { 66 | this.rendererListener = rendererListener; 67 | Period period = manifest.periods.get(0); 68 | Handler mainHandler = handler; 69 | 70 | boolean hasContentProtection = false; 71 | int videoAdaptationSetIndex = period.getAdaptationSetIndex(AdaptationSet.TYPE_VIDEO); 72 | int audioAdaptationSetIndex = period.getAdaptationSetIndex(AdaptationSet.TYPE_AUDIO); 73 | AdaptationSet videoAdaptationSet = null; 74 | AdaptationSet audioAdaptationSet = null; 75 | if (videoAdaptationSetIndex != -1) { 76 | videoAdaptationSet = period.adaptationSets.get(videoAdaptationSetIndex); 77 | hasContentProtection |= videoAdaptationSet.hasContentProtection(); 78 | } 79 | if (audioAdaptationSetIndex != -1) { 80 | audioAdaptationSet = period.adaptationSets.get(audioAdaptationSetIndex); 81 | hasContentProtection |= audioAdaptationSet.hasContentProtection(); 82 | } 83 | 84 | //Unfortunately SimpleExoPlayer does not support DRM at this time 85 | if(hasContentProtection){ 86 | throw new IllegalStateException("SimpleExoPlayer does not support DRM"); 87 | } 88 | 89 | // Fail if we have neither video or audio. 90 | if (videoAdaptationSet == null && audioAdaptationSet == null) { 91 | throw new IllegalStateException("No video or audio adaptation sets"); 92 | } 93 | 94 | 95 | // Determine which video representations we should use for playback. 96 | int[] videoRepresentationIndices = null; 97 | if (videoAdaptationSet != null) { 98 | try { 99 | videoRepresentationIndices = VideoFormatSelectorUtil.selectVideoFormatsForDefaultDisplay( 100 | context, videoAdaptationSet.representations, null, true); 101 | } catch (MediaCodecUtil.DecoderQueryException e) { 102 | e.printStackTrace(); 103 | } 104 | } 105 | 106 | // Build the video renderer. 107 | if (videoRepresentationIndices == null || videoRepresentationIndices.length == 0) { 108 | videoTrackRenderer = null; 109 | } else { 110 | DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, media.getUserAgent()); 111 | ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, 112 | videoAdaptationSetIndex, videoRepresentationIndices, videoDataSource, 113 | new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset, 114 | mainHandler, null); 115 | ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, 116 | AdaptiveMedia.VIDEO_BUFFER_SEGMENTS * Media.BUFFER_SEGMENT_SIZE, mainHandler, null, 117 | TYPE_VIDEO); 118 | videoTrackRenderer = new MediaCodecVideoTrackRenderer(videoSampleSource, null, true, 119 | MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, null, mainHandler, null, 50); 120 | } 121 | 122 | // Build the audio chunk sources. 123 | List audioChunkSourceList = new ArrayList<>(); 124 | List audioTrackNameList = new ArrayList<>(); 125 | if (audioAdaptationSet != null) { 126 | DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, media.getUserAgent()); 127 | FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator(); 128 | List audioRepresentations = audioAdaptationSet.representations; 129 | List codecs = new ArrayList<>(); 130 | for (int i = 0; i < audioRepresentations.size(); i++) { 131 | Format format = audioRepresentations.get(i).format; 132 | audioTrackNameList.add(format.id + " (" + format.numChannels + "ch, " + 133 | format.audioSamplingRate + "Hz)"); 134 | audioChunkSourceList.add(new DashChunkSource(manifestFetcher, audioAdaptationSetIndex, 135 | new int[]{i}, audioDataSource, audioEvaluator, LIVE_EDGE_LATENCY_MS, 136 | elapsedRealtimeOffset, mainHandler, null)); 137 | codecs.add(format.codecs); 138 | } 139 | } 140 | 141 | // Build the audio renderer. 142 | final String[] audioTrackNames; 143 | final MultiTrackChunkSource audioChunkSource; 144 | if (audioChunkSourceList.isEmpty()) { 145 | audioTrackNames = null; 146 | audioChunkSource = null; 147 | audioTrackRenderer = null; 148 | } else { 149 | audioTrackNames = new String[audioTrackNameList.size()]; 150 | audioTrackNameList.toArray(audioTrackNames); 151 | audioChunkSource = new MultiTrackChunkSource(audioChunkSourceList); 152 | SampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, 153 | AdaptiveMedia.AUDIO_BUFFER_SEGMENTS * Media.BUFFER_SEGMENT_SIZE, mainHandler, null, 154 | TYPE_AUDIO); 155 | audioTrackRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, null, true, 156 | mainHandler, null); 157 | } 158 | 159 | DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, media.getUserAgent()); 160 | FormatEvaluator textEvaluator = new FormatEvaluator.FixedEvaluator(); 161 | List textChunkSourceList = new ArrayList<>(); 162 | List textTrackNameList = new ArrayList<>(); 163 | for (int i = 0; i < period.adaptationSets.size(); i++) { 164 | AdaptationSet adaptationSet = period.adaptationSets.get(i); 165 | if (adaptationSet.type == AdaptationSet.TYPE_TEXT) { 166 | List representations = adaptationSet.representations; 167 | for (int j = 0; j < representations.size(); j++) { 168 | Representation representation = representations.get(j); 169 | textTrackNameList.add(representation.format.id); 170 | textChunkSourceList.add(new DashChunkSource(manifestFetcher, i, new int[]{j}, 171 | textDataSource, textEvaluator, LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset, 172 | mainHandler, null)); 173 | } 174 | } 175 | } 176 | 177 | // Build the text renderers 178 | final String[] textTrackNames; 179 | final MultiTrackChunkSource textChunkSource; 180 | if (textChunkSourceList.isEmpty()) { 181 | textTrackNames = null; 182 | textChunkSource = null; 183 | this.textTrackRenderer = null; 184 | } else { 185 | textTrackNames = new String[textTrackNameList.size()]; 186 | textTrackNameList.toArray(textTrackNames); 187 | textChunkSource = new MultiTrackChunkSource(textChunkSourceList); 188 | SampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, 189 | TEXT_BUFFER_SEGMENTS * Media.BUFFER_SEGMENT_SIZE, mainHandler, null, 190 | TYPE_TEXT); 191 | this.textTrackRenderer = new TextTrackRenderer(textSampleSource, null, mainHandler.getLooper(), 192 | new TtmlParser(), new WebvttParser()); 193 | } 194 | } 195 | 196 | @Override 197 | public void onTimestampResolved(UtcTimingElement utcTimingElement, long l) { 198 | this.elapsedRealtimeOffset = l; 199 | prepareRender(media.getContext(), rendererListener); 200 | } 201 | 202 | @Override 203 | public void onTimestampError(UtcTimingElement utcTimingElement, IOException e) { 204 | e.printStackTrace(); 205 | } 206 | 207 | @Override 208 | public void onSingleManifest(Object o) { 209 | if(o instanceof MediaPresentationDescription) { 210 | this.manifest = (MediaPresentationDescription) o; 211 | if (manifest.dynamic && manifest.utcTiming != null) { 212 | UtcTimingElementResolver.resolveTimingElement(manifestDataSource, manifest.utcTiming, 213 | manifestFetcher.getManifestLoadCompleteTimestamp(), this); 214 | } else { 215 | prepareRender(media.getContext(), rendererListener); 216 | TrackRenderer[] array = new TrackRenderer[3]; 217 | array[TYPE_VIDEO] = videoTrackRenderer; 218 | array[TYPE_AUDIO] = audioTrackRenderer; 219 | array[TYPE_TEXT] = textTrackRenderer; 220 | rendererListener.onPrepared(array); 221 | } 222 | }else{ 223 | throw new IllegalArgumentException("onSingleManifest is not passing a MediaPresentationDescription"); 224 | } 225 | } 226 | 227 | @Override 228 | public void onSingleManifestError(IOException e) { 229 | e.printStackTrace(); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/Renderers/HlsRenderer.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.Renderers; 2 | 3 | import android.content.Context; 4 | import android.media.MediaCodec; 5 | 6 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 7 | import com.google.android.exoplayer.MediaCodecUtil; 8 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 9 | import com.google.android.exoplayer.TrackRenderer; 10 | import com.google.android.exoplayer.chunk.VideoFormatSelectorUtil; 11 | import com.google.android.exoplayer.hls.HlsChunkSource; 12 | import com.google.android.exoplayer.hls.HlsMasterPlaylist; 13 | import com.google.android.exoplayer.hls.HlsPlaylist; 14 | import com.google.android.exoplayer.hls.HlsPlaylistParser; 15 | import com.google.android.exoplayer.hls.HlsSampleSource; 16 | import com.google.android.exoplayer.upstream.DataSource; 17 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 18 | import com.google.android.exoplayer.util.ManifestFetcher; 19 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 20 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.RendererListener; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * Created by jfowler on 8/28/15. 26 | */ 27 | public class HlsRenderer extends Renderer implements ManifestFetcher.ManifestCallback{ 28 | 29 | private static final int BUFFER_SEGMENT_SIZE = 256 * 1024; 30 | private static final int BUFFER_SEGMENTS = 64; 31 | 32 | private HlsPlaylist manifest; 33 | 34 | public HlsRenderer(Media media) { 35 | super(media); 36 | HlsPlaylistParser parser = new HlsPlaylistParser(); 37 | this.manifestFetcher = new ManifestFetcher(media.getUri().toString(), manifestDataSource, parser); 38 | this.manifestFetcher.singleLoad(handler.getLooper(), this); 39 | } 40 | 41 | @Override 42 | public void prepareRender(Context context, RendererListener rendererListener) { 43 | this.rendererListener = rendererListener; 44 | 45 | int[] variantIndices = null; 46 | if(manifest instanceof HlsMasterPlaylist) { 47 | try { 48 | variantIndices = VideoFormatSelectorUtil.selectVideoFormatsForDefaultDisplay( 49 | context, ((HlsMasterPlaylist)manifest).variants, null, false); 50 | } catch (MediaCodecUtil.DecoderQueryException e) { 51 | e.printStackTrace(); 52 | return; 53 | } 54 | 55 | if (variantIndices.length == 0) { 56 | throw new IllegalStateException("No variants selected."); 57 | } 58 | } 59 | 60 | DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, media.getUserAgent()); 61 | HlsChunkSource chunkSource = new HlsChunkSource(dataSource, media.getUri().toString(), manifest, bandwidthMeter, 62 | variantIndices, HlsChunkSource.ADAPTIVE_MODE_SPLICE, null); 63 | HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl, 64 | BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, this.handler, null, TYPE_VIDEO); 65 | this.videoTrackRenderer = new MediaCodecVideoTrackRenderer(sampleSource, 66 | MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, this.handler, null, 50); 67 | this.audioTrackRenderer = new MediaCodecAudioTrackRenderer(sampleSource); 68 | } 69 | 70 | 71 | @Override 72 | public void onSingleManifest(HlsPlaylist hlsPlaylist) { 73 | this.manifest = hlsPlaylist; 74 | prepareRender(media.getContext(), rendererListener); 75 | TrackRenderer[] array = new TrackRenderer[3]; 76 | array[TYPE_VIDEO] = videoTrackRenderer; 77 | array[TYPE_AUDIO] = audioTrackRenderer; 78 | array[TYPE_TEXT] = textTrackRenderer; 79 | rendererListener.onPrepared(array); 80 | } 81 | 82 | @Override 83 | public void onSingleManifestError(IOException e) { 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/Renderers/Renderer.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.Renderers; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | 6 | import com.google.android.exoplayer.DefaultLoadControl; 7 | import com.google.android.exoplayer.LoadControl; 8 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 9 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 10 | import com.google.android.exoplayer.text.TextTrackRenderer; 11 | import com.google.android.exoplayer.upstream.BandwidthMeter; 12 | import com.google.android.exoplayer.upstream.DefaultAllocator; 13 | import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; 14 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 15 | import com.google.android.exoplayer.util.ManifestFetcher; 16 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 17 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.RendererListener; 18 | 19 | /** 20 | * Created by jfowler on 8/31/15. 21 | */ 22 | public abstract class Renderer{ 23 | 24 | protected static final int LIVE_EDGE_LATENCY_MS = 30000; 25 | protected static final int TEXT_BUFFER_SEGMENTS = 2; 26 | public static final int TYPE_VIDEO = 0; 27 | public static final int TYPE_AUDIO = 1; 28 | public static final int TYPE_TEXT = 2; 29 | public static final int TYPE_METADATA = 3; 30 | 31 | protected Media media; 32 | protected BandwidthMeter bandwidthMeter; 33 | protected ManifestFetcher manifestFetcher; 34 | protected LoadControl loadControl; 35 | protected DefaultUriDataSource manifestDataSource; 36 | protected Handler handler; 37 | protected MediaCodecVideoTrackRenderer videoTrackRenderer; 38 | protected MediaCodecAudioTrackRenderer audioTrackRenderer; 39 | protected TextTrackRenderer textTrackRenderer; 40 | protected RendererListener rendererListener; 41 | 42 | public Renderer(Media media){ 43 | this.media = media; 44 | this.handler = new Handler(); 45 | this.loadControl = new DefaultLoadControl(new DefaultAllocator(Media.BUFFER_SEGMENT_SIZE)); 46 | this.bandwidthMeter = new DefaultBandwidthMeter(); 47 | this.manifestDataSource = new DefaultUriDataSource(this.media.getContext(), this.media.getUserAgent()); 48 | this.rendererListener = (RendererListener) media; 49 | } 50 | 51 | public MediaCodecAudioTrackRenderer getAudioTrackRenderer(){ 52 | return audioTrackRenderer; 53 | } 54 | 55 | public MediaCodecVideoTrackRenderer getVideoTrackRenderer(){ 56 | return videoTrackRenderer; 57 | } 58 | 59 | public abstract void prepareRender(Context context, RendererListener rendererListener); 60 | } 61 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/Renderers/RendererInterfaces/MediaListener.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces; 2 | 3 | 4 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 5 | 6 | /** 7 | * Created by jfowler on 8/28/15. 8 | */ 9 | public interface MediaListener { 10 | void mediaPrepared(Media media); 11 | } 12 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/Renderers/RendererInterfaces/RendererListener.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces; 2 | 3 | import com.google.android.exoplayer.TrackRenderer; 4 | 5 | /** 6 | * Created by jfowler on 8/28/15. 7 | */ 8 | public interface RendererListener { 9 | void onPrepared(TrackRenderer[] renderers); 10 | } 11 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/Renderers/SmoothStreamingRenderer.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.Renderers; 2 | 3 | import android.content.Context; 4 | import android.media.MediaCodec; 5 | import android.os.Handler; 6 | 7 | import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 8 | import com.google.android.exoplayer.MediaCodecUtil; 9 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 10 | import com.google.android.exoplayer.TrackRenderer; 11 | import com.google.android.exoplayer.chunk.ChunkSampleSource; 12 | import com.google.android.exoplayer.chunk.ChunkSource; 13 | import com.google.android.exoplayer.chunk.FormatEvaluator; 14 | import com.google.android.exoplayer.chunk.MultiTrackChunkSource; 15 | import com.google.android.exoplayer.chunk.VideoFormatSelectorUtil; 16 | import com.google.android.exoplayer.smoothstreaming.SmoothStreamingChunkSource; 17 | import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest; 18 | import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser; 19 | import com.google.android.exoplayer.text.TextTrackRenderer; 20 | import com.google.android.exoplayer.text.ttml.TtmlParser; 21 | import com.google.android.exoplayer.upstream.DataSource; 22 | import com.google.android.exoplayer.upstream.DefaultUriDataSource; 23 | import com.google.android.exoplayer.util.ManifestFetcher; 24 | import com.google.android.exoplayer.util.Util; 25 | import com.jfowler.onramp.simpleexoplayer.MediaModels.AdaptiveMedia; 26 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 27 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.RendererListener; 28 | 29 | import java.io.IOException; 30 | import java.util.Arrays; 31 | 32 | /** 33 | * Created by jfowler on 8/28/15. 34 | */ 35 | public class SmoothStreamingRenderer extends Renderer implements ManifestFetcher.ManifestCallback { 36 | 37 | private SmoothStreamingManifest manifest; 38 | 39 | public SmoothStreamingRenderer(Media media) { 40 | super(media); 41 | String url = Util.toLowerInvariant(this.media.getUri().toString()).endsWith("/manifest") ? this.media.getUri().toString() : this.media.getUri().toString() + "/Manifest"; 42 | this.manifestFetcher = new ManifestFetcher<>(url, manifestDataSource, new SmoothStreamingManifestParser()); 43 | this.manifestFetcher.singleLoad(handler.getLooper(), this); 44 | } 45 | 46 | @Override 47 | public void prepareRender(Context context, RendererListener rendererListener) { 48 | this.rendererListener = rendererListener; 49 | Handler mainHandler = handler; 50 | 51 | if(manifest.protectionElement != null){ 52 | throw new IllegalStateException("SimpleExoPlayer does not support DRM protected content"); 53 | } 54 | 55 | // Obtain stream elements for playback. 56 | int audioStreamElementCount = 0; 57 | int textStreamElementCount = 0; 58 | int videoStreamElementIndex = -1; 59 | for (int i = 0; i < manifest.streamElements.length; i++) { 60 | if (manifest.streamElements[i].type == SmoothStreamingManifest.StreamElement.TYPE_AUDIO) { 61 | audioStreamElementCount++; 62 | } else if (manifest.streamElements[i].type == SmoothStreamingManifest.StreamElement.TYPE_TEXT) { 63 | textStreamElementCount++; 64 | } else if (videoStreamElementIndex == -1 65 | && manifest.streamElements[i].type == SmoothStreamingManifest.StreamElement.TYPE_VIDEO) { 66 | videoStreamElementIndex = i; 67 | } 68 | } 69 | 70 | // Determine which video tracks we should use for playback. 71 | int[] videoTrackIndices = null; 72 | if (videoStreamElementIndex != -1) { 73 | try { 74 | videoTrackIndices = VideoFormatSelectorUtil.selectVideoFormatsForDefaultDisplay(context, 75 | Arrays.asList(manifest.streamElements[videoStreamElementIndex].tracks), null, false); 76 | } catch (MediaCodecUtil.DecoderQueryException e) { 77 | e.printStackTrace(); 78 | return; 79 | } 80 | } 81 | 82 | // Build the video renderer. 83 | if (videoTrackIndices == null || videoTrackIndices.length == 0) { 84 | this.videoTrackRenderer = null; 85 | } else { 86 | DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, media.getUserAgent()); 87 | ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher, 88 | videoStreamElementIndex, videoTrackIndices, videoDataSource, 89 | new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS); 90 | ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, 91 | AdaptiveMedia.VIDEO_BUFFER_SEGMENTS * Media.BUFFER_SEGMENT_SIZE, mainHandler, null, 92 | TYPE_VIDEO); 93 | this.videoTrackRenderer = new MediaCodecVideoTrackRenderer(videoSampleSource, null, true, 94 | MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, null, mainHandler, null, 50); 95 | } 96 | 97 | // Build the audio renderer. 98 | final String[] audioTrackNames; 99 | final MultiTrackChunkSource audioChunkSource; 100 | if (audioStreamElementCount == 0) { 101 | audioTrackNames = null; 102 | audioChunkSource = null; 103 | this.audioTrackRenderer = null; 104 | } else { 105 | audioTrackNames = new String[audioStreamElementCount]; 106 | ChunkSource[] audioChunkSources = new ChunkSource[audioStreamElementCount]; 107 | DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, media.getUserAgent()); 108 | FormatEvaluator audioFormatEvaluator = new FormatEvaluator.FixedEvaluator(); 109 | audioStreamElementCount = 0; 110 | for (int i = 0; i < manifest.streamElements.length; i++) { 111 | if (manifest.streamElements[i].type == TYPE_AUDIO) { 112 | audioTrackNames[audioStreamElementCount] = manifest.streamElements[i].name; 113 | audioChunkSources[audioStreamElementCount] = new SmoothStreamingChunkSource( 114 | manifestFetcher, i, new int[] {0}, audioDataSource, audioFormatEvaluator, 115 | LIVE_EDGE_LATENCY_MS); 116 | audioStreamElementCount++; 117 | } 118 | } 119 | audioChunkSource = new MultiTrackChunkSource(audioChunkSources); 120 | ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, 121 | AdaptiveMedia.AUDIO_BUFFER_SEGMENTS * Media.BUFFER_SEGMENT_SIZE, mainHandler, null, 122 | TYPE_AUDIO); 123 | this.audioTrackRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, null, true, 124 | mainHandler, null); 125 | } 126 | 127 | // Build the text renderer. 128 | final String[] textTrackNames; 129 | final MultiTrackChunkSource textChunkSource; 130 | if (textStreamElementCount == 0) { 131 | textTrackNames = null; 132 | textChunkSource = null; 133 | this.textTrackRenderer = null; 134 | } else { 135 | textTrackNames = new String[textStreamElementCount]; 136 | ChunkSource[] textChunkSources = new ChunkSource[textStreamElementCount]; 137 | DataSource ttmlDataSource = new DefaultUriDataSource(context, bandwidthMeter, media.getUserAgent()); 138 | FormatEvaluator ttmlFormatEvaluator = new FormatEvaluator.FixedEvaluator(); 139 | textStreamElementCount = 0; 140 | for (int i = 0; i < manifest.streamElements.length; i++) { 141 | if (manifest.streamElements[i].type == SmoothStreamingManifest.StreamElement.TYPE_TEXT) { 142 | textTrackNames[textStreamElementCount] = manifest.streamElements[i].language; 143 | textChunkSources[textStreamElementCount] = new SmoothStreamingChunkSource( 144 | manifestFetcher, i, new int[] {0}, ttmlDataSource, ttmlFormatEvaluator, 145 | LIVE_EDGE_LATENCY_MS); 146 | textStreamElementCount++; 147 | } 148 | } 149 | textChunkSource = new MultiTrackChunkSource(textChunkSources); 150 | ChunkSampleSource ttmlSampleSource = new ChunkSampleSource(textChunkSource, loadControl, 151 | TEXT_BUFFER_SEGMENTS * Media.BUFFER_SEGMENT_SIZE, mainHandler, null, 152 | TYPE_TEXT); 153 | this.textTrackRenderer = new TextTrackRenderer(ttmlSampleSource, null, mainHandler.getLooper(), 154 | new TtmlParser()); 155 | } 156 | 157 | } 158 | 159 | @Override 160 | public void onSingleManifest(SmoothStreamingManifest smoothStreamingManifest) { 161 | this.manifest = smoothStreamingManifest; 162 | prepareRender(media.getContext(), rendererListener); 163 | TrackRenderer[] array = new TrackRenderer[3]; 164 | array[TYPE_VIDEO] = videoTrackRenderer; 165 | array[TYPE_AUDIO] = audioTrackRenderer; 166 | array[TYPE_TEXT] = textTrackRenderer; 167 | rendererListener.onPrepared(array); 168 | } 169 | 170 | @Override 171 | public void onSingleManifestError(IOException e) { 172 | 173 | } 174 | } 175 | 176 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/java/com/jfowler/onramp/simpleexoplayer/SimpleExoPlayer.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer; 2 | 3 | import android.content.Context; 4 | import android.view.Surface; 5 | import android.widget.MediaController; 6 | 7 | import com.google.android.exoplayer.ExoPlaybackException; 8 | import com.google.android.exoplayer.ExoPlayer; 9 | import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 10 | import com.google.android.exoplayer.TrackRenderer; 11 | import com.google.android.exoplayer.util.PlayerControl; 12 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 13 | import com.jfowler.onramp.simpleexoplayer.Renderers.Renderer; 14 | 15 | /** 16 | * Created by jfowler on 8/27/15. 17 | */ 18 | public class SimpleExoPlayer { 19 | 20 | private static int NUM_RENDERERS = 3; 21 | 22 | private ExoPlayer exoPlayer; 23 | private MediaController.MediaPlayerControl mediaPlayerControl; 24 | 25 | public SimpleExoPlayer(){ 26 | setupMediaPlayer(); 27 | } 28 | 29 | public SimpleExoPlayer(ExoPlayer exoPlayer){ 30 | this.exoPlayer = exoPlayer; 31 | setupMediaPlayer(); 32 | } 33 | 34 | public void playMedia(Context context, ExoPlayer.Listener listener, Surface surface, Media... media){ 35 | setPlayerListener(listener); 36 | for(Media m : media) { 37 | TrackRenderer[] renders = m.buildRenderer(context); 38 | if(surface != null){ 39 | setVideoSurface(surface, (MediaCodecVideoTrackRenderer) renders[Renderer.TYPE_VIDEO]); 40 | exoPlayer.prepare(renders[0], renders[1]); 41 | }else { 42 | if(renders.length > 1){ 43 | exoPlayer.prepare(renders[1]); 44 | }else { 45 | exoPlayer.prepare(renders); 46 | } 47 | } 48 | } 49 | mediaPlayerControl.start(); 50 | } 51 | 52 | public void setVideoSurface(Surface surface, MediaCodecVideoTrackRenderer videoRenderer){ 53 | pushSurface(false, surface, videoRenderer); 54 | } 55 | 56 | private void pushSurface(boolean blockForSurfacePush, Surface surface, MediaCodecVideoTrackRenderer videoRenderer) { 57 | if (videoRenderer == null) { 58 | return; 59 | } 60 | 61 | if (blockForSurfacePush) { 62 | exoPlayer.blockingSendMessage( 63 | videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); 64 | } else { 65 | exoPlayer.sendMessage( 66 | videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); 67 | } 68 | } 69 | 70 | private void setPlayerListener(ExoPlayer.Listener listener){ 71 | if(listener == null){ 72 | listener = new ExoPlayer.Listener() { 73 | @Override 74 | public void onPlayerStateChanged(boolean b, int i) { 75 | 76 | } 77 | 78 | @Override 79 | public void onPlayWhenReadyCommitted() { 80 | 81 | } 82 | 83 | @Override 84 | public void onPlayerError(ExoPlaybackException e) { 85 | reinstantiateExoPlayer(); 86 | } 87 | }; 88 | } 89 | exoPlayer.removeListener(listener); 90 | exoPlayer.addListener(listener); 91 | } 92 | 93 | public void pauseMedia(){ 94 | if(mediaPlayerControl.canPause()) { 95 | mediaPlayerControl.pause(); 96 | } 97 | } 98 | 99 | public void stopMedia(){ 100 | if(mediaPlayerControl.canPause()){ 101 | mediaPlayerControl.pause(); 102 | mediaPlayerControl.seekTo(0); 103 | } 104 | exoPlayer.stop(); 105 | } 106 | 107 | public boolean isPlaying() { 108 | boolean isPlaying = false; 109 | if(exoPlayer != null) { 110 | if (mediaPlayerControl == null) { 111 | mediaPlayerControl = new PlayerControl(exoPlayer); 112 | } 113 | isPlaying = mediaPlayerControl.isPlaying(); 114 | } 115 | return isPlaying; 116 | } 117 | 118 | public MediaController.MediaPlayerControl getMediaPlayerControl(){ 119 | return mediaPlayerControl; 120 | } 121 | 122 | public void reinstantiateExoPlayer(){ 123 | destroyMediaPlayer(); 124 | setupMediaPlayer(); 125 | } 126 | 127 | private void setupMediaPlayer() { 128 | if(exoPlayer == null){ 129 | exoPlayer = ExoPlayer.Factory.newInstance(NUM_RENDERERS); 130 | } 131 | exoPlayer.stop(); 132 | mediaPlayerControl = new PlayerControl(exoPlayer); 133 | } 134 | 135 | private void destroyMediaPlayer() { 136 | if (exoPlayer != null) { 137 | mediaPlayerControl.pause(); 138 | mediaPlayerControl = null; 139 | exoPlayer.stop(); 140 | exoPlayer.release(); 141 | exoPlayer = null; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /SimpleExoPlayer/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SimpleExoPlayer 3 | 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.idea 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.0" 6 | defaultConfig { 7 | applicationId 'com.srmarlins.simpleexoplayer.demo' 8 | minSdkVersion 16 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | productFlavors { 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(include: ['*.jar'], dir: 'libs') 25 | compile project(':simpleexoplayer') 26 | compile 'com.android.support:appcompat-v7:23.0.0' 27 | } 28 | -------------------------------------------------------------------------------- /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 /Users/jfowler/Library/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/com/jfowler/onramp/simpleexoplayer/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer; 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 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/jfowler/onramp/simpleexoplayer/Utils/MediaFactory.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.Utils; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.jfowler.onramp.simpleexoplayer.MediaModels.AudioMedia; 7 | import com.jfowler.onramp.simpleexoplayer.MediaModels.DashMedia; 8 | import com.jfowler.onramp.simpleexoplayer.MediaModels.HlsMedia; 9 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 10 | import com.jfowler.onramp.simpleexoplayer.MediaModels.SmoothStreamingMedia; 11 | import com.jfowler.onramp.simpleexoplayer.MediaModels.VideoMedia; 12 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.MediaListener; 13 | 14 | /** 15 | * Created by jfowler on 9/2/15. 16 | */ 17 | public class MediaFactory { 18 | 19 | public static final String MEDIA_NAME_TAG = "mediaName"; 20 | public static final String MEDIA_URI_TAG = "mediaUri"; 21 | public static final String MEDIA_TYPE_TAG = "mediaType"; 22 | public static final String STREAM_TYPE_TAG = "streamType"; 23 | 24 | public static final int STREAM_TYPE_DASH = 0; 25 | public static final int STREAM_TYPE_SS = 1; 26 | public static final int STREAM_TYPE_HLS = 2; 27 | public static final int STREAM_TYPE_STANDARD = 3; 28 | 29 | public static final int MEDIA_TYPE_VIDEO = 4; 30 | public static final int MEDIA_TYPE_AUDIO = 5; 31 | 32 | 33 | public static Media getAdaptiveMedia(Context context, MediaListener mediaListener, String uri, String userAgent, int streamType, int mediaType){ 34 | Media media = null; 35 | 36 | switch(streamType){ 37 | case STREAM_TYPE_DASH: media = new DashMedia(context, mediaListener, Uri.parse(uri), userAgent); break; 38 | case STREAM_TYPE_SS: media = new SmoothStreamingMedia(context, mediaListener, Uri.parse(uri), userAgent); break; 39 | case STREAM_TYPE_HLS: media = new HlsMedia(context, mediaListener, Uri.parse(uri), userAgent); break; 40 | case STREAM_TYPE_STANDARD: media = getStandardMedia(context, uri, userAgent, mediaType); break; 41 | } 42 | 43 | return media; 44 | } 45 | 46 | public static Media getStandardMedia(Context context, String uri, String userAgent, int mediaType){ 47 | Media media = null; 48 | 49 | switch (mediaType){ 50 | case MEDIA_TYPE_VIDEO: media = new VideoMedia(context, Uri.parse(uri), userAgent); break; 51 | case MEDIA_TYPE_AUDIO: media = new AudioMedia(context, Uri.parse(uri), userAgent); break; 52 | } 53 | 54 | return media; 55 | } 56 | 57 | public static int stringToInt(String type){ 58 | switch (type.toLowerCase()){ 59 | case "dash": return STREAM_TYPE_DASH; 60 | case "smoothstreaming": return STREAM_TYPE_SS; 61 | case "hls": return STREAM_TYPE_HLS; 62 | case "standard media": return STREAM_TYPE_STANDARD; 63 | case "video": return MEDIA_TYPE_VIDEO; 64 | case "audio": return MEDIA_TYPE_AUDIO; 65 | } 66 | return -1; 67 | } 68 | 69 | public static String intToString(int type){ 70 | switch (type){ 71 | case STREAM_TYPE_DASH: return "DASH"; 72 | case STREAM_TYPE_SS: return "SmoothStreaming"; 73 | case STREAM_TYPE_HLS: return "HLS"; 74 | case STREAM_TYPE_STANDARD: return "Standard Media"; 75 | case MEDIA_TYPE_VIDEO: return "Video"; 76 | case MEDIA_TYPE_AUDIO: return "Audio"; 77 | } 78 | return null; 79 | } 80 | 81 | public static boolean isAdaptiveMedia(int type){ 82 | boolean isAdaptive = false; 83 | switch (type){ 84 | case STREAM_TYPE_DASH: 85 | case STREAM_TYPE_SS: 86 | case STREAM_TYPE_HLS: isAdaptive = true; break; 87 | } 88 | return isAdaptive; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/jfowler/onramp/simpleexoplayer/Utils/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 com.jfowler.onramp.simpleexoplayer.Utils; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.Locale; 21 | 22 | /** 23 | * Holds statically defined sample definitions. 24 | */ 25 | public class Samples { 26 | 27 | public static class Sample { 28 | public final String name; 29 | public final String contentId; 30 | public final String uri; 31 | public final int type; 32 | 33 | public Sample(String name, String uri, int type) { 34 | this(name, name.toLowerCase(Locale.US).replaceAll("\\s", ""), uri, type); 35 | } 36 | 37 | public Sample(String name, String contentId, String uri, int type) { 38 | this.name = name; 39 | this.contentId = contentId; 40 | this.uri = uri; 41 | this.type = type; 42 | } 43 | 44 | } 45 | 46 | public static final Sample[] YOUTUBE_DASH_MP4 = new Sample[]{ 47 | new Sample("Google Glass", 48 | "http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?" 49 | + "as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" 50 | + "ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7." 51 | + "8506521BFC350652163895D4C26DEE124209AA9E&key=ik0", MediaFactory.STREAM_TYPE_DASH), 52 | new Sample("Google Play", 53 | "http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?" 54 | + "as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&" 55 | + "ipbits=0&expire=19000000000&signature=A2716F75795F5D2AF0E88962FFCD10DB79384F29." 56 | + "84308FF04844498CE6FBCE4731507882B8307798&key=ik0", MediaFactory.STREAM_TYPE_DASH), 57 | }; 58 | 59 | 60 | public static final Sample[] SMOOTHSTREAMING = new Sample[]{ 61 | new Sample("Super speed", 62 | "http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/manifest", 63 | MediaFactory.STREAM_TYPE_SS), 64 | new Sample("Big Buck Bunny Multi Res", 65 | "http://mediadl.microsoft.com/mediadl/iisnet/smoothmedia/Experience/BigBuckBunny_720p.ism/Manifest", 66 | MediaFactory.STREAM_TYPE_SS), 67 | new Sample("To The Limit", 68 | "http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest", 69 | MediaFactory.STREAM_TYPE_SS), 70 | 71 | }; 72 | 73 | public static final Sample[] SMOOTHSTREAM_AUDIO = new Sample[]{ 74 | new Sample("Taxi3", 75 | "http://playready.directtaps.net/smoothstreaming/ISMAAACLC/Taxi3_AACLC.ism/Manifest", 76 | MediaFactory.STREAM_TYPE_SS) 77 | }; 78 | 79 | 80 | public static final Sample[] HLS_VIDEO = new Sample[]{ 81 | new Sample("Apple master playlist", 82 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/" 83 | + "bipbop_4x3_variant.m3u8", MediaFactory.STREAM_TYPE_HLS), 84 | new Sample("Apple master playlist advanced", 85 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/" 86 | + "bipbop_16x9_variant.m3u8", MediaFactory.STREAM_TYPE_HLS), 87 | new Sample("Apple TS media playlist", 88 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/" 89 | + "prog_index.m3u8", MediaFactory.STREAM_TYPE_HLS), 90 | }; 91 | 92 | public static final Sample[] HLS_AUDIO = new Sample[]{ 93 | new Sample("Apple AAC media playlist", 94 | "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear0/" 95 | + "prog_index.m3u8", MediaFactory.STREAM_TYPE_HLS), 96 | }; 97 | 98 | public static final Sample[] STANDARD_VIDEO = new Sample[]{ 99 | new Sample("Dizzy", "http://html5demos.com/assets/dizzy.mp4", MediaFactory.STREAM_TYPE_STANDARD), 100 | new Sample("Android screens (Matroska)", "http://storage.googleapis.com/exoplayer-test-media-1/" 101 | + "mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv", 102 | MediaFactory.STREAM_TYPE_STANDARD), 103 | new Sample("Big Buck Bunny (MP4 Video)", 104 | "http://redirector.c.youtube.com/videoplayback?id=604ed5ce52eda7ee&itag=22&source=youtube&" 105 | + "sparams=ip,ipbits,expire,source,id&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=" 106 | + "513F28C7FDCBEC60A66C86C9A393556C99DC47FB.04C88036EEE12565A1ED864A875A58F15D8B5300" 107 | + "&key=ik0", MediaFactory.STREAM_TYPE_STANDARD), 108 | }; 109 | 110 | public static final Sample[] STANDARD_AUDIO = new Sample[]{ 111 | new Sample("Apple AAC 10s", "https://devimages.apple.com.edgekey.net/" 112 | + "streaming/examples/bipbop_4x3/gear0/fileSequence0.aac", MediaFactory.STREAM_TYPE_STANDARD), 113 | new Sample("Apple TS 10s", "https://devimages.apple.com.edgekey.net/streaming/examples/" 114 | + "bipbop_4x3/gear1/fileSequence0.ts", MediaFactory.STREAM_TYPE_STANDARD), 115 | new Sample("Google Play (MP3 Audio)", 116 | "http://storage.googleapis.com/exoplayer-test-media-0/play.mp3", MediaFactory.STREAM_TYPE_STANDARD), 117 | new Sample("WUFT Live Stream", 118 | "http://ice02.jou.ufl.edu:8000/wufthd164", MediaFactory.STREAM_TYPE_STANDARD), 119 | }; 120 | 121 | public static Sample[] getAllVideoSamples(){ 122 | ArrayList samples = new ArrayList<>(); 123 | samples.addAll(Arrays.asList(Samples.HLS_VIDEO)); 124 | samples.addAll(Arrays.asList(Samples.SMOOTHSTREAMING)); 125 | samples.addAll(Arrays.asList(Samples.YOUTUBE_DASH_MP4)); 126 | samples.addAll(Arrays.asList(Samples.STANDARD_VIDEO)); 127 | 128 | return samples.toArray(new Sample[samples.size()]); 129 | } 130 | 131 | public static Sample[] getAllVideoSamplesByType(int type){ 132 | ArrayList samples = new ArrayList<>(); 133 | for (Sample sample: getAllVideoSamples()) { 134 | if(type == sample.type){ 135 | samples.add(sample); 136 | } 137 | } 138 | 139 | return samples.toArray(new Sample[samples.size()]); 140 | } 141 | 142 | public static Sample[] getAllAudioSamples(){ 143 | ArrayList samples = new ArrayList<>(); 144 | samples.addAll(Arrays.asList(getAllVideoSamples())); 145 | samples.addAll(Arrays.asList(Samples.SMOOTHSTREAM_AUDIO)); 146 | samples.addAll(Arrays.asList(Samples.STANDARD_AUDIO)); 147 | samples.addAll(Arrays.asList(Samples.HLS_AUDIO)); 148 | 149 | return samples.toArray(new Sample[samples.size()]); 150 | } 151 | 152 | public static Sample[] getAllAudioSamplesByType(int type){ 153 | ArrayList samples = new ArrayList<>(); 154 | for (Sample sample: getAllAudioSamples()) { 155 | if(type == sample.type){ 156 | samples.add(sample); 157 | } 158 | } 159 | return samples.toArray(new Sample[samples.size()]); 160 | } 161 | 162 | private Samples() { 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/jfowler/onramp/simpleexoplayer/activities/AudioPlayerActivity.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.activities; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.LinearLayout; 8 | import android.widget.MediaController; 9 | import android.widget.TextView; 10 | 11 | import com.google.android.exoplayer.util.Util; 12 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 13 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.MediaListener; 14 | import com.jfowler.onramp.simpleexoplayer.SimpleExoPlayer; 15 | import com.jfowler.onramp.simpleexoplayer.Utils.MediaFactory; 16 | import com.jfowler.onramp.simpleexoplayerdemo.R; 17 | 18 | import java.net.MalformedURLException; 19 | import java.net.URL; 20 | 21 | public class AudioPlayerActivity extends AppCompatActivity { 22 | 23 | private static String POS_TAG = "seekPos"; 24 | 25 | /** 26 | * The SimpleExoPlayer object used to handle media 27 | */ 28 | private SimpleExoPlayer mSimpleExoPlayer; 29 | 30 | private MediaController.MediaPlayerControl mMediaPlayerControl; 31 | 32 | private MediaController mMediaController; 33 | 34 | private int mSeekPos; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_player_audio); 40 | 41 | if(savedInstanceState != null){ 42 | mSeekPos = savedInstanceState.getInt(POS_TAG, 0); 43 | } 44 | 45 | if(mSimpleExoPlayer == null) { 46 | mSimpleExoPlayer = new SimpleExoPlayer(); 47 | } 48 | 49 | TextView title = (TextView) findViewById(R.id.text_audio_title); 50 | title.setText(getIntent().getStringExtra(MediaFactory.MEDIA_NAME_TAG)); 51 | 52 | TextView url = (TextView) findViewById(R.id.text_audio_uri); 53 | String uri = getIntent().getStringExtra(MediaFactory.MEDIA_URI_TAG); 54 | url.setText(getBaseUrl(uri)); 55 | 56 | LinearLayout layout = (LinearLayout) findViewById(R.id.audioSublayout); 57 | prepareMediaController(layout); 58 | layout.setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View v) { 61 | mMediaController.show(); 62 | } 63 | }); 64 | 65 | } 66 | 67 | private String getBaseUrl(String fullUrl){ 68 | try { 69 | URL url = new URL(fullUrl); 70 | return url.getProtocol() + "://" + url.getHost(); 71 | } catch (MalformedURLException e) 72 | { 73 | return null; 74 | } 75 | } 76 | 77 | private void prepareMediaController(View view){ 78 | mMediaController = new MediaController(AudioPlayerActivity.this); 79 | //The SimpleExoPlayer object contains a MediaPlayerControl which can be used with Android's 80 | //MediaController class 81 | mMediaPlayerControl = mSimpleExoPlayer.getMediaPlayerControl(); 82 | //Associate the MediaController with the VideoView we're playing the media through 83 | mMediaController.setMediaPlayer(mMediaPlayerControl); 84 | mMediaController.setAnchorView(view); 85 | } 86 | 87 | private Media prepareMedia() throws Exception{ 88 | Bundle extras = getIntent().getExtras(); 89 | Media media = null; 90 | 91 | if(extras != null){ 92 | String uri = extras.getString(MediaFactory.MEDIA_URI_TAG); 93 | final int mediaType = extras.getInt(MediaFactory.MEDIA_TYPE_TAG); 94 | int streamType = extras.getInt(MediaFactory.STREAM_TYPE_TAG); 95 | if(MediaFactory.isAdaptiveMedia(streamType)){ 96 | media = MediaFactory.getAdaptiveMedia(this, new MediaListener() { 97 | @Override 98 | public void mediaPrepared(Media media) { 99 | mSimpleExoPlayer.playMedia(AudioPlayerActivity.this, null, null, media); 100 | mMediaPlayerControl.seekTo(mSeekPos); 101 | } 102 | }, uri, Util.getUserAgent(this, getString(R.string.app_name)), streamType, mediaType); 103 | }else{ 104 | media = MediaFactory.getStandardMedia(this, uri, Util.getUserAgent(this, getString(R.string.app_name)), mediaType); 105 | mSimpleExoPlayer.playMedia(this, null, null, media); 106 | mMediaPlayerControl.seekTo(mSeekPos); 107 | } 108 | } 109 | 110 | if(media == null){ 111 | throw new Exception("Invalid Media parameters were given"); 112 | } 113 | 114 | return media; 115 | } 116 | 117 | 118 | @Override 119 | protected void onResume() { 120 | super.onResume(); 121 | try { 122 | prepareMedia(); 123 | }catch (Exception e){ 124 | mSimpleExoPlayer.stopMedia(); 125 | Intent resultIntent = new Intent(); 126 | resultIntent.putExtra(MainActivity.ERROR_STRING_TAG, e.getMessage()); 127 | setResult(MainActivity.PLAY_MEDIA_CANCELED, resultIntent); 128 | finish(); 129 | } 130 | } 131 | 132 | @Override 133 | protected void onPause() { 134 | super.onPause(); 135 | mSeekPos = mMediaPlayerControl.getCurrentPosition(); 136 | mMediaPlayerControl.pause(); 137 | } 138 | 139 | @Override 140 | protected void onSaveInstanceState(Bundle outState) { 141 | outState.putInt(POS_TAG, mSeekPos); 142 | super.onSaveInstanceState(outState); 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/java/com/jfowler/onramp/simpleexoplayer/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.activities; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.support.v7.app.ActionBar; 6 | import android.support.v4.app.FragmentManager; 7 | import android.os.Bundle; 8 | import android.support.v4.widget.DrawerLayout; 9 | import android.widget.Toast; 10 | 11 | import com.jfowler.onramp.simpleexoplayer.Utils.MediaFactory; 12 | import com.jfowler.onramp.simpleexoplayer.fragments.MediaSelectorFragment; 13 | import com.jfowler.onramp.simpleexoplayer.fragments.NavigationDrawerFragment; 14 | import com.jfowler.onramp.simpleexoplayerdemo.R; 15 | 16 | public class MainActivity extends AppCompatActivity 17 | implements NavigationDrawerFragment.NavigationDrawerCallbacks { 18 | 19 | public static final int PLAY_MEDIA_REQUEST = 0; 20 | 21 | public static final int PLAY_MEDIA_CANCELED = -1; 22 | 23 | public static final String ERROR_STRING_TAG = "errorTag"; 24 | 25 | /** 26 | * Fragment managing the behaviors, interactions and presentation of the navigation drawer. 27 | */ 28 | private NavigationDrawerFragment mNavigationDrawerFragment; 29 | 30 | /** 31 | * Used to store the last screen title. For use in {@link #restoreActionBar()}. 32 | */ 33 | private CharSequence mTitle; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_main); 39 | 40 | mNavigationDrawerFragment = (NavigationDrawerFragment) 41 | getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); 42 | mTitle = getTitle(); 43 | 44 | // Set up the drawer. 45 | mNavigationDrawerFragment.setUp( 46 | R.id.navigation_drawer, 47 | (DrawerLayout) findViewById(R.id.drawer_layout)); 48 | } 49 | 50 | @Override 51 | public void onNavigationDrawerItemSelected(int position) { 52 | // update the main content by replacing fragments 53 | FragmentManager fragmentManager = getSupportFragmentManager(); 54 | fragmentManager.beginTransaction() 55 | .replace(R.id.container, MediaSelectorFragment.newInstance(MediaFactory.intToString(position))) 56 | .commit(); 57 | } 58 | 59 | @Override 60 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 61 | if(resultCode == PLAY_MEDIA_CANCELED){ 62 | Toast.makeText(MainActivity.this, data.getStringExtra(ERROR_STRING_TAG), Toast.LENGTH_SHORT).show(); 63 | } 64 | } 65 | 66 | public void onSectionAttached(int number) { 67 | mTitle = MediaFactory.intToString(number); 68 | } 69 | 70 | public void restoreActionBar() { 71 | ActionBar actionBar = getSupportActionBar(); 72 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 73 | actionBar.setDisplayShowTitleEnabled(true); 74 | actionBar.setTitle(mTitle); 75 | } 76 | 77 | 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/jfowler/onramp/simpleexoplayer/activities/VideoPlayerActivity.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.activities; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | import android.widget.MediaController; 9 | import android.widget.VideoView; 10 | 11 | import com.google.android.exoplayer.util.Util; 12 | import com.jfowler.onramp.simpleexoplayer.MediaModels.Media; 13 | import com.jfowler.onramp.simpleexoplayer.Renderers.RendererInterfaces.MediaListener; 14 | import com.jfowler.onramp.simpleexoplayer.SimpleExoPlayer; 15 | import com.jfowler.onramp.simpleexoplayer.Utils.MediaFactory; 16 | import com.jfowler.onramp.simpleexoplayerdemo.R; 17 | 18 | public class VideoPlayerActivity extends AppCompatActivity { 19 | 20 | private static String POS_TAG = "seekPos"; 21 | 22 | /** 23 | * The SimpleExoPlayer object used to handle media 24 | */ 25 | private SimpleExoPlayer mSimpleExoPlayer; 26 | 27 | private VideoView mVideoView; 28 | 29 | private MediaController.MediaPlayerControl mMediaPlayerControl; 30 | 31 | private MediaController mMediaController; 32 | 33 | private int mSeekPos; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_player_video); 39 | 40 | if(savedInstanceState != null){ 41 | mSeekPos = savedInstanceState.getInt(POS_TAG, 0); 42 | } 43 | 44 | if(mSimpleExoPlayer == null) { 45 | mSimpleExoPlayer = new SimpleExoPlayer(); 46 | } 47 | 48 | mVideoView = (VideoView) findViewById(R.id.videoViewSurface); 49 | 50 | prepareMediaController(); 51 | 52 | mVideoView.setOnTouchListener(new View.OnTouchListener() { 53 | @Override 54 | public boolean onTouch(View v, MotionEvent event) { 55 | if (mMediaController != null) { 56 | mMediaController.show(); 57 | } 58 | return false; 59 | } 60 | }); 61 | 62 | } 63 | 64 | private void prepareMediaController(){ 65 | mMediaController = new MediaController(VideoPlayerActivity.this); 66 | //The SimpleExoPlayer object contains a MediaPlayerControl which can be used with Android's 67 | //MediaController class 68 | mMediaPlayerControl = mSimpleExoPlayer.getMediaPlayerControl(); 69 | //Associate the MediaController with the VideoView we're playing the media through 70 | mMediaController.setMediaPlayer(mMediaPlayerControl); 71 | mMediaController.setAnchorView(mVideoView); 72 | mVideoView.setMediaController(mMediaController); 73 | } 74 | 75 | private Media prepareMedia() throws Exception{ 76 | Bundle extras = getIntent().getExtras(); 77 | Media media = null; 78 | if(extras != null){ 79 | String uri = extras.getString(MediaFactory.MEDIA_URI_TAG); 80 | final int mediaType = extras.getInt(MediaFactory.MEDIA_TYPE_TAG); 81 | int streamType = extras.getInt(MediaFactory.STREAM_TYPE_TAG); 82 | if(MediaFactory.isAdaptiveMedia(streamType)){ 83 | media = MediaFactory.getAdaptiveMedia(this, new MediaListener() { 84 | @Override 85 | public void mediaPrepared(Media media) { 86 | if(mediaType == MediaFactory.MEDIA_TYPE_VIDEO) { 87 | mSimpleExoPlayer.playMedia(VideoPlayerActivity.this, null, mVideoView.getHolder().getSurface(), media); 88 | }else{ 89 | mSimpleExoPlayer.playMedia(VideoPlayerActivity.this, null, null, media); 90 | } 91 | mMediaPlayerControl.seekTo(mSeekPos); 92 | } 93 | }, uri, Util.getUserAgent(this, getString(R.string.app_name)), streamType, mediaType); 94 | }else{ 95 | media = MediaFactory.getStandardMedia(this, uri, Util.getUserAgent(this, getString(R.string.app_name)), mediaType); 96 | if(mediaType == MediaFactory.MEDIA_TYPE_VIDEO){ 97 | mSimpleExoPlayer.playMedia(this, null, mVideoView.getHolder().getSurface(), media); 98 | }else{ 99 | mSimpleExoPlayer.playMedia(this, null, null, media); 100 | } 101 | 102 | mMediaPlayerControl.seekTo(mSeekPos); 103 | } 104 | } 105 | 106 | if(media == null){ 107 | throw new Exception("Invalid Media parameters were given"); 108 | } 109 | 110 | return media; 111 | } 112 | 113 | 114 | @Override 115 | protected void onResume() { 116 | super.onResume(); 117 | try { 118 | prepareMedia(); 119 | }catch (Exception e){ 120 | mSimpleExoPlayer.stopMedia(); 121 | Intent resultIntent = new Intent(); 122 | resultIntent.putExtra(MainActivity.ERROR_STRING_TAG, e.getMessage()); 123 | setResult(MainActivity.PLAY_MEDIA_CANCELED, resultIntent); 124 | finish(); 125 | } 126 | } 127 | 128 | @Override 129 | protected void onPause() { 130 | super.onPause(); 131 | mSeekPos = mMediaPlayerControl.getCurrentPosition(); 132 | mMediaPlayerControl.pause(); 133 | } 134 | 135 | @Override 136 | protected void onSaveInstanceState(Bundle outState) { 137 | outState.putInt(POS_TAG, mSeekPos); 138 | super.onSaveInstanceState(outState); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/com/jfowler/onramp/simpleexoplayer/fragments/MediaSelectorFragment.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.fragments; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.support.v4.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.AdapterView; 12 | import android.widget.BaseAdapter; 13 | import android.widget.ListView; 14 | import android.widget.TabHost; 15 | import android.widget.TextView; 16 | 17 | import com.jfowler.onramp.simpleexoplayer.activities.AudioPlayerActivity; 18 | import com.jfowler.onramp.simpleexoplayer.activities.MainActivity; 19 | import com.jfowler.onramp.simpleexoplayer.Utils.Samples; 20 | import com.jfowler.onramp.simpleexoplayer.Utils.MediaFactory; 21 | import com.jfowler.onramp.simpleexoplayer.activities.VideoPlayerActivity; 22 | import com.jfowler.onramp.simpleexoplayerdemo.R; 23 | 24 | 25 | /** 26 | * Created by jfowler on 9/1/15. 27 | */ 28 | public class MediaSelectorFragment extends Fragment{ 29 | 30 | private static final String TAG = "MediaSelectorFragment"; 31 | 32 | private TabHost mTabHost; 33 | private Context mContext; 34 | 35 | public static MediaSelectorFragment newInstance(String mediaType) { 36 | MediaSelectorFragment frag = new MediaSelectorFragment(); 37 | Bundle args = new Bundle(); 38 | args.putInt(MediaFactory.MEDIA_TYPE_TAG, MediaFactory.stringToInt(mediaType)); 39 | frag.setArguments(args); 40 | return frag; 41 | } 42 | 43 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 44 | Bundle savedInstanceState) { 45 | 46 | View rootView = inflater.inflate(R.layout.fragment_media_selector, container, false); 47 | 48 | Bundle args = getArguments(); 49 | int mediaType = args.getInt(MediaFactory.MEDIA_TYPE_TAG); 50 | 51 | mTabHost = (TabHost)rootView.findViewById(R.id.tabHost); 52 | 53 | mTabHost.setup(); 54 | 55 | TabHost.TabSpec specVideo = mTabHost.newTabSpec(getResources().getString(R.string.tab1_name)); 56 | 57 | specVideo.setContent(R.id.listview_video); 58 | specVideo.setIndicator(getResources().getString(R.string.tab1_name)); 59 | 60 | mTabHost.addTab(specVideo); 61 | TabHost.TabSpec specAudio = mTabHost.newTabSpec(getResources().getString(R.string.tab2_name)); 62 | specAudio.setContent(R.id.listview_audio); 63 | specAudio.setIndicator(getResources().getString(R.string.tab2_name)); 64 | 65 | mTabHost.addTab(specAudio); 66 | 67 | final Samples.Sample[] videoSamples = Samples.getAllVideoSamplesByType(mediaType); 68 | MediaAdapter videoAdapter = new MediaAdapter(mContext, videoSamples); 69 | 70 | ListView videoList = (ListView) rootView.findViewById(R.id.listview_video); 71 | videoList.setAdapter(videoAdapter); 72 | videoList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 73 | @Override 74 | public void onItemClick(AdapterView parent, View view, int position, long id) { 75 | startMediaIntent(videoSamples[position], MediaFactory.MEDIA_TYPE_VIDEO); 76 | } 77 | }); 78 | 79 | ListView audioList = (ListView) rootView.findViewById(R.id.listview_audio); 80 | final Samples.Sample[] audioSamples = Samples.getAllAudioSamplesByType(mediaType); 81 | MediaAdapter audioAdapter = new MediaAdapter(mContext, audioSamples); 82 | audioList.setAdapter(audioAdapter); 83 | audioList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 84 | @Override 85 | public void onItemClick(AdapterView parent, View view, int position, long id) { 86 | startMediaIntent(audioSamples[position], MediaFactory.MEDIA_TYPE_AUDIO); 87 | } 88 | }); 89 | 90 | 91 | return rootView; 92 | } 93 | 94 | public void startMediaIntent(Samples.Sample sample, int mediaType){ 95 | Intent mediaIntent = new Intent(mContext, (mediaType == MediaFactory.MEDIA_TYPE_VIDEO) ? VideoPlayerActivity.class:AudioPlayerActivity.class); 96 | mediaIntent.putExtra(MediaFactory.MEDIA_NAME_TAG, sample.name); 97 | mediaIntent.putExtra(MediaFactory.MEDIA_URI_TAG, sample.uri); 98 | mediaIntent.putExtra(MediaFactory.MEDIA_TYPE_TAG, mediaType); 99 | mediaIntent.putExtra(MediaFactory.STREAM_TYPE_TAG, sample.type); 100 | startActivityForResult(mediaIntent, MainActivity.PLAY_MEDIA_REQUEST); 101 | } 102 | 103 | @Override 104 | public void onAttach(Context context) { 105 | super.onAttach(context); 106 | mContext = context; 107 | } 108 | 109 | public class MediaAdapter extends BaseAdapter{ 110 | 111 | private Samples.Sample[] mSamples; 112 | private Context mContext; 113 | 114 | public MediaAdapter(Context context, Samples.Sample[] samples){ 115 | mContext = context; 116 | mSamples = samples; 117 | } 118 | 119 | @Override 120 | public int getCount() { 121 | return mSamples.length; 122 | } 123 | 124 | @Override 125 | public Object getItem(int position) { 126 | return position; 127 | } 128 | 129 | @Override 130 | public long getItemId(int position) { 131 | return position; 132 | } 133 | 134 | @Override 135 | public View getView(int position, View convertView, ViewGroup parent) { 136 | 137 | ViewHolder holder = new ViewHolder(); 138 | Samples.Sample currentSample = mSamples[position]; 139 | 140 | if (convertView == null) { 141 | LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); 142 | convertView = inflater.inflate(R.layout.listview_media_item, parent, false); 143 | 144 | holder.mMediaName = (TextView) convertView.findViewById(R.id.text_media_title); 145 | holder.mMediaType = (TextView) convertView.findViewById(R.id.text_media_type); 146 | convertView.setTag(holder); 147 | } else { 148 | holder = (ViewHolder) convertView.getTag(); 149 | } 150 | 151 | holder.mMediaName.setText(currentSample.name); 152 | holder.mMediaType.setText(MediaFactory.intToString(currentSample.type)); 153 | 154 | 155 | return convertView; 156 | } 157 | 158 | } 159 | 160 | static class ViewHolder{ 161 | TextView mMediaName; 162 | TextView mMediaType; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/java/com/jfowler/onramp/simpleexoplayer/fragments/NavigationDrawerFragment.java: -------------------------------------------------------------------------------- 1 | package com.jfowler.onramp.simpleexoplayer.fragments; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.app.Activity; 5 | import android.support.v7.app.ActionBar; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.app.ActionBarDrawerToggle; 8 | import android.support.v4.view.GravityCompat; 9 | import android.support.v4.widget.DrawerLayout; 10 | import android.content.SharedPreferences; 11 | import android.content.res.Configuration; 12 | import android.os.Bundle; 13 | import android.preference.PreferenceManager; 14 | import android.view.LayoutInflater; 15 | import android.view.MenuItem; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.widget.AdapterView; 19 | import android.widget.ArrayAdapter; 20 | import android.widget.ListView; 21 | 22 | import com.jfowler.onramp.simpleexoplayerdemo.R; 23 | 24 | /** 25 | * Fragment used for managing interactions for and presentation of a navigation drawer. 26 | * See the 27 | * design guidelines for a complete explanation of the behaviors implemented here. 28 | */ 29 | public class NavigationDrawerFragment extends Fragment { 30 | 31 | /** 32 | * Remember the position of the selected item. 33 | */ 34 | private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; 35 | 36 | /** 37 | * Per the design guidelines, you should show the drawer on launch until the user manually 38 | * expands it. This shared preference tracks this. 39 | */ 40 | private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; 41 | 42 | /** 43 | * A pointer to the current callbacks instance (the Activity). 44 | */ 45 | private NavigationDrawerCallbacks mCallbacks; 46 | 47 | /** 48 | * Helper component that ties the action bar to the navigation drawer. 49 | */ 50 | private ActionBarDrawerToggle mDrawerToggle; 51 | 52 | private DrawerLayout mDrawerLayout; 53 | private ListView mDrawerListView; 54 | private View mFragmentContainerView; 55 | 56 | private int mCurrentSelectedPosition = 0; 57 | private boolean mFromSavedInstanceState; 58 | private boolean mUserLearnedDrawer; 59 | 60 | public NavigationDrawerFragment() { 61 | } 62 | 63 | @Override 64 | public void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | 67 | // Read in the flag indicating whether or not the user has demonstrated awareness of the 68 | // drawer. See PREF_USER_LEARNED_DRAWER for details. 69 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); 70 | mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); 71 | 72 | if (savedInstanceState != null) { 73 | mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); 74 | mFromSavedInstanceState = true; 75 | } 76 | 77 | // Select either the default item (0) or the last selected item. 78 | selectItem(mCurrentSelectedPosition); 79 | } 80 | 81 | @Override 82 | public void onActivityCreated(Bundle savedInstanceState) { 83 | super.onActivityCreated(savedInstanceState); 84 | // Indicate that this fragment would like to influence the set of actions in the action bar. 85 | setHasOptionsMenu(true); 86 | } 87 | 88 | @Override 89 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 90 | Bundle savedInstanceState) { 91 | mDrawerListView = (ListView) inflater.inflate( 92 | R.layout.fragment_navigation_drawer, container, false); 93 | mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 94 | @Override 95 | public void onItemClick(AdapterView parent, View view, int position, long id) { 96 | selectItem(position); 97 | } 98 | }); 99 | mDrawerListView.setAdapter(new ArrayAdapter( 100 | getActionBar().getThemedContext(), 101 | android.R.layout.simple_list_item_activated_1, 102 | android.R.id.text1, 103 | new String[]{ 104 | getString(R.string.option_dash), 105 | getString(R.string.option_smoothstreaming), 106 | getString(R.string.option_hls), 107 | getString(R.string.option_standard) 108 | })); 109 | mDrawerListView.setItemChecked(mCurrentSelectedPosition, true); 110 | return mDrawerListView; 111 | } 112 | 113 | public boolean isDrawerOpen() { 114 | return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); 115 | } 116 | 117 | /** 118 | * Users of this fragment must call this method to set up the navigation drawer interactions. 119 | * 120 | * @param fragmentId The android:id of this fragment in its activity's layout. 121 | * @param drawerLayout The DrawerLayout containing this fragment's UI. 122 | */ 123 | public void setUp(int fragmentId, DrawerLayout drawerLayout) { 124 | mFragmentContainerView = getActivity().findViewById(fragmentId); 125 | mDrawerLayout = drawerLayout; 126 | 127 | // set a custom shadow that overlays the main content when the drawer opens 128 | mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); 129 | // set up the drawer's list view with items and click listener 130 | 131 | ActionBar actionBar = getActionBar(); 132 | actionBar.setDisplayHomeAsUpEnabled(true); 133 | actionBar.setHomeButtonEnabled(true); 134 | 135 | // ActionBarDrawerToggle ties together the the proper interactions 136 | // between the navigation drawer and the action bar app icon. 137 | mDrawerToggle = new ActionBarDrawerToggle( 138 | getActivity(), /* host Activity */ 139 | mDrawerLayout, /* DrawerLayout object */ 140 | R.string.navigation_drawer_open, /* "open drawer" description for accessibility */ 141 | R.string.navigation_drawer_close /* "close drawer" description for accessibility */ 142 | ) { 143 | @Override 144 | public void onDrawerClosed(View drawerView) { 145 | super.onDrawerClosed(drawerView); 146 | if (!isAdded()) { 147 | return; 148 | } 149 | 150 | getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() 151 | } 152 | 153 | @Override 154 | public void onDrawerOpened(View drawerView) { 155 | super.onDrawerOpened(drawerView); 156 | if (!isAdded()) { 157 | return; 158 | } 159 | 160 | if (!mUserLearnedDrawer) { 161 | // The user manually opened the drawer; store this flag to prevent auto-showing 162 | // the navigation drawer automatically in the future. 163 | mUserLearnedDrawer = true; 164 | SharedPreferences sp = PreferenceManager 165 | .getDefaultSharedPreferences(getActivity()); 166 | sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); 167 | } 168 | 169 | getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() 170 | } 171 | }; 172 | 173 | // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, 174 | // per the navigation drawer design guidelines. 175 | if (!mUserLearnedDrawer && !mFromSavedInstanceState) { 176 | mDrawerLayout.openDrawer(mFragmentContainerView); 177 | } 178 | 179 | // Defer code dependent on restoration of previous instance state. 180 | mDrawerLayout.post(new Runnable() { 181 | @Override 182 | public void run() { 183 | mDrawerToggle.syncState(); 184 | } 185 | }); 186 | 187 | mDrawerLayout.setDrawerListener(mDrawerToggle); 188 | } 189 | 190 | private void selectItem(int position) { 191 | mCurrentSelectedPosition = position; 192 | if (mDrawerListView != null) { 193 | mDrawerListView.setItemChecked(position, true); 194 | } 195 | if (mDrawerLayout != null) { 196 | mDrawerLayout.closeDrawer(mFragmentContainerView); 197 | } 198 | if (mCallbacks != null) { 199 | mCallbacks.onNavigationDrawerItemSelected(position); 200 | } 201 | } 202 | 203 | @Override 204 | public void onAttach(Activity activity) { 205 | super.onAttach(activity); 206 | try { 207 | mCallbacks = (NavigationDrawerCallbacks) activity; 208 | } catch (ClassCastException e) { 209 | throw new ClassCastException("Activity must implement NavigationDrawerCallbacks."); 210 | } 211 | } 212 | 213 | @Override 214 | public void onDetach() { 215 | super.onDetach(); 216 | mCallbacks = null; 217 | } 218 | 219 | @Override 220 | public void onSaveInstanceState(Bundle outState) { 221 | super.onSaveInstanceState(outState); 222 | outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); 223 | } 224 | 225 | @Override 226 | public void onConfigurationChanged(Configuration newConfig) { 227 | super.onConfigurationChanged(newConfig); 228 | // Forward the new configuration the drawer toggle component. 229 | mDrawerToggle.onConfigurationChanged(newConfig); 230 | } 231 | 232 | 233 | @Override 234 | public boolean onOptionsItemSelected(MenuItem item) { 235 | if (mDrawerToggle.onOptionsItemSelected(item)) { 236 | return true; 237 | } 238 | return super.onOptionsItemSelected(item); 239 | } 240 | 241 | /** 242 | * Per the navigation drawer design guidelines, updates the action bar to show the global app 243 | * 'context', rather than just what's in the current screen. 244 | */ 245 | private void showGlobalContextActionBar() { 246 | ActionBar actionBar = getActionBar(); 247 | actionBar.setDisplayShowTitleEnabled(true); 248 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 249 | actionBar.setTitle(R.string.app_name); 250 | } 251 | 252 | private ActionBar getActionBar() { 253 | return ((AppCompatActivity) getActivity()).getSupportActionBar(); 254 | } 255 | 256 | 257 | /** 258 | * Callbacks interface that all activities using this fragment must implement. 259 | */ 260 | public static interface NavigationDrawerCallbacks { 261 | /** 262 | * Called when an item in the navigation drawer is selected. 263 | */ 264 | void onNavigationDrawerItemSelected(int position); 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/drawable-hdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/drawable-mdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 16 | 17 | 22 | 24 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_player_audio.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 24 | 25 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_player_video.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_media_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 15 | 19 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_navigation_drawer.xml: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listview_media_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #26A69A 4 | #2979FF 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 8 | 240dp 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SimpleExoPlayerDemo 3 | 4 | SimpleExoPlayer 5 | 6 | Standard 7 | HLS 8 | DASH 9 | SmoothStreaming 10 | 11 | Video 12 | Audio 13 | 14 | Open navigation drawer 15 | Close navigation drawer 16 | PlayerActivity 17 | 18 | Hello world! 19 | Settings 20 | AudioPlayerActivity 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 21 | 22 | 23 | 28 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:1.3.0' 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2' 11 | classpath 'com.github.dcendents:android-maven-plugin:1.2' 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srMarlins/SimpleExoPlayer/7388c0b1cff37e76cfffb03dac92fa87f298c4ec/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 27 11:18:31 EDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':simpleexoplayer' 2 | --------------------------------------------------------------------------------