From bb8b310103fbdf2f3ea7328f7a957bca7b42e103 Mon Sep 17 00:00:00 2001 From: Jackson Sui Date: Mon, 17 Jun 2024 07:10:17 -0700 Subject: [PATCH] Adds a pod serving sample app. PiperOrigin-RevId: 643995853 --- .github/workflows/build.yaml | 1 + PodServingExample/app/build.gradle | 43 +++ PodServingExample/app/proguard-rules.pro | 15 + .../app/src/main/AndroidManifest.xml | 24 ++ .../samplevideoplayer/SampleVideoPlayer.java | 256 ++++++++++++++ .../v3/samples/videoplayerapp/MyActivity.java | 122 +++++++ .../videoplayerapp/SampleAdsWrapper.java | 320 ++++++++++++++++++ .../ic_action_play_over_video.png | Bin 0 -> 419 bytes .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 7593 bytes .../ic_action_play_over_video.png | Bin 0 -> 307 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 4329 bytes .../ic_action_play_over_video.png | Bin 0 -> 709 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 11680 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 17042 bytes .../app/src/main/res/layout/activity_my.xml | 68 ++++ .../app/src/main/res/values-w820dp/dimens.xml | 6 + .../app/src/main/res/values/dimens.xml | 6 + .../app/src/main/res/values/strings.xml | 9 + PodServingExample/build.gradle | 14 + PodServingExample/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 5 + PodServingExample/gradlew | 164 +++++++++ PodServingExample/gradlew.bat | 90 +++++ PodServingExample/settings.gradle | 1 + 24 files changed, 1146 insertions(+) create mode 100644 PodServingExample/app/build.gradle create mode 100644 PodServingExample/app/proguard-rules.pro create mode 100644 PodServingExample/app/src/main/AndroidManifest.xml create mode 100644 PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/samplevideoplayer/SampleVideoPlayer.java create mode 100644 PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/MyActivity.java create mode 100644 PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/SampleAdsWrapper.java create mode 100644 PodServingExample/app/src/main/res/drawable-hdpi/ic_action_play_over_video.png create mode 100644 PodServingExample/app/src/main/res/drawable-hdpi/ic_launcher.png create mode 100644 PodServingExample/app/src/main/res/drawable-mdpi/ic_action_play_over_video.png create mode 100644 PodServingExample/app/src/main/res/drawable-mdpi/ic_launcher.png create mode 100644 PodServingExample/app/src/main/res/drawable-xhdpi/ic_action_play_over_video.png create mode 100644 PodServingExample/app/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100644 PodServingExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 PodServingExample/app/src/main/res/layout/activity_my.xml create mode 100644 PodServingExample/app/src/main/res/values-w820dp/dimens.xml create mode 100644 PodServingExample/app/src/main/res/values/dimens.xml create mode 100644 PodServingExample/app/src/main/res/values/strings.xml create mode 100644 PodServingExample/build.gradle create mode 100644 PodServingExample/gradle.properties create mode 100644 PodServingExample/gradle/wrapper/gradle-wrapper.properties create mode 100755 PodServingExample/gradlew create mode 100644 PodServingExample/gradlew.bat create mode 100644 PodServingExample/settings.gradle diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index dffc831..5be144b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,6 +22,7 @@ jobs: - "BasicExample" - "ExoPlayerExample" - "SampleVideoPlayer" + - "PodServingExample" steps: - name: Set up JDK 17 uses: actions/setup-java@v1 diff --git a/PodServingExample/app/build.gradle b/PodServingExample/app/build.gradle new file mode 100644 index 0000000..cb4732d --- /dev/null +++ b/PodServingExample/app/build.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.application' + +android { + namespace 'com.google.ads.interactivemedia.v3.samples.videoplayerapp' + compileSdk 34 + + // Java 17 required by Gradle 8+ + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + defaultConfig { + applicationId "com.google.ads.interactivemedia.v3.samples.videoplayerapp" + minSdkVersion 21 + targetSdkVersion 34 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + def media3_version = "1.3.1" + implementation platform("org.jetbrains.kotlin:kotlin-bom:1.8.0") + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation "androidx.media3:media3-ui:$media3_version" + implementation "androidx.media3:media3-exoplayer:$media3_version" + implementation "androidx.media3:media3-exoplayer-hls:$media3_version" + implementation "androidx.media3:media3-exoplayer-dash:$media3_version" + implementation 'androidx.mediarouter:mediarouter:1.7.0' + implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.34.0' +} diff --git a/PodServingExample/app/proguard-rules.pro b/PodServingExample/app/proguard-rules.pro new file mode 100644 index 0000000..9d6093f --- /dev/null +++ b/PodServingExample/app/proguard-rules.pro @@ -0,0 +1,15 @@ +# Add project specific ProGuard rules here. +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/PodServingExample/app/src/main/AndroidManifest.xml b/PodServingExample/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..719abb4 --- /dev/null +++ b/PodServingExample/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + diff --git a/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/samplevideoplayer/SampleVideoPlayer.java b/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/samplevideoplayer/SampleVideoPlayer.java new file mode 100644 index 0000000..7895308 --- /dev/null +++ b/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/samplevideoplayer/SampleVideoPlayer.java @@ -0,0 +1,256 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ads.interactivemedia.v3.samples.samplevideoplayer; + +import static androidx.media3.common.C.CONTENT_TYPE_DASH; +import static androidx.media3.common.C.CONTENT_TYPE_HLS; +import static androidx.media3.common.C.CONTENT_TYPE_OTHER; +import static androidx.media3.common.C.TIME_UNSET; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.Uri; +import android.util.Log; +import androidx.media3.common.C.ContentType; +import androidx.media3.common.ForwardingPlayer; +import androidx.media3.common.MediaItem; +import androidx.media3.common.Metadata; +import androidx.media3.common.Player; +import androidx.media3.common.Timeline; +import androidx.media3.common.util.Util; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DefaultDataSource; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.exoplayer.dash.DashMediaSource; +import androidx.media3.exoplayer.dash.DefaultDashChunkSource; +import androidx.media3.exoplayer.hls.HlsMediaSource; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.extractor.metadata.emsg.EventMessage; +import androidx.media3.extractor.metadata.id3.TextInformationFrame; +import androidx.media3.ui.PlayerView; +import com.google.ads.interactivemedia.v3.api.player.VideoStreamPlayer; + +/** A video player that plays HLS or DASH streams using ExoPlayer. */ +@SuppressLint("UnsafeOptInUsageError") +/* @SuppressLint is needed for new media3 APIs. */ +public class SampleVideoPlayer { + + private static final String LOG_TAG = "SampleVideoPlayer"; + + /** + * Video player callback interface that extends IMA's VideoStreamPlayerCallback by adding the + * onSeek() callback to support ad snapback. + */ + public interface SampleVideoPlayerCallback extends VideoStreamPlayer.VideoStreamPlayerCallback { + void onSeek(int windowIndex, long positionMs); + } + + private final Context context; + + private ExoPlayer player; + private final PlayerView playerView; + private SampleVideoPlayerCallback playerCallback; + + @ContentType private int currentlyPlayingStreamType = CONTENT_TYPE_OTHER; + + private String streamUrl; + private Boolean streamRequested; + private boolean canSeek; + + public SampleVideoPlayer(Context context, PlayerView playerView) { + this.context = context; + this.playerView = playerView; + streamRequested = false; + canSeek = true; + } + + private void initPlayer() { + release(); + + player = new ExoPlayer.Builder(context).build(); + playerView.setPlayer( + new ForwardingPlayer(player) { + @Override + public void seekToDefaultPosition() { + seekToDefaultPosition(getCurrentMediaItemIndex()); + } + + @Override + public void seekToDefaultPosition(int windowIndex) { + seekTo(windowIndex, /* positionMs= */ TIME_UNSET); + } + + @Override + public void seekTo(long positionMs) { + seekTo(getCurrentMediaItemIndex(), positionMs); + } + + @Override + public void seekTo(int windowIndex, long positionMs) { + if (canSeek) { + if (playerCallback != null) { + playerCallback.onSeek(windowIndex, positionMs); + } else { + super.seekTo(windowIndex, positionMs); + } + } + } + }); + } + + public void play() { + if (streamRequested) { + // Stream requested, just resume. + player.setPlayWhenReady(true); + if (playerCallback != null) { + playerCallback.onResume(); + } + return; + } + initPlayer(); + + DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(context); + + // Create the MediaItem to play, specifying the content URI. + Uri contentUri = Uri.parse(streamUrl); + MediaItem mediaItem = new MediaItem.Builder().setUri(contentUri).build(); + + MediaSource mediaSource; + currentlyPlayingStreamType = Util.inferContentType(Uri.parse(streamUrl)); + switch (currentlyPlayingStreamType) { + case CONTENT_TYPE_HLS: + mediaSource = new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem); + break; + case CONTENT_TYPE_DASH: + mediaSource = + new DashMediaSource.Factory( + new DefaultDashChunkSource.Factory(dataSourceFactory), dataSourceFactory) + .createMediaSource(mediaItem); + break; + default: + throw new UnsupportedOperationException("Unknown stream type."); + } + + player.setMediaSource(mediaSource); + player.prepare(); + + // Register for ID3 events. + player.addListener( + new Player.Listener() { + @Override + public void onMetadata(Metadata metadata) { + for (int i = 0; i < metadata.length(); i++) { + Metadata.Entry entry = metadata.get(i); + if (entry instanceof TextInformationFrame) { + TextInformationFrame textFrame = (TextInformationFrame) entry; + if ("TXXX".equals(textFrame.id)) { + Log.d(LOG_TAG, "Received user text: " + textFrame.values.get(0)); + if (playerCallback != null) { + playerCallback.onUserTextReceived(textFrame.values.get(0)); + } + } + } else if (entry instanceof EventMessage) { + EventMessage eventMessage = (EventMessage) entry; + String eventMessageValue = new String(eventMessage.messageData); + Log.d(LOG_TAG, "Received user text: " + eventMessageValue); + if (playerCallback != null) { + playerCallback.onUserTextReceived(eventMessageValue); + } + } + } + } + }); + + player.setPlayWhenReady(true); + streamRequested = true; + } + + public void pause() { + player.setPlayWhenReady(false); + if (playerCallback != null) { + playerCallback.onPause(); + } + } + + public void seekTo(long positionMs) { + player.seekTo(positionMs); + } + + public void seekTo(int windowIndex, long positionMs) { + player.seekTo(windowIndex, positionMs); + } + + private void release() { + if (player != null) { + player.release(); + player = null; + streamRequested = false; + } + } + + public void setStreamUrl(String streamUrl) { + this.streamUrl = streamUrl; + streamRequested = false; // request new stream on play + } + + public void enableControls(boolean doEnable) { + if (doEnable) { + playerView.showController(); + } else { + playerView.hideController(); + } + canSeek = doEnable; + } + + public boolean isStreamRequested() { + return streamRequested; + } + + // Methods for exposing player information. + public void setSampleVideoPlayerCallback(SampleVideoPlayerCallback callback) { + playerCallback = callback; + } + + /** Returns current offset position of the playhead in milliseconds for DASH and HLS stream. */ + public long getCurrentPositionMs() { + if (player == null) { + return 0; + } + Timeline currentTimeline = player.getCurrentTimeline(); + if (currentTimeline.isEmpty()) { + return player.getCurrentPosition(); + } + Timeline.Window window = new Timeline.Window(); + player.getCurrentTimeline().getWindow(player.getCurrentMediaItemIndex(), window); + if (window.isLive()) { + return player.getCurrentPosition() + window.windowStartTimeMs; + } else { + return player.getCurrentPosition(); + } + } + + public long getDuration() { + if (player == null) { + return 0; + } + return player.getDuration(); + } + + public void setVolume(int percentage) { + player.setVolume(percentage); + } +} diff --git a/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/MyActivity.java b/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/MyActivity.java new file mode 100644 index 0000000..ab32d4a --- /dev/null +++ b/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/MyActivity.java @@ -0,0 +1,122 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ads.interactivemedia.v3.samples.videoplayerapp; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ScrollView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import com.google.ads.interactivemedia.v3.samples.samplevideoplayer.SampleVideoPlayer; + +/** Main Activity that plays media using {@link SampleVideoPlayer}. */ +@SuppressLint("UnsafeOptInUsageError") +/* @SuppressLint is needed for new media3 APIs. */ +public class MyActivity extends Activity { + + private static final String DEFAULT_STREAM_URL = + "https://storage.googleapis.com/interactive-media-ads/media/bbb.m3u8"; + private static final String APP_LOG_TAG = "ImaDaiExample"; + + /** An interface defining how this class emits log messages. */ + public interface Logger { + void log(String logMessage); + } + + /** Initializes the logger for displaying events to screen. */ + private void initializeLogger() { + final ScrollView scrollView = findViewById(R.id.logScroll); + final TextView textView = findViewById(R.id.logText); + + this.logger = + (logMessage -> { + Log.i(APP_LOG_TAG, logMessage); + if (textView != null) { + textView.append(logMessage); + } + if (scrollView != null) { + scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN)); + } + }); + } + + private Logger logger; + + protected SampleVideoPlayer sampleVideoPlayer; + protected ImageButton playButton; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_my); + + initializeLogger(); + + sampleVideoPlayer = new SampleVideoPlayer(MyActivity.this, findViewById(R.id.playerView)); + playButton = findViewById(R.id.playButton); + final SampleAdsWrapper sampleAdsWrapper = + new SampleAdsWrapper(this, sampleVideoPlayer, findViewById(R.id.adUiContainer), logger); + sampleAdsWrapper.setFallbackUrl(DEFAULT_STREAM_URL); + + // Set up play button listener to play video then hide play button. + playButton.setOnClickListener( + view -> { + sampleVideoPlayer.enableControls(true); + sampleAdsWrapper.requestAndPlayAds(); + playButton.setVisibility(View.GONE); + }); + updateVideoDescriptionVisibility(); + } + + @Override + public void onConfigurationChanged(@NonNull Configuration configuration) { + super.onConfigurationChanged(configuration); + // Hide the extra content when in landscape so the video is as large as possible. + updateVideoDescriptionVisibility(); + } + + private void updateVideoDescriptionVisibility() { + int orientation = getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + findViewById(R.id.descriptionLayout).setVisibility(View.GONE); + } else { + findViewById(R.id.descriptionLayout).setVisibility(View.VISIBLE); + } + } + + // Needed to pause/resume app from background. + @Override + public void onPause() { + super.onPause(); + if (sampleVideoPlayer != null && sampleVideoPlayer.isStreamRequested()) { + sampleVideoPlayer.pause(); + } + } + + @Override + public void onResume() { + super.onResume(); + if (sampleVideoPlayer != null && sampleVideoPlayer.isStreamRequested()) { + sampleVideoPlayer.play(); + } + } +} diff --git a/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/SampleAdsWrapper.java b/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/SampleAdsWrapper.java new file mode 100644 index 0000000..d45fd68 --- /dev/null +++ b/PodServingExample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/SampleAdsWrapper.java @@ -0,0 +1,320 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ads.interactivemedia.v3.samples.videoplayerapp; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import com.google.ads.interactivemedia.v3.api.AdErrorEvent; +import com.google.ads.interactivemedia.v3.api.AdEvent; +import com.google.ads.interactivemedia.v3.api.AdsLoader; +import com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent; +import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings; +import com.google.ads.interactivemedia.v3.api.CuePoint; +import com.google.ads.interactivemedia.v3.api.ImaSdkFactory; +import com.google.ads.interactivemedia.v3.api.ImaSdkSettings; +import com.google.ads.interactivemedia.v3.api.StreamDisplayContainer; +import com.google.ads.interactivemedia.v3.api.StreamManager; +import com.google.ads.interactivemedia.v3.api.StreamRequest; +import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat; +import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate; +import com.google.ads.interactivemedia.v3.api.player.VideoStreamPlayer; +import com.google.ads.interactivemedia.v3.samples.samplevideoplayer.SampleVideoPlayer; +import com.google.ads.interactivemedia.v3.samples.samplevideoplayer.SampleVideoPlayer.SampleVideoPlayerCallback; +import com.google.ads.interactivemedia.v3.samples.videoplayerapp.MyActivity.Logger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** This class implements IMA to add pod ad-serving support to SampleVideoPlayer */ +@SuppressLint("UnsafeOptInUsageError") +/* @SuppressLint is needed for new media3 APIs. */ +public class SampleAdsWrapper + implements AdEvent.AdEventListener, AdErrorEvent.AdErrorListener, AdsLoader.AdsLoadedListener { + + // Set up the pod serving variables. + private static final String NETWORK_CODE = ""; + private static final String CUSTOM_ASSET_KEY = ""; + private static final String API_KEY = ""; + private static final String STREAM_URL = ""; + private static final StreamFormat STREAM_FORMAT = StreamFormat.HLS; + + private enum StreamType { + LIVESTREAM, + VOD, + } + + // Change this enum to make either a live or VOD pod stream request. + private static final StreamType CONTENT_STREAM_TYPE = StreamType.LIVESTREAM; + + private final ImaSdkFactory sdkFactory; + private final AdsLoader adsLoader; + private StreamManager streamManager; + private final List playerCallbacks; + + private final SampleVideoPlayer videoPlayer; + private final Context context; + private final ViewGroup adUiContainer; + private final Logger logger; + private String fallbackUrl; + + /** + * Creates a new SampleAdsWrapper that implements IMA Dynamic Ad Insertion. + * + * @param context the app's context. + * @param videoPlayer underlying ExoPlayer wrapped by the SampleVideoPlayer. + * @param adUiContainer ViewGroup that displays the ad UI (ad timer, skip button, adChoices icon). + * @param logger Logger to log messages to. + */ + public SampleAdsWrapper( + @NonNull Context context, + @NonNull SampleVideoPlayer videoPlayer, + @NonNull ViewGroup adUiContainer, + @NonNull Logger logger) { + this.context = context; + this.videoPlayer = videoPlayer; + this.adUiContainer = adUiContainer; + this.logger = logger; + playerCallbacks = new ArrayList<>(); + sdkFactory = ImaSdkFactory.getInstance(); + adsLoader = createAdsLoader(); + } + + private AdsLoader createAdsLoader() { + ImaSdkSettings settings = sdkFactory.createImaSdkSettings(); + // Change any settings as necessary here. + settings.setDebugMode(true); + VideoStreamPlayer videoStreamPlayer = createVideoStreamPlayer(); + StreamDisplayContainer displayContainer = + ImaSdkFactory.createStreamDisplayContainer(adUiContainer, videoStreamPlayer); + videoPlayer.setSampleVideoPlayerCallback( + new SampleVideoPlayerCallback() { + @Override + public void onUserTextReceived(@NonNull String userText) { + for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { + callback.onUserTextReceived(userText); + } + } + + @Override + public void onSeek(int windowIndex, long positionMs) { + // See if we would seek past an ad, and if so, jump back to it. + long newSeekPositionMs = positionMs; + if (streamManager != null) { + CuePoint prevCuePoint = streamManager.getPreviousCuePointForStreamTimeMs(positionMs); + if (prevCuePoint != null && !prevCuePoint.isPlayed()) { + newSeekPositionMs = prevCuePoint.getStartTimeMs(); + } + } + videoPlayer.seekTo(windowIndex, newSeekPositionMs); + } + + @Override + public void onContentComplete() { + for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { + callback.onContentComplete(); + } + } + + @Override + public void onPause() { + for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { + callback.onPause(); + } + } + + @Override + public void onResume() { + for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { + callback.onResume(); + } + } + + @Override + public void onVolumeChanged(int percentage) { + for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { + callback.onVolumeChanged(percentage); + } + } + }); + AdsLoader adsLoader = sdkFactory.createAdsLoader(context, settings, displayContainer); + adsLoader.addAdErrorListener(SampleAdsWrapper.this); + adsLoader.addAdsLoadedListener(SampleAdsWrapper.this); + return adsLoader; + } + + public void requestAndPlayAds() { + StreamRequest request; + switch (CONTENT_STREAM_TYPE) { + case LIVESTREAM: + // Live pod stream request. + request = sdkFactory.createPodStreamRequest(NETWORK_CODE, CUSTOM_ASSET_KEY, API_KEY); + break; + case VOD: + // VOD pod stream request. + request = sdkFactory.createPodVodStreamRequest(NETWORK_CODE); + break; + default: + throw new IllegalStateException("Unexpected value: " + CONTENT_STREAM_TYPE); + } + request.setFormat(STREAM_FORMAT); + adsLoader.requestStream(request); + } + + private VideoStreamPlayer createVideoStreamPlayer() { + return new VideoStreamPlayer() { + @Override + public void loadUrl( + @NonNull String streamUrl, @NonNull List> subtitles) { + // For VOD stream only, the IMA DAI SDK calls loadUrl() when it completes + // loading the ad metadata. + videoPlayer.setStreamUrl(streamUrl); + videoPlayer.play(); + } + + @Override + public void pause() { + // Pause player. + videoPlayer.pause(); + } + + @Override + public void resume() { + // Resume player. + videoPlayer.play(); + } + + @Override + public int getVolume() { + // Make the video player play at the current device volume. + return 100; + } + + @Override + public void addCallback(@NonNull VideoStreamPlayerCallback videoStreamPlayerCallback) { + playerCallbacks.add(videoStreamPlayerCallback); + } + + @Override + public void removeCallback(@NonNull VideoStreamPlayerCallback videoStreamPlayerCallback) { + playerCallbacks.remove(videoStreamPlayerCallback); + } + + @Override + public void onAdBreakStarted() { + // Disable player controls. + videoPlayer.enableControls(false); + logger.log("Ad Break Started\n"); + } + + @Override + public void onAdBreakEnded() { + // Re-enable player controls. + if (videoPlayer != null) { + videoPlayer.enableControls(true); + } + logger.log("Ad Break Ended\n"); + } + + @Override + public void onAdPeriodStarted() { + logger.log("Ad Period Started\n"); + } + + @Override + public void onAdPeriodEnded() { + logger.log("Ad Period Ended\n"); + } + + @Override + public void seek(@NonNull long timeMs) { + // An ad was skipped. Skip to the content time. + videoPlayer.seekTo(timeMs); + logger.log("seek\n"); + } + + @NonNull + @Override + public VideoProgressUpdate getContentProgress() { + return new VideoProgressUpdate( + videoPlayer.getCurrentPositionMs(), videoPlayer.getDuration()); + } + }; + } + + /** AdErrorListener implementation */ + @Override + public void onAdError(@NonNull AdErrorEvent event) { + logger.log(String.format("Error: %s\n", event.getError().getMessage())); + // play fallback URL. + logger.log("Playing fallback Url\n"); + videoPlayer.setStreamUrl(fallbackUrl); + videoPlayer.enableControls(true); + videoPlayer.play(); + } + + /** AdEventListener implementation */ + @Override + public void onAdEvent(@NonNull AdEvent event) { + switch (event.getType()) { + case AD_PROGRESS: + // Do nothing or else log will be filled by these messages. + break; + default: + logger.log(String.format("Event: %s\n", event.getType())); + break; + } + } + + /** AdsLoadedListener implementation */ + @Override + public void onAdsManagerLoaded(@NonNull AdsManagerLoadedEvent event) { + streamManager = event.getStreamManager(); + streamManager.addAdErrorListener(this); + streamManager.addAdEventListener(this); + + AdsRenderingSettings adsRenderingSettings = sdkFactory.createAdsRenderingSettings(); + // Add any ads rendering settings here. + // This init() only loads the UI rendering settings locally. + streamManager.init(adsRenderingSettings); + + // To enable ad pod streams + String streamID = streamManager.getStreamId(); + String streamUrl = STREAM_URL.replace("[[STREAMID]]", streamID); + switch (CONTENT_STREAM_TYPE) { + case LIVESTREAM: + // Play the live pod stream. + videoPlayer.setStreamUrl(streamUrl); + videoPlayer.play(); + // The SDK doesn't call the loadUrl() function for livestream. + break; + case VOD: + // Refer to your Video Tech Partner (VTP) or video stitching guide to fetch the stream URL + // and the subtitles for a the ad stitched VOD stream. + List> subtitles = new ArrayList<>(); + streamManager.loadThirdPartyStream(streamUrl, subtitles); + break; + } + } + + /** Sets fallback URL in case ads stream fails. */ + void setFallbackUrl(String url) { + fallbackUrl = url; + } +} diff --git a/PodServingExample/app/src/main/res/drawable-hdpi/ic_action_play_over_video.png b/PodServingExample/app/src/main/res/drawable-hdpi/ic_action_play_over_video.png new file mode 100644 index 0000000000000000000000000000000000000000..157f6b27d89fd82f1d26af0215102d46a283d537 GIT binary patch literal 419 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 LNkvXXu0mjfV$y>3 literal 0 HcmV?d00001 diff --git a/PodServingExample/app/src/main/res/drawable-mdpi/ic_action_play_over_video.png b/PodServingExample/app/src/main/res/drawable-mdpi/ic_action_play_over_video.png new file mode 100644 index 0000000000000000000000000000000000000000..e4fb0dd3b857a26782412a11cc68ab91d664eb2a GIT binary patch literal 307 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 XwJw_4^X8Cp00000NkvXXu0mjf1ZPu8 literal 0 HcmV?d00001 diff --git a/PodServingExample/app/src/main/res/drawable-xhdpi/ic_action_play_over_video.png b/PodServingExample/app/src/main/res/drawable-xhdpi/ic_action_play_over_video.png new file mode 100644 index 0000000000000000000000000000000000000000..47d09cb075f5c47d790b8c998813ce65ba9850a6 GIT binary patch literal 709 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 B+4led literal 0 HcmV?d00001 diff --git a/PodServingExample/app/src/main/res/layout/activity_my.xml b/PodServingExample/app/src/main/res/layout/activity_my.xml new file mode 100644 index 0000000..356ca91 --- /dev/null +++ b/PodServingExample/app/src/main/res/layout/activity_my.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/PodServingExample/app/src/main/res/values-w820dp/dimens.xml b/PodServingExample/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/PodServingExample/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/PodServingExample/app/src/main/res/values/dimens.xml b/PodServingExample/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..186f1bb --- /dev/null +++ b/PodServingExample/app/src/main/res/values/dimens.xml @@ -0,0 +1,6 @@ + + + 16dp + 16dp + 16sp + diff --git a/PodServingExample/app/src/main/res/values/strings.xml b/PodServingExample/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..78e3298 --- /dev/null +++ b/PodServingExample/app/src/main/res/values/strings.xml @@ -0,0 +1,9 @@ + + + + IMA Sample Pod Serving app + Settings + Sample Video Stream + #000000 + + diff --git a/PodServingExample/build.gradle b/PodServingExample/build.gradle new file mode 100644 index 0000000..ca069a7 --- /dev/null +++ b/PodServingExample/build.gradle @@ -0,0 +1,14 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.4.0' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} diff --git a/PodServingExample/gradle.properties b/PodServingExample/gradle.properties new file mode 100644 index 0000000..5465fec --- /dev/null +++ b/PodServingExample/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/PodServingExample/gradle/wrapper/gradle-wrapper.properties b/PodServingExample/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..17655d0 --- /dev/null +++ b/PodServingExample/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/PodServingExample/gradlew b/PodServingExample/gradlew new file mode 100755 index 0000000..86d2bbc --- /dev/null +++ b/PodServingExample/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted through cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/PodServingExample/gradlew.bat b/PodServingExample/gradlew.bat new file mode 100644 index 0000000..8fca95a --- /dev/null +++ b/PodServingExample/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/PodServingExample/settings.gradle b/PodServingExample/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/PodServingExample/settings.gradle @@ -0,0 +1 @@ +include ':app'