Help us understand the problem. What is going on with this article?

Android ExoPlayer + HLS + IMA

More than 1 year has passed since last update.

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

mehdi
cloudpack
Amazon Web Services (AWS) の導入設計、環境構築、運用・保守をサポートするマネジドホスティングサービス
https://cloudpack.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした