Android ExoPlayer + HLS + Google IMA
はじめに
こんにちは streampack チームのメディです。
https://cloudpack.jp/service/option/streampack.html
Objective: ・目的
Play a HLS stream in an Android APP with ExoPlayer & display Google IMA advertisements.
ExoPlayerを使用してAndroidアプリでHLSストリームを再生し、Google IMA広告を表示します。
Implementation・ 実装
Step 0
HLS
If you don’t have a HLS stream, please visit the following page to find one.
HLSストリームがない場合は、以下のページをご覧ください。
https://github.com/notanewbie/LegalStream
IMA
If you don’t have IMA credentials, you can use demo tags.
IMA資格情報がない場合は、デモタグを使用できます。
https://developers.google.com/interactive-media-ads/docs/sdks/html5/tags
Step 1
Create a new project & a blank Main Activity.
新しいプロジェクトと空白のメインアクティビティを作成してください。
MainActivity.java
package jp.co.mycompany.com.exotuto; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
AndroidManifest.xml
Enable hardware acceleration.
ハードウェアアクセラレーションを有効にしてください。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="jp.co.mycompany.com.exotuto"> <application android:allowBackup="true" android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Dependencies ・ソフトウェアの依存関係
buid.gradle(Module:app)
apply plugin: 'com.android.application' android { compileSdkVersion 27 defaultConfig { applicationId "jp.co.mycompany.com.exotuto" minSdkVersion 15 targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.0.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' compile 'com.google.android.exoplayer:exoplayer:2.6.0' compile 'com.google.android.exoplayer:exoplayer-hls:2.6.0' compile 'com.google.android.exoplayer:extension-ima:2.6.0' }
Step 2
Create a new Basic Activity & name it PlayerActivity.
新しいBasicアクティビティを作成し、それをPlayerActivityと名づけます。
Then modify MainActivity.java as follows:
MainActivity.java を次のように変更します。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, PlayerActivity.class); startActivity(intent); } }
Step 3
Simple HLS implementation・ 単純なHLS実装
res/layout/content_player.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="jp.co.mycompany.com.exotuto.PlayerActivity" tools:showIn="@layout/activity_player"> <com.google.android.exoplayer2.ui.SimpleExoPlayerView android:id="@+id/player_view" android:focusable="true" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="10dp" /> </android.support.constraint.ConstraintLayout>
PLayerActivity.java
public class PlayerActivity extends AppCompatActivity { //Player private SimpleExoPlayerView simpleExoPlayerView; private SimpleExoPlayer player; //Logs final private String TAG = "PlayerActivity"; //HLS final private String VIDEO_URL = "https://nhkworld.webcdn.stream.ne.jp/www11/nhkworld-tv/domestic/263942/live_wa_s.m3u8"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_player); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); //ExoPlayer implementation //Create a default TrackSelector BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); // Create a default LoadControl LoadControl loadControl = new DefaultLoadControl(); //Bis. Create a RenderFactory RenderersFactory renderersFactory = new DefaultRenderersFactory(this); //Create the player player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl); simpleExoPlayerView = new SimpleExoPlayerView(this); simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view); //Set media controller simpleExoPlayerView.setUseController(true); simpleExoPlayerView.requestFocus(); // Bind the player to the view. simpleExoPlayerView.setPlayer(player); // Set the media source Uri mp4VideoUri = Uri.parse(VIDEO_URL); //Measures bandwidth during playback. Can be null if not required. DefaultBandwidthMeter bandwidthMeterA = new DefaultBandwidthMeter(); //Produces DataSource instances through which media data is loaded. DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "PiwikVideoApp"), bandwidthMeterA); //Produces Extractor instances for parsing the media data. ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); //FOR LIVE STREAM LINK: MediaSource videoSource = new HlsMediaSource(mp4VideoUri, dataSourceFactory, 1, null, null); final MediaSource mediaSource = videoSource; player.prepare(videoSource); } //Android Life cycle @Override protected void onStop() { player.release(); super.onStop(); Log.v(TAG, "onStop()..."); } @Override protected void onStart() { super.onStart(); Log.v(TAG, "onStart()..."); } @Override protected void onResume() { super.onResume(); Log.v(TAG, "onResume()..."); } @Override protected void onPause() { super.onPause(); Log.v(TAG, "onPause()..."); } @Override protected void onDestroy() { super.onDestroy(); Log.v(TAG, "onDestroy()..."); player.release(); } }
Please run your app in the Android emulator.
Androidエミュレータでアプリをテストしてください。
Step 4
Player listeners ・プレーヤーリスナー
In the onCreate method of PlayerActivity.java, please add the following listeners.
PlayerActivityのonCreateメソッドで使用します。次のリスナーを追加してください。
//ExoPLayer events listener player.addListener(new Player.EventListener() { @Override public void onTimelineChanged(Timeline timeline, Object manifest) { Log.v(TAG, "Listener-onTimelineChanged..."); } @Override public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { Log.v(TAG, "Listener-onTracksChanged..."); } @Override public void onLoadingChanged(boolean isLoading) { Log.v(TAG, "Listener-onLoadingChanged...isLoading:" + isLoading); } @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { Log.v(TAG, "Listener-onPlayerStateChanged..." + playbackState); switch (playbackState) { case Player.STATE_IDLE: Log.v(TAG, "STATE IDLE"); break; case Player.STATE_BUFFERING: Log.v(TAG, "STATE BUFFERING"); break; case Player.STATE_READY: Log.v(TAG, "STATE READY"); break; case Player.STATE_ENDED: Log.v(TAG, "STATE ENDED"); break; default: break; } } @Override public void onRepeatModeChanged(int repeatMode) { Log.v(TAG, "Listener-onRepeatModeChanged..."); } @Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { } @Override public void onPlayerError(ExoPlaybackException error) { Log.v(TAG, "Listener-onPlayerError..."); player.stop(); player.prepare(adsMediaSource); player.setPlayWhenReady(true); } @Override public void onPositionDiscontinuity(int reason) { Log.v(TAG, "Listener-onPositionDiscontinuity..."); } @Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { Log.v(TAG, "Listener-onPlaybackParametersChanged..."); } @Override public void onSeekProcessed() { } });
Step 5
IMA implementation ・IMAの実装
In PlayerActivity.java, add the following class variables.
PlayerActivity.java で、次のクラス変数を追加してください。
//IMA private ImaAdsLoader imaAdsLoader; final private String AD_TAG_URI = "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpreonly&cmsid=496&vid=short_onecue&correlator=";
Please update the onCreate method of PlayerActivity.java as follows.
PlayerActivity.java のonCreateメソッドを次のように更新してください。
//player.prepare(adsMediaSource);//Remove this line. imaAdsLoader = new ImaAdsLoader(this, Uri.parse(AD_TAG_URI)); AdsMediaSource.AdsListener adsListener = new AdsMediaSource.AdsListener() { @Override public void onAdLoadError(IOException error) { error.printStackTrace(); } @Override public void onAdClicked() { } @Override public void onAdTapped() { } }; AdsMediaSource adsMediaSource = new AdsMediaSource( mediaSource, dataSourceFactory, imaAdsLoader, simpleExoPlayerView.getOverlayFrameLayout(), null, adsListener ); player.prepare(adsMediaSource);
Results 結果
It works!
できます!
Step 6
Adding IMA event listeners ・IMAイベントリスナーの追加
Please add the following code to the onCreate method of PlayerActivity.java.
PlayerActivity.javaのonCreateメソッドに次のコードを追加してください。
//IMA event listeners com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader = imaAdsLoader.getAdsLoader(); adsLoader.addAdsLoadedListener(new AdsLoader.AdsLoadedListener() { @Override public void onAdsManagerLoaded(AdsManagerLoadedEvent adsManagerLoadedEvent) { AdsManager imaAdsManager = adsManagerLoadedEvent.getAdsManager(); imaAdsManager.addAdEventListener(new AdEvent.AdEventListener() { @Override public void onAdEvent(AdEvent adEvent) { Log.v("AdEvent: ", adEvent.getType().toString()); switch (adEvent.getType()) { case LOADED: break; case PAUSED: break; case STARTED: break; case COMPLETED: break; case ALL_ADS_COMPLETED: break; default: break; /* Full list of events. Implement what you need. LOADED, TAPPED, PAUSED, LOG, CLICKED, RESUMED, SKIPPED, STARTED, MIDPOINT, COMPLETED, AD_PROGRESS, ICON_TAPPED, AD_BREAK_ENDED, AD_BREAK_READY, FIRST_QUARTILE, THIRD_QUARTILE, AD_BREAK_STARTED, ALL_ADS_COMPLETED, CUEPOINTS_CHANGED, CONTENT_PAUSE_REQUESTED,CONTENT_RESUME_REQUESTED */ } } }); } });
Full project code
https://github.com/mr1985/exoplayer_demo_hls
Information sources・ 情報源
https://github.com/sakurabird/Android-Example-HLS-ExoPlayer
https://developers.google.com/interactive-media-ads/docs/sdks/android/
https://github.com/google/ExoPlayer
https://google.github.io/ExoPlayer/demo-application.html