Dual Player Activity

The **Dual Player Activity** module implements a specialized player activity that manages two separate ExoPlayer instances simultaneously: one dedicated to content playback and the other dedicated to advertisement playback. This design supports seamless integration and concurrent control of video content and ads, enabling advanced playback features such as preroll, midroll, and postroll ads with smooth transitions.


Overview

The core class for this module is `DoubleViewTubiPlayerActivity`, which extends a base player activity and implements interfaces for dual player control and auto-play behavior. This activity encapsulates the logic for initializing, managing, and releasing two distinct media players, coordinating them through a finite state machine (FSM) player (`FsmPlayer`) that orchestrates playback states and transitions.

Key features include:


Dual Player Setup

Purpose

Managing two separate ExoPlayer instances allows the system to:

How It Works

  1. Content Player Initialization

    The content player (mMoviePlayer inherited from base class) is initialized in the usual manner for video playback.

  2. Ad Player Initialization

    The ad player (adPlayer) is initialized with its own track selector and bandwidth meter:

    private void setupAdPlayer() {
        TrackSelection.Factory adaptiveTrackSelectionFactory =
            new AdaptiveTrackSelection.Factory(BANDWIDTH_METER_AD);
        trackSelector_ad = new DefaultTrackSelector(adaptiveTrackSelectionFactory);
        adPlayer = ExoPlayerFactory.newSimpleInstance(this, trackSelector_ad);
    }
    

    This separate initialization ensures that ad playback can be controlled independently.

  3. Media Source Preparation

    Both content and ad media models have their media sources prepared separately via:

    protected void createMediaSource(MediaModel videoMediaModel) {
        videoMediaModel.setMediaSource(buildMediaSource(videoMediaModel));
    }
    
    @Override
    public void onPrepareAds(@Nullable AdMediaModel adMediaModel) {
        for (MediaModel singleMedia : adMediaModel.getListOfAds()) {
            MediaSource adMediaSource = buildMediaSource(singleMedia);
            singleMedia.setMediaSource(adMediaSource);
        }
    }
    
  4. Player Release and Resume Position Tracking

    Both players track their playback positions and save resume information when released, allowing seamless resumption:

    private void updateAdResumePosition() {
        if (adPlayer != null && playerUIController != null) {
            int adResumeWindow = adPlayer.getCurrentWindowIndex();
            long adResumePosition = adPlayer.isCurrentWindowSeekable() ? Math.max(0, adPlayer.getCurrentPosition())
                    : C.TIME_UNSET;
            playerUIController.setAdResumeInfo(adResumeWindow, adResumePosition);
        }
    }
    

    The content player resumes similarly in updateResumePosition().


Dependency Injection Integration

The activity leverages Dagger 2 to inject dependencies essential for playback and ad management:


Media Selection Interface

The `SelectionActivity` provides a simple UI for selecting media content to play using the dual player activity. It demonstrates how media models are passed to the player activity via intents:

Intent intent = new Intent(SelectionActivity.this, DoubleViewTubiPlayerActivity.class);
intent.putExtra(TubiPlayerActivity.TUBI_MEDIA_KEY, MediaModel.video(name, VIDEO_URL, artwork, null));
startActivity(intent);

This interface enables launching the dual player with different media content, showcasing the modularity and reusability of the dual player activity.


Key Workflows and Interactions

FSM Preparation and Playback Coordination

The `prepareFSM()` method ties together the FSM player and UI controllers with the dual players:

@Override
public void prepareFSM() {
    playerUIController.setContentPlayer(mMoviePlayer);

    if (!PlayerDeviceUtils.useSinglePlayer()) {
        playerUIController.setAdPlayer(adPlayer);
    }

    playerUIController.setExoPlayerView(mTubiPlayerView);
    playerUIController.setVpaidWebView(vpaidWebView);

    fsmPlayer.setController(playerUIController);
    fsmPlayer.setMovieMedia(mediaModel);
    fsmPlayer.setAdRetriever(adRetriever);
    fsmPlayer.setCuePointsRetriever(cuePointsRetriever);
    fsmPlayer.setAdServerInterface(adInterface);

    playerComponentController.setAdPlayingMonitor(adPlayingMonitor);
    playerComponentController.setTubiPlaybackInterface(this);
    playerComponentController.setDoublePlayerInterface(this);
    playerComponentController.setCuePointMonitor(cuePointMonitor);
    playerComponentController.setVpaidClient(vpaidClient);
    fsmPlayer.setPlayerComponentController(playerComponentController);
    fsmPlayer.setLifecycle(getLifecycle());

    if (fsmPlayer.isInitialized()) {
        fsmPlayer.updateSelf();
        Utils.hideSystemUI(this, true);
    } else {
        fsmPlayer.transit(Input.INITIALIZE);
    }
}

UI Interaction and Playback Events

The activity implements callbacks like `onProgress()`, `onSeek()`, `onPlayToggle()`, and `onLearnMoreClick()` to handle user interactions and playback updates, forwarding these events to monitors and the FSM as needed.

For example, cue points are displayed via a UI indicator:

@Override
public void onCuePointReceived(long[] cuePoints) {
    cuePointIndictor.setText(printCuePoints(cuePoints));
}

Design Patterns and Concepts


Mermaid Flowchart: Dual Player Activity Workflow

flowchart TD
    Start[Activity Created] --> Inject[Inject Dependencies]
    Inject --> PrepareDep[Prepare Dependencies]
    PrepareDep --> InitContent[Initialize Content Player]
    InitContent --> CheckDevice{Use Single Player?}
    CheckDevice -->|No| InitAd[Initialize Ad Player]
    CheckDevice -->|Yes| SkipAd[Skip Ad Player Init]
    InitAd --> PrepareFSM[Prepare FSM Player]
    SkipAd --> PrepareFSM
    PrepareFSM --> LoadMedia[Load Content Media Source]
    LoadMedia --> StartPlayback[Start Playback]
    StartPlayback -->|Ad Cue Point Triggered| AdFetch[Fetch Ad Media Source]
    AdFetch --> AdPlay[Play Ad on Ad Player]
    AdPlay --> AdEnd[Ad Playback Finished]
    AdEnd --> ResumeContent[Resume Content Playback]
    ResumeContent --> PlaybackProgress[Monitor Playback Progress]
    PlaybackProgress -->|Midroll Cue Point| AdFetch
    PlaybackProgress -->|Content Ended| End[Playback Finished]
    End --> Release[Release Players and Resources]

This flowchart illustrates the lifecycle of the dual player activity from creation through ad and content playback cycles until release.


Summary

The Dual Player Activity is a critical module enabling simultaneous management of content and advertisement playback through separate ExoPlayer instances. It leverages FSM-driven playback control, dependency injection, and rich UI integration to deliver a robust user experience with advanced ad insertion capabilities. The design supports modularity, extensibility, and clear separation of responsibilities, accommodating complex playback scenarios including VPAID ads.