Tubi Engineering

Engineering blog for Tubi TV

Follow publication

How a Seamless Play Single-Player Improved the Tubi Viewing Experience

Shaowei Chang
Tubi Engineering
Published in
7 min readAug 31, 2024

Shaowei Chang and Xin Zheng

At Tubi, one of our primary goals is to provide viewers with a smooth playback experience. To achieve this, our Android Team has implemented seamless playback for both video and ads on a single-player system.

Overview

Previously, we used two separate players — one for video content and one for ads. During transitions, the previous player was released, and a new player was initialized. However, releasing the first player before initializing the second led to a buffering issue because no data was preloaded during the switch. This caused the video to pause, resulting in delays like black screens and wait times until the app caught up.

Ad and video content transition comparisons

The following videos highlight how the two-player system compares to the single-player system during transitions. Each segment demonstrates the difference between an ‘Original’ two-player system and a ‘Seamless’ single-player system.

Segment 1 compares the effects of transitioning between video content and ads:

  • The top video, labeled ‘Original’ shows a black screen during a video content-to-ad transition.
  • The bottom video, labeled ‘Seamless’ shows a smooth, uninterrupted transition from video content to ad.
The ‘Original’ video shows how a switch from video content to ad using a two-player system results in a black screen, while the ‘Seamless’ video demo using a single-player shows a smooth transition.

Segment 2 compares the effects of transitioning between ads and video content:

  • The top video, labeled ‘Original’ shows a black screen and buffering during an ad-to-video content transition.
  • The bottom video, labeled ‘Seamless’ shows a smooth, uninterrupted transition during an ad-to-video content transition.
The ‘Original’ video shows how switching from ad to video content using a two-player system results in a black screen and buffering while the ‘Seamless’ video using a single-player shows a smooth transition.

Implementation

The Tubi Android team developed playback-related features using Media3/ExoPlayer. To support ad playback, we referenced the third-party ads SDK section and implemented a custom AdsLoader that integrates our ads SDK with these features.

ExoPlayer ensures seamless play with a single-player system by maintaining a unified playback session. As the current content nears its end, it preloads the next piece of content to ensure it is ready to play immediately upon switching. ExoPlayer follows a buffering strategy to guarantee smooth and uninterrupted transitions between different content types.

Seamless play single-player experiment results

The primary and playback metric results from the seamless play single-player experiment are as follows.

Significant gains in primary metrics

When the experiment was conducted, there was a significant, positive lift observed in key primary metrics such as ad impressions, TVT capped, and gross revenue.

Significant improvements in playback metrics

When the experiment was conducted, there were several positive improvements observed in the playback metrics. These include:

ExoTimeoutException — A significant reduction in ExoTimeoutException demonstrates that the adjustments made to enhance seamless playback have been effective.

Ad Join Time — The treatment group experienced a significant drop in Ad Join Time due to the seamless transition, meaning ads now start more quickly.

Ad Drop Percentage — The treatment group experienced a significant decrease in the percentage of viewers dropping due to ads, indicating that viewers are less likely to abandon content because of ad-related issues.

Content Playback Errors — The treatment group experienced a significant drop in Content Playback Errors, including both startup and in-stream errors.

Ad Playback Errors — A significant drop in Ad Playback Errors suggests that Tubi’s switch from a two-player setup to a seamless play single-player has improved the reliability and performance of ad playback.

Seamless play single-player transition issues

Despite the encouraging results we observed during the seamless play single-player transition, a few technical challenges remained that required our attention. Since no known solutions for these issues were available, we decided to develop a few of our own.

This section describes the key obstacles we encountered and the solutions that we implemented to ensure seamless content and ad playback.

Decoder init failed error

The seamless play single-player system has significantly more decoder init failed errors on Android TV compared to the two-player system. The errors happen when transitioning from Digital Rights Management (DRM) content to clear ads, but they do not occur on Android phones.

ExoPlayer addressed a similar issue by implementing a solution that involved sleeping for 50 ms before retrying a failed decoder creation.

For more details, refer to the discussion on GitHub here.

Solution

Tubi developed a similar solution by creating a custom MediaCodecFactory and RenderersFactory to override MediaCodec.createAdapter() and extend the sleep duration when a MediaCodec instance fails during the creation process. This approach significantly reduced the number of decoder init failed errors.

Result (before and after applying the solution)

The following two images display the frequency of decoder initialization errors (error code 4001) before and after the solution was applied, highlighting the significant decrease in errors after the solution was applied.

Slight buffering occurs at non-keyframe ad points

When playback reaches an ad point at a non-keyframe position, a brief buffer occurs because the content piece after the ad has to re-decode everything from the previous keyframe before continuing. While waiting for this to happen, the player enters STATE_BUFFERING because it is not progressing.

For more details, refer to the discussion on GitHub here.

Video demo

The following video demo shows the slight buffering that occurs at non-keyframe ad points in the video content under smooth network conditions, even when the ad is not filled.

The video is played at 0.4 speed, and the playback state displayed on the bottom of the screen switches from ready to buffering and back to ready.

This video demo shows a brief buffering at the ad point in the video content.

Current status

There is currently no solution available for the slight buffering that occurs at non-keyframe ad points. According to the Androidx/Media3 library, the only way to achieve perfect smoothing is by preparing a parallel second decoder. However, this process is complex and time-consuming, so it remains a feature request for now.

A video frame freezes at the ad cue point when loading .srt subtitles

When streaming movies with .srt subtitles using ExoPlayer, the video frame freezes at the next ad point after passing an unfilled ad slot, regardless of whether the next ad slot is filled. When this occurs, no audio plays, although the subtitles continue to display correctly. The issue can be temporarily resolved by fast-forwarding past the frozen frame.

For more details, refer to the discussion on GitHub here.

Video demo

The following video demo shows a frame freezing at the ad cue point when loading .srt subtitles.

This demo shows how a video frame freezes at the ad cue point when .srt subtitles are loading.

Solution

SettingDefaultMediaSourceFactory.experimentalUseProgressiveMediaSourceForSubtitles to true prevented the frame from freezing at the ad cue point when loading .srt subtitles.

Subtitles with .srt format disappear and reappear

When streaming movies with .srt subtitle, setting DefaultMediaSourceFactory.experimentalUseProgressiveMediaSourceForSubtitles to true in ExoPlayer causes some closed captions to disappear.

For more details, refer to the discussion on GitHub here.

Video demo

In the following video demo, the subtitles appear, disappear, and reappear due to timing conflicts. You can also see these timing conflicts in the code snippet that follows the video demo.

This demo shows how subtitles appear, disappear, and reappear due to timing conflicts.

Code snippet

The following code snippet illustrates that subtitles 24 to 26 from the above video demo are not displaying due to timing conflicts. This issue occurs because ExoPlayer cannot handle cases where the start time of the current subtitle exactly matches the end time of the previous one.

22                                          (subtitle id)
00:01:35,301 --> 00:01:37,037 (subtitle duration startTime-endTime)
And then, no, no, no, no, no, no, no, no. (subtitle content)

23
00:01:37,137 --> 00:01:38,371
Fuck you. Don't talk. <------- Show
(00:01:37,037 ≠ 00:01:37,137)

24
00:01:38,371 --> 00:01:40,706
Keep your mouth shut. Nod, smile, and be chill. <------- Disappear
(00:01:38,371 = 00:01:38,371)

25
00:01:40,706 --> 00:01:43,776
Be chill and blend in. Okay? <------- Disappear
(00:01:40,706 = 00:01:40,706)

26
00:01:43,776 --> 00:01:47,247
You've got this. This is your day. <------- Disappear
(00:01:43,776 = 00:01:43,776)

27
00:01:47,347 --> 00:01:49,149
You good? <------- Show


28
00:01:49,249 --> 00:01:50,883
So good. <------- Show

Solution

Although the latest version of Androidx/Media3 resolves the issue of .srt subtitles appearing and reappearing, we encountered other limitations that required us to develop solutions before fully migrating to Media3. We addressed these challenges, as follows.

  • Subtitles Fixed in ExoPlayer — We resolved the issue of .srt subtitles appearing and reappearing in ExoPlayer by increasing the start time of each subtitle by 1 microsecond when parsing .srt files.
  • Subtitles Fixed in Media3 Migration — We have already migrated to Media3 on AndroidTV, which resolved the .srt subtitle issue. The next step is to migrate to Media3 on Android Phone.

Our solution ensures that the end time of the previous subtitle does not match the start time of the current one, preventing the display issue.

      Matcher matcher = SUBRIP_TIMING_LINE.matcher(currentLine);
if (matcher.matches()) {
// Modify the startTime=startTime+1 of the subtitle
cueTimesUs.add(parseTimecode(matcher, /* groupOffset= */ 1) + 1);
cueTimesUs.add(parseTimecode(matcher, /* groupOffset= */ 6) );
} else {
Log.w(TAG, "Skipping invalid timing: " + currentLine);
continue;
}

Summary

By replacing the two-player system with a seamless play single-player, When the network is smooth, Tubi viewers no longer have to experience black screens or buffering when switching between video content and ads.

We’re Hiring!

Tubi’s Android Team successfully replaced a two-player system that caused black screens and buffering during back-and-forth transitions between video content and ads with a single-player system that ensures smooth, uninterrupted playback. The journey was challenging, but the results have been rewarding, and we’ve got more exciting work ahead.

Did you find our seamless playback journey as intriguing as we did? If so, why not join us? Tubi Engineering is hiring! You can check out Tubi careers here.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response