带有ima扩展的android exoplayer无法在react native中自动显示广告的倒计时
Posted
技术标签:
【中文标题】带有ima扩展的android exoplayer无法在react native中自动显示广告的倒计时【英文标题】:android exoplayer with ima extension can't automatically show ad's countdown inside react native 【发布时间】:2020-03-15 23:34:28 【问题描述】:我使用 android exoplayer 制作了视频播放器,在我使用 ima 扩展程序的视频上显示广告。但我面临一个问题,广告无法自动显示广告的倒计时。要让视频播放器显示广告倒计时,我必须播放视频并旋转屏幕。
广告无法自动显示广告倒计时:
播放视频并旋转后,出现广告倒计时:
这是我的 ExoPlayerView :
package com.brentvatne.exoplayer;
import android.annotation.TargetApi;
import android.content.Context;
import androidx.core.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ads.AdsLoader;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.SubtitleView;
import com.google.android.exoplayer2.util.Assertions;
import java.util.List;
import java.util.ArrayList;
@TargetApi(16)
public final class ExoPlayerView extends FrameLayout implements AdsLoader.AdViewProvider
private View surfaceView;
private final View shutterView;
private final SubtitleView subtitleLayout;
private final AspectRatioFrameLayout layout;
private final ComponentListener componentListener;
private SimpleExoPlayer player;
private Context context;
private ViewGroup.LayoutParams layoutParams;
private final FrameLayout adOverlayFrameLayout;
private boolean useTextureView = true;
private boolean hideShutterView = false;
public ExoPlayerView(Context context)
this(context, null);
public ExoPlayerView(Context context, AttributeSet attrs)
this(context, attrs, 0);
public ExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
this.context = context;
layoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
componentListener = new ComponentListener();
FrameLayout.LayoutParams aspectRatioParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
aspectRatioParams.gravity = Gravity.CENTER;
layout = new AspectRatioFrameLayout(context);
layout.setLayoutParams(aspectRatioParams);
shutterView = new View(getContext());
shutterView.setLayoutParams(layoutParams);
shutterView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.black));
subtitleLayout = new SubtitleView(context);
subtitleLayout.setLayoutParams(layoutParams);
subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize();
adOverlayFrameLayout = new FrameLayout(context);
adOverlayFrameLayout.setLayoutParams(layoutParams);
updateSurfaceView();
layout.addView(shutterView, 1, layoutParams);
layout.addView(subtitleLayout, 2, layoutParams);
layout.addView(adOverlayFrameLayout, 3, layoutParams);
addViewInLayout(layout, 0, aspectRatioParams);
private void setVideoView()
if (surfaceView instanceof TextureView)
player.setVideoTextureView((TextureView) surfaceView);
else if (surfaceView instanceof SurfaceView)
player.setVideoSurfaceView((SurfaceView) surfaceView);
private void updateSurfaceView()
View view = useTextureView ? new TextureView(context) : new SurfaceView(context);
view.setLayoutParams(layoutParams);
surfaceView = view;
if (layout.getChildAt(0) != null)
layout.removeViewAt(0);
layout.addView(surfaceView, 0, layoutParams);
if (this.player != null)
setVideoView();
private void updateShutterViewVisibility()
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
// AdsLoader.AdViewProvider implementation.
@Override
public ViewGroup getAdViewGroup()
return Assertions.checkNotNull(adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback");
@Override
public View[] getAdOverlayViews()
ArrayList<View> overlayViews = new ArrayList<>();
if (adOverlayFrameLayout != null)
overlayViews.add(adOverlayFrameLayout);
return overlayViews.toArray(new View[0]);
/**
* Set the @link SimpleExoPlayer to use. The @link SimpleExoPlayer#setTextOutput and
* @link SimpleExoPlayer#setVideoListener method of the player will be called and previous
* assignments are overridden.
*
* @param player The @link SimpleExoPlayer to use.
*/
public void setPlayer(SimpleExoPlayer player)
if (this.player == player)
return;
if (this.player != null)
this.player.setTextOutput(null);
this.player.setVideoListener(null);
this.player.removeListener(componentListener);
this.player.setVideoSurface(null);
this.player = player;
shutterView.setVisibility(VISIBLE);
if (player != null)
setVideoView();
player.setVideoListener(componentListener);
player.addListener(componentListener);
player.setTextOutput(componentListener);
/**
* Sets the resize mode which can be of value @link ResizeMode.Mode
*
* @param resizeMode The resize mode.
*/
public void setResizeMode(@ResizeMode.Mode int resizeMode)
if (layout.getResizeMode() != resizeMode)
layout.setResizeMode(resizeMode);
post(measureAndLayout);
/**
* Get the view onto which video is rendered. This is either a @link SurfaceView (default)
* or a @link TextureView if the @code use_texture_view view attribute has been set to true.
*
* @return either a @link SurfaceView or a @link TextureView.
*/
public View getVideoSurfaceView()
return surfaceView;
public void setUseTextureView(boolean useTextureView)
if (useTextureView != this.useTextureView)
this.useTextureView = useTextureView;
updateSurfaceView();
public void setHideShutterView(boolean hideShutterView)
this.hideShutterView = hideShutterView;
updateShutterViewVisibility();
private final Runnable measureAndLayout = new Runnable()
@Override
public void run()
measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
;
private void updateForCurrentTrackSelections()
if (player == null)
return;
TrackSelectionArray selections = player.getCurrentTrackSelections();
for (int i = 0; i < selections.length; i++)
if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null)
// Video enabled so artwork must be hidden. If the shutter is closed, it will be opened in
// onRenderedFirstFrame().
return;
// Video disabled so the shutter must be closed.
shutterView.setVisibility(VISIBLE);
private final class ComponentListener implements SimpleExoPlayer.VideoListener,
TextRenderer.Output, ExoPlayer.EventListener
// TextRenderer.Output implementation
@Override
public void onCues(List<Cue> cues)
subtitleLayout.onCues(cues);
// SimpleExoPlayer.VideoListener implementation
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio)
boolean isInitialRatio = layout.getAspectRatio() == 0;
layout.setAspectRatio(height == 0 ? 1 : (width * pixelWidthHeightRatio) / height);
// React native workaround for measuring and layout on initial load.
if (isInitialRatio)
post(measureAndLayout);
@Override
public void onRenderedFirstFrame()
shutterView.setVisibility(INVISIBLE);
// ExoPlayer.EventListener implementation
@Override
public void onLoadingChanged(boolean isLoading)
// Do nothing.
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState)
// Do nothing.
@Override
public void onPlayerError(ExoPlaybackException e)
// Do nothing.
@Override
public void onPositionDiscontinuity(int reason)
// Do nothing.
@Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason)
// Do nothing.
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections)
updateForCurrentTrackSelections();
@Override
public void onPlaybackParametersChanged(PlaybackParameters params)
// Do nothing
@Override
public void onSeekProcessed()
// Do nothing.
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled)
// Do nothing.
@Override
public void onRepeatModeChanged(int repeatMode)
// Do nothing.
这是初始化播放器方法:
private void initializePlayer()
ReactExoplayerView self = this;
// This ensures all props have been settled, to avoid async racing conditions.
new Handler().postDelayed(new Runnable()
@Override
public void run()
if (player == null)
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
trackSelector.setParameters(trackSelector.buildUponParameters()
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
DefaultLoadControl.Builder defaultLoadControlBuilder = new DefaultLoadControl.Builder();
defaultLoadControlBuilder.setAllocator(allocator);
defaultLoadControlBuilder.setBufferDurationsMs(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs);
defaultLoadControlBuilder.setTargetBufferBytes(-1);
defaultLoadControlBuilder.setPrioritizeTimeOverSizeThresholds(true);
DefaultLoadControl defaultLoadControl = defaultLoadControlBuilder.createDefaultLoadControl();
DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext())
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
// TODO: Add drmSessionManager to 5th param from: https://github.com/react-native-community/react-native-video/pull/1445
player = ExoPlayerFactory.newSimpleInstance(getContext(), renderersFactory,
trackSelector, defaultLoadControl, null, bandwidthMeter);
player.addListener(self);
player.addMetadataOutput(self);
adsLoader.setPlayer(player);
exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(self);
bandwidthMeter.addEventListener(new Handler(), self);
setPlayWhenReady(!isPaused);
playerNeedsSource = true;
PlaybackParameters params = new PlaybackParameters(rate, 1f);
player.setPlaybackParameters(params);
if (playerNeedsSource && srcUri != null)
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(srcUri, extension);
MediaSource mediaSourceWithAds = new AdsMediaSource(videoSource, mediaDataSourceFactory, adsLoader, exoPlayerView);
MediaSource mediaSource;
if (mediaSourceList.size() == 0)
mediaSource = mediaSourceWithAds;
else
mediaSourceList.add(0, mediaSourceWithAds);
MediaSource[] textSourceArray = mediaSourceList.toArray(
new MediaSource[mediaSourceList.size()]
);
mediaSource = new MergingMediaSource(textSourceArray);
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
if (haveResumePosition)
player.seekTo(resumeWindow, resumePosition);
player.prepare(mediaSource, !haveResumePosition, false);
playerNeedsSource = false;
eventEmitter.loadStart();
loadVideoStarted = true;
// Initializing the playerControlView
initializePlayerControl();
setControls(controls);
applyModifiers();
, 1);
我的代码有什么遗漏吗?
【问题讨论】:
【参考方案1】:问题是 Google IMA Webview 在 android 上的 react native 中使用时没有显示:https://groups.google.com/forum/#!topic/ima-sdk/UUJ7du52lxY
我使用这个解决方案:React Native custom component doesn't set component's size dynamically,来解决问题
【讨论】:
以上是关于带有ima扩展的android exoplayer无法在react native中自动显示广告的倒计时的主要内容,如果未能解决你的问题,请参考以下文章
如何在 android 中更改 ExoPlayer 字幕位置?
ExoPlayer 无法播放 Adobe 实时流编码器流式传输的音频/视频 (RTMP)