带有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中自动显示广告的倒计时的主要内容,如果未能解决你的问题,请参考以下文章

在 Exoplayer IMA 扩展中保存和恢复广告进度

使用 IMASDK、android 暂停广告?

如何在 android 中更改 ExoPlayer 字幕位置?

ExoPlayer 无法播放 Adob​​e 实时流编码器流式传输的音频/视频 (RTMP)

如何从 ExoPlayer Cast 扩展创建 CastPlayer?

ExoPlayer添加Ffmpeg扩展实现软解功能