package com.kaltura.playersdk.players;
import android.app.Activity;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.libraries.mediaframework.exoplayerextensions.ExoplayerWrapper;
import com.google.android.libraries.mediaframework.exoplayerextensions.Video;
import com.google.android.libraries.mediaframework.layeredvideo.SimpleVideoPlayer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.kaltura.playersdk.utils.LogUtils.LOGD;
/**
* Created by nissopa on 7/2/15.
*/
public class KIMAAdPlayer implements VideoAdPlayer, ExoplayerWrapper.PlaybackListener{
private static final String TAG = "KIMAAdPlayer";
private ViewGroup mAdUIContainer;
private String mAdMimeType;
private int mAdPreferredBitrate;
private FrameLayout mPlayerContainer;
private Activity mActivity;
private SimpleVideoPlayer mAdPlayer;
private KState mReadiness = KState.IDLE;
private KIMAAdPlayerEvents mListener;
private String mSrc;
private boolean isSeeking;
private int currentPosition;
private final List<VideoAdPlayerCallback> mAdCallbacks =
new ArrayList<VideoAdPlayerCallback>(1);
private static final long PLAYHEAD_UPDATE_INTERVAL = 200;
@NonNull
private Handler mPlaybackTimeReporter = new Handler(Looper.getMainLooper());
// [START VideoAdPlayer region]
@Override
public void playAd() {
if (mAdPlayer != null) {
mAdPlayer.play();
startPlaybackTimeReporter();
}
}
@Override
public void loadAd(String s) {
setAdPlayerSource(s);
}
@Override
public void stopAd() {
if (mAdPlayer != null) {
mAdPlayer.pause();
}
}
@Override
public void pauseAd() {
if (mAdPlayer != null) {
stopPlaybackTimeReporter();
mAdPlayer.pause();
}
}
@Override
public void resumeAd() {
if (mAdPlayer != null) {
mAdPlayer.play();
}
}
@Override
public void addCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
mAdCallbacks.add(videoAdPlayerCallback);
}
@Override
public void removeCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
mAdCallbacks.remove(videoAdPlayerCallback);
}
@Override
public VideoProgressUpdate getAdProgress() {
if (mAdPlayer == null || mAdPlayer.getDuration() <= 0) {
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
}
return new VideoProgressUpdate(mAdPlayer.getCurrentPosition(), mAdPlayer.getDuration());
}
public void pauseAdCallback(){
for (VideoAdPlayer.VideoAdPlayerCallback callback : mAdCallbacks) {
callback.onPause();
}
}
public void resumeAdCallback(){
for (VideoAdPlayer.VideoAdPlayerCallback callback : mAdCallbacks) {
callback.onResume();
}
}
private void startPlaybackTimeReporter() {
mPlaybackTimeReporter.removeMessages(0); // Stop reporter if already running
mPlaybackTimeReporter.post(new Runnable() {
@Override
public void run() {
if (mAdPlayer != null) {
maybeReportPlaybackTime();
mPlaybackTimeReporter.postDelayed(this, PLAYHEAD_UPDATE_INTERVAL);
}
}
});
}
private void stopPlaybackTimeReporter() {
LOGD(TAG, "remove handler callbacks");
mPlaybackTimeReporter.removeMessages(0);
}
private void maybeReportPlaybackTime() {
if (mListener != null) {
mListener.adDidProgress((float)mAdPlayer.getCurrentPosition() / 1000, (float)mAdPlayer.getDuration() / 1000);
}
}
// [END VideoAdPlayer region]
// [START ExoplayerWrapper.PlaybackListener region]
@Override
public void onStateChanged(boolean playWhenReady, int playbackState) {
switch (playbackState) {
case ExoPlayer.STATE_READY:
if (playWhenReady) {
if (mReadiness != KState.READY) {
mReadiness = KState.READY;
updateAdVideoTrackQuality();
mListener.adDurationUpdate((float) mAdPlayer.getDuration() / 1000);
}
for (VideoAdPlayer.VideoAdPlayerCallback callback : mAdCallbacks) {
callback.onPlay();
}
} else if (currentPosition > 0) {
mAdPlayer.seek(currentPosition, true);
isSeeking = true;
currentPosition = 0;
} else if (isSeeking) {
isSeeking = false;
mAdPlayer.play();
} else {
for (VideoAdPlayer.VideoAdPlayerCallback callback : mAdCallbacks) {
callback.onPause();
}
}
break;
case ExoPlayer.STATE_ENDED:
removeAd();
for (VideoAdPlayer.VideoAdPlayerCallback callback : mAdCallbacks) {
callback.onEnded();
}
mReadiness = KState.IDLE;
break;
}
}
private void updateAdVideoTrackQuality() {
if (KMediaFormat.hls_clear.mimeType.equals(mAdMimeType) && mAdPreferredBitrate != -1) {
Map<Integer,Integer> videoTrackBitrateMap = mAdPlayer.getAvailableBitrateMap();
int bitrateIndex = -1;
List<Integer> videoTrackBitrateSortedKeys = new ArrayList(videoTrackBitrateMap.keySet());
Collections.sort(videoTrackBitrateSortedKeys);
int selectTrackStratIndex = 0;
if (videoTrackBitrateSortedKeys.get(0) == -1) {
selectTrackStratIndex = 1;
}
for (int i = 0; i < videoTrackBitrateSortedKeys.size(); i++) {
LOGD(TAG, i +"-"+ videoTrackBitrateSortedKeys.size() + " HLS Bitrate[" + i + "] = " + videoTrackBitrateSortedKeys.get(i));
if (i > selectTrackStratIndex && videoTrackBitrateSortedKeys.get(i) > mAdPreferredBitrate) {
bitrateIndex = i - 1;
LOGD(TAG, "HLS selected bitrate = " + videoTrackBitrateSortedKeys.get(bitrateIndex));
mAdPlayer.changeTrack(ExoplayerWrapper.TYPE_VIDEO, videoTrackBitrateMap.get(videoTrackBitrateSortedKeys.get(bitrateIndex)));
break;
}
if (i > selectTrackStratIndex && i == videoTrackBitrateSortedKeys.size()-1) {
LOGD(TAG, "HLS selected last bitrate = " + videoTrackBitrateSortedKeys.get(i));
mAdPlayer.changeTrack(ExoplayerWrapper.TYPE_VIDEO, videoTrackBitrateMap.get(videoTrackBitrateSortedKeys.get(i)));
break;
}
}
}
}
@Override
public void onError(Exception e) {
for (VideoAdPlayer.VideoAdPlayerCallback callback : mAdCallbacks) {
callback.onError();
}
}
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
}
// [END ExoplayerWrapper.PlaybackListener region]
public interface KIMAAdPlayerEvents {
void adDidProgress(float toTome, float totalTime);
void adDurationUpdate(float totalTime);
}
public KIMAAdPlayer(Activity activity, FrameLayout playerContainer, ViewGroup adUIContainer, String adMimeType, int adPreferredBitrate) {
mActivity = activity;
mPlayerContainer = playerContainer;
mAdUIContainer = adUIContainer;
mAdMimeType = adMimeType;
mAdPreferredBitrate = adPreferredBitrate;
}
public void resume() {
setAdPlayerSource(mSrc);
}
public void pause() {
if(mAdPlayer != null) {
currentPosition = mAdPlayer.getCurrentPosition();
}
removeAd();
}
public void setKIMAAdEventListener(KIMAAdPlayerEvents listener) {
mListener = listener;
}
public ViewGroup getAdUIContainer() {
return mAdUIContainer;
}
private void setAdPlayerSource(String src) {
mSrc = src;
Video source = new Video(src.toString(), getVideoType());
mAdPlayer = new SimpleVideoPlayer(mActivity, mPlayerContainer, source, "", true);
mAdPlayer.addPlaybackListener(this);
mPlayerContainer.setVisibility(View.VISIBLE);
mAdPlayer.moveSurfaceToForeground();
mAdPlayer.disableSeeking();
mAdPlayer.hideTopChrome();
}
public void removeAd() {
if (mAdPlayer != null) {
mAdPlayer.release();
mAdPlayer.moveSurfaceToBackground();
mPlayerContainer.setVisibility(View.INVISIBLE);
mAdPlayer = null;
}
}
public void release() {
if (mAdPlayer != null) {
mAdPlayer.pause();
mAdPlayer.moveSurfaceToBackground();
}
// mAdUIContainer = null;
// mPlayerContainer = null;
}
private Video.VideoType getVideoType() {
String videoFileName = Uri.parse(mSrc).getLastPathSegment();
switch (videoFileName.substring(videoFileName.lastIndexOf('.')).toLowerCase()) {
case ".mpd":
return Video.VideoType.DASH;
case ".mp4":
return Video.VideoType.MP4;
case ".m3u8":
return Video.VideoType.HLS;
default:
return Video.VideoType.OTHER;
}
}
}