Android
Video
HLS
Exoplayer
IMA

Android ExoPlayer + HLS + IMA


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と名づけます。

Screen Shot 2018-01-09 at 12.06.36.png

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エミュレータでアプリをテストしてください。

Screen Shot 2018-01-09 at 18.52.34.png


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!

できます!

Screen Shot 2018-01-09 at 19.02.17.png


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