├── .gitignore ├── .idea ├── .name ├── codeStyles │ └── Project.xml ├── dictionaries │ └── owonseog.xml ├── gradle.xml ├── misc.xml ├── render.experimental.xml ├── runConfigurations.xml └── vcs.xml ├── Readme.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── ows │ │ └── boostcourse │ │ └── uiplayer │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── ows │ │ │ └── boostcourse │ │ │ └── uiplayer │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── question2.png │ │ ├── layout │ │ ├── activity_main.xml │ │ └── dialog.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── ows │ └── boostcourse │ └── uiplayer │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── iaplayer ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── ows │ │ └── boostcourse │ │ └── iaplayer │ │ ├── ExampleInstrumentedTest.java │ │ └── IAPlayerTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── ows │ │ │ └── boostcourse │ │ │ └── iaplayer │ │ │ ├── IAListener.java │ │ │ ├── IAMeesage.java │ │ │ ├── IAPlayer.java │ │ │ ├── PlayerListener.java │ │ │ ├── ServiceListener.java │ │ │ ├── SimpleIAPlayer.java │ │ │ └── SocketService.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── ows │ └── boostcourse │ └── iaplayer │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | UIPlayer -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/dictionaries/owonseog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Android 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /.idea/render.experimental.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## IAPlayer 2 | 3 | IAPlayer is a media player that interacts with users. You can make your video's story change with your response. IAPlayer has all the capabilites of the Exoplayer and communicates with the server through SocketService. It was UIPlayer at first, but I changed it to IAPlayer because it could cause confusion with the user interface. 4 | 5 | 6 | 7 | ### Project Scenario 8 | 9 | ![image](https://user-images.githubusercontent.com/34837583/91696316-59b19780-ebaa-11ea-9512-02cedae70805.png) 10 | 11 | 12 | 1. Start IAPlayer 13 | 2. Foreground : specfic movie playback 14 | Background : server connet (socket server) 15 | 3. Wait Event 16 | 4. Require a user to response when an event occurs 17 | 5. Send a user's response to server 18 | 6. server socket change movie 19 | 20 | ### Component Design 21 | 22 | ![image](https://user-images.githubusercontent.com/34837583/91696433-89609f80-ebaa-11ea-98b0-75e0a13d81a1.png) 23 | 24 | 25 | - IAPlayer : Interaction Player 26 | IAPlayer has mainPlayer and playerlist. 27 | 28 | mainPlayer is SimpleIAPlayer who is in charge of the actual player 29 | 30 | playerlist is a temporary storage player list to solve buffering problems 31 | 32 | - SimpleIAPlayer : Exoplayer child and has customizing player.EventListener 33 | 34 | - SocketService : connect to server sockets in the background 35 | 36 | - IAMessage : IAMessage has event-related information ex) EventTime, url, title... 37 | 38 | ### Using IAPlayer 39 | 40 | The same environment for using Exoplayer 41 | 42 | ### Step 1 43 | 44 | Create IAPlayer instance. 45 | 46 | ``` 47 | // Component that set the network bandwidth 48 | DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter.Builder().build(); 49 | 50 | // AdaptiveTrackSelection Component 51 | AdaptiveTrackSelection.Factory adaptiveTrackSelection = new AdaptiveTrackSelection.Factory(defaultBandwidthMeter); 52 | 53 | // Set default track section 54 | DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(adaptiveTrackSelection); 55 | 56 | // Control media buffering 57 | LoadControl loadControl = new DefaultLoadControl(); 58 | 59 | // Component acting as rendering 60 | DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this); 61 | 62 | // Create IAPlayer 63 | iaPlayer = new IAPlayer(renderersFactory,defaultTrackSelector,loadControl,null); 64 | binding = DataBindingUtil.setContentView(this,R.layout.activity_main); 65 | binding.exoplayer.setPlayer(iaPlayer.getIAPlayer()); 66 | ``` 67 | 68 | ### Step 2 69 | 70 | Prepare media sample and set host and port. 71 | 72 | ``` 73 | // Media sample uri 74 | Uri uri = Uri.parse(URL); 75 | 76 | // Component for requesting data (HTTP, uri ..) 77 | DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this,"example-test")); 78 | 79 | // Media sample sources required for http live streaming 80 | HlsMediaSource hlsMediaSource = new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(uri); 81 | iaPlayer.setDataSourceFactory(dataSourceFactory); 82 | iaPlayer.setHostAndPort("localhost",5001); 83 | ``` 84 | 85 | ### Step3 86 | 87 | Customize IAListener and connect SocketService. 88 | 89 | ``` 90 | // IAPlayer prepare 91 | iaPlayer.prepare( 92 | 93 | // Customizeing IAListener 94 | new IAListener() { 95 | 96 | // Callback method invoked when an event occurs. 97 | @Override 98 | public void onConnet() { 99 | 100 | } 101 | 102 | // Callback method invoked when a user responses. 103 | @Override 104 | public void onUserSelect(final IAMeesage iaMeesage) { 105 | 106 | 107 | } 108 | }, 109 | hlsMediaSource 110 | ); 111 | iaPlayer.connect(this, SocketService.class); 112 | ``` 113 | 114 | You can also customize the socketservice that is responsible for server communication. 115 | 116 | ### End 117 | 118 | I'm still junior developer. I'm going to grow into an Android developer. I know is a way that not right to be inefficient and library iaplayer But I have made based on knowledge of the library. Thanks very much a drip if you would give the advice you gave me an efficient development. ( ex) pull request ..) 119 | 120 | Thank you for reading my readme. 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.0" 6 | defaultConfig { 7 | applicationId "ows.boostcourse.uiplayer" 8 | minSdkVersion 15 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | dataBinding{ 21 | enabled = true 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation 'androidx.appcompat:appcompat:1.0.2' 28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 29 | implementation project(':iaplayer') 30 | implementation 'com.google.android.exoplayer:exoplayer:2.8.0' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'androidx.test.ext:junit:1.1.0' 33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 34 | } 35 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/ows/boostcourse/uiplayer/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package ows.boostcourse.uiplayer; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("ows.boostcourse.uiplayer", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/ows/boostcourse/uiplayer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package ows.boostcourse.uiplayer; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.databinding.DataBindingUtil; 5 | 6 | import android.app.AlertDialog; 7 | import android.app.Dialog; 8 | import android.content.Context; 9 | import android.content.DialogInterface; 10 | import android.net.Uri; 11 | import android.os.Bundle; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.WindowManager; 15 | import android.widget.Button; 16 | 17 | import com.google.android.exoplayer2.DefaultLoadControl; 18 | import com.google.android.exoplayer2.DefaultRenderersFactory; 19 | import com.google.android.exoplayer2.LoadControl; 20 | import com.google.android.exoplayer2.source.hls.HlsMediaSource; 21 | import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; 22 | import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; 23 | import com.google.android.exoplayer2.upstream.DataSource; 24 | import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; 25 | import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; 26 | import com.google.android.exoplayer2.util.Util; 27 | 28 | import ows.boostcourse.uiplayer.databinding.ActivityMainBinding; 29 | import ows.boostcourse.iaplayer.IAPlayer; 30 | import ows.boostcourse.iaplayer.IAListener; 31 | import ows.boostcourse.iaplayer.IAMeesage; 32 | import ows.boostcourse.iaplayer.SocketService; 33 | 34 | public class MainActivity extends AppCompatActivity{ 35 | 36 | public static final String URL = "https://multiplatform-f.akamaihd.net/i/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,.f4v.csmil/master.m3u8"; 37 | ActivityMainBinding binding; 38 | 39 | IAPlayer iaPlayer; // 실행할 IAPlayer 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | 46 | // Component that set the network bandwidth 47 | DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter.Builder().build(); 48 | 49 | // AdaptiveTrackSelection Component 50 | AdaptiveTrackSelection.Factory adaptiveTrackSelection = new AdaptiveTrackSelection.Factory(defaultBandwidthMeter); 51 | 52 | // Set default track section 53 | DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(adaptiveTrackSelection); 54 | 55 | // Control media buffering 56 | LoadControl loadControl = new DefaultLoadControl(); 57 | 58 | // Component acting as rendering 59 | DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this); 60 | 61 | // Create IAPlayer 62 | iaPlayer = new IAPlayer(renderersFactory,defaultTrackSelector,loadControl,null); 63 | binding = DataBindingUtil.setContentView(this,R.layout.activity_main); 64 | binding.exoplayer.setPlayer(iaPlayer.getIAPlayer()); 65 | 66 | // Media sample uri 67 | Uri uri = Uri.parse(URL); 68 | 69 | // Component for requesting data (HTTP, uri ..) 70 | DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this,"example-test")); 71 | 72 | // Media sample sources required for http live streaming 73 | HlsMediaSource hlsMediaSource = new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(uri); 74 | iaPlayer.setDataSourceFactory(dataSourceFactory); 75 | iaPlayer.setHostAndPort("localhost",5001); 76 | 77 | // IAPlayer prepare 78 | iaPlayer.prepare( 79 | 80 | // Customizeing IAListener 81 | new IAListener() { 82 | 83 | // Callback method invoked when an event occurs. 84 | @Override 85 | public void onConnet() { 86 | 87 | } 88 | 89 | // Callback method invoked when a user responses. 90 | @Override 91 | public void onUserSelect(final IAMeesage iaMeesage) { 92 | 93 | final Dialog dialog = new Dialog(MainActivity.this); 94 | View view = getLayoutInflater().inflate(R.layout.dialog,null); 95 | 96 | Button button = view.findViewById(R.id.button); 97 | button.setText(iaMeesage.getTitle()[0]); 98 | button.setOnClickListener(new View.OnClickListener() { 99 | @Override 100 | public void onClick(View v) { 101 | iaPlayer.decidePlayer(0,iaMeesage.getNextId()[0]); 102 | binding.exoplayer.setPlayer(iaPlayer.getIAPlayer()); 103 | dialog.dismiss(); 104 | } 105 | }); 106 | 107 | Button button2 = view.findViewById(R.id.button2); 108 | button2.setText(iaMeesage.getTitle()[1]); 109 | button2.setOnClickListener(new View.OnClickListener() { 110 | @Override 111 | public void onClick(View v) { 112 | iaPlayer.decidePlayer(1,iaMeesage.getNextId()[1]); 113 | binding.exoplayer.setPlayer(iaPlayer.getIAPlayer()); 114 | dialog.dismiss(); 115 | } 116 | }); 117 | dialog.setContentView(view); 118 | 119 | WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); 120 | lp.copyFrom(dialog.getWindow().getAttributes()); 121 | lp.width = 1000; 122 | lp.height = 1000; 123 | dialog.getWindow().setAttributes(lp); 124 | 125 | dialog.show(); 126 | 127 | } 128 | }, 129 | hlsMediaSource 130 | ); 131 | iaPlayer.connect(this, SocketService.class); 132 | 133 | 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/question2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ows3090/UIPlayer/8977e12ed170bbf43ce9c93feaf18b88fbfd18cd/app/src/main/res/drawable/question2.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 32 | 33 |