betterplayer icon indicating copy to clipboard operation
betterplayer copied to clipboard

[BUG] [Android TV] HLS ABR video doesn't fill screen until resolution changes

Open trms-alex opened this issue 3 years ago • 7 comments

History check I've checked the issue history and haven't found any reports which quite match.

Describe the bug In release mode, on the new Google Chromecast with Google TV, when playing an HLS ABR video that starts out as 720p but later switches to 1080p, the video is rendered in a small portion of the upper left corner of the screen. Once the video switches to 1080p, it is rendered filling the screen as expected.

To Reproduce Steps to reproduce the behavior:

  1. Add BetterPlayer widget and allow it to fill the screen
  2. Build in release mode and install to Android/Google TV device
  3. Play an ABR video over HLS
  4. See that the video doesn't respect the fit setting and plays in the upper-left corner until the video's resolution changes

Example code

import 'package:better_player/better_player.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late BetterPlayerController _betterPlayerController;

  @override
  void initState() {
    super.initState();

    BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource(
        BetterPlayerDataSourceType.network,
        "https://stream.mux.com/LC7NoNM00LQTQRmzsSLhYhrwDSLwD8uaX8N2e4qXn8pQ.m3u8",
    );

    _betterPlayerController = BetterPlayerController(
        const BetterPlayerConfiguration(
          autoPlay: true,
          fit: BoxFit.contain,
        ),
        betterPlayerDataSource: betterPlayerDataSource);
  }

  @override
  Widget build(BuildContext context) {
    return BetterPlayer(
      controller: _betterPlayerController,
    );
  }
}

Expected behavior The video should always fill the screen as per the fit: BoxFit.contain setting.

Screenshots Before switching to 1080p quality (in practice, the first few seconds of playback): my_photo-2 After switching to 1080p quality: my_photo-3

Flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.5.3, on Fedora 34 (Workstation Edition) 5.14.11-200.fc34.x86_64, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2020.3)
[✓] Android Studio (version 4.2)
[✓] VS Code (version 1.61.1)
[✓] Connected device (3 available)

• No issues found!

Better Player version

  • Version: 0.0.77

Smartphone (please complete the following information): I understand that TV devices aren't the intended use case for better_player (or even Flutter), but other than this bug, it meets our needs quite nicely. I was hoping the author of the plugin or anyone else could provide some insight on how to fix or work around this.

  • Device: Chromecast with Google TV
  • OS: Android TV 10

The problem also happens on Fire TV sticks, but the area the video doesn't cover is solid green, strangely:

  • Device: Fire TV Stick 4K
  • OS: Fire OS 6.2.8.1

Additional context

  • It's important to note that this problem only seems to happen when the app is run in release mode. In debug mode, I wasn't able to reproduce the issue reliably (or at all).
  • It only happens on the first run of the app. Stopping the video and playing another without restarting or reinstalling the app doesn't show the same incorrect behavior.
  • 720p videos played directly from MP4 files don't show this behavior either. It only applies to ABR videos streamed through HLS.
  • It doesn't seem to matter whether better_player is "fullscreen" or not.

trms-alex avatar Oct 19 '21 21:10 trms-alex

@trms-alex Thanks for preparing such detailed description.

Unfortunately, right now I don't see any solution for that. I'm seeing same error for official video_player plugin: https://github.com/flutter/flutter/issues/85583 which hasn't been solved.

Other links: https://github.com/jhomlala/betterplayer/issues/658

I'll leave this ticket open, maybe in the future I'll find solution for that.

jhomlala avatar Oct 31 '21 16:10 jhomlala

Just wanted to throw out that I just saw this same behavior on a a simulator: image

Simulator details incase its useful:



Name: Pixel_3a_API_30_x86

CPU/ABI: Google APIs Intel Atom (x86)

Path: /Users/raytiley/.android/avd/Pixel_3a_API_30_x86.avd

Target: google_apis [Google APIs] (API level 30)

Skin: pixel_3a

SD Card: 800M

Snapshot: no

hw.dPad: no

hw.lcd.height: 2220

runtime.network.speed: full

hw.accelerometer: yes

hw.device.name: pixel_3a

vm.heapSize: 256

hw.device.manufacturer: Google

hw.lcd.width: 1080

hw.gps: yes

image.androidVersion.api: 30

hw.audioInput: yes

image.sysdir.1: system-images/android-30/google_apis/x86/

tag.id: google_apis

hw.camera.back: emulated

hw.mainKeys: no

AvdId: Pixel_3a_API_30_x86

hw.camera.front: emulated

hw.lcd.density: 440

avd.ini.displayname: Pixel_3a_API_30_x86

hw.arc: false

hw.gpu.mode: auto

snapshot.present: no

hw.device.hash2: MD5:0e6953ebf01bdc6b33a2f54746629c50

hw.ramSize: 1536

hw.trackBall: no

PlayStore.enabled: false

hw.battery: yes

hw.cpu.ncore: 6

hw.sdCard: yes

tag.display: Google APIs

runtime.network.latency: none

hw.keyboard: yes

hw.sensors.proximity: yes

disk.dataPartition.size: 6442450944

hw.sensors.orientation: yes

avd.ini.encoding: UTF-8

hw.gpu.enabled: yes

raytiley avatar Dec 14 '21 13:12 raytiley

I have the exact same problem on a Mi Android TV (Android 9) setup.

I also tested it with an emulator using @raytiley 's proposed version and settings and I confirm that the problem can be repeated on this emulator.

However I could not find why it behaves different on different devices.

I forked the repository (https://github.com/postacik/betterplayer) and upgraded Exoplayer to v2.16.1 but it did not solve the problem.

Has any of you found a solution for this problem?

postacik avatar Feb 13 '22 14:02 postacik

I extended the sample above with video_player plugin and it has the same behaviour as better_player:

import 'dart:developer';

import 'package:better_player/better_player.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _mediaFile = "https://stream.mux.com/LC7NoNM00LQTQRmzsSLhYhrwDSLwD8uaX8N2e4qXn8pQ.m3u8";
  late BetterPlayerController _betterPlayerController;
  late VideoPlayerController _videoPlayerController;

  @override
  void initState() {
    super.initState();

    BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource(BetterPlayerDataSourceType.network, _mediaFile);
    _betterPlayerController = BetterPlayerController(
      BetterPlayerConfiguration(
        autoPlay: true,
        // controlsConfiguration: const BetterPlayerControlsConfiguration(
        //   enableFullscreen: false,
        //   playerTheme: BetterPlayerTheme.cupertino,
        //   enableSkips: false,
        // ),
        // fullScreenByDefault: false,
        // expandToFill: false,
        fit: BoxFit.contain,
        eventListener: (ev) {
          if (ev.betterPlayerEventType != BetterPlayerEventType.progress && ev.betterPlayerEventType != BetterPlayerEventType.bufferingUpdate) {
            log("${ev.betterPlayerEventType}");
          }
          if (ev.betterPlayerEventType == BetterPlayerEventType.initialized) {
            var size = _betterPlayerController.videoPlayerController!.value.size;
            log("${size!.width}x${size.height}");
          }
        },
      ),
      betterPlayerDataSource: betterPlayerDataSource,
    );

    _videoPlayerController = VideoPlayerController.network(_mediaFile);
    _videoPlayerController.initialize().then((_) {
      // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
      setState(() {
        _videoPlayerController.play();
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              BetterPlayer(
                controller: _betterPlayerController,
              ),
              _videoPlayerController.value.isInitialized
                  ? GestureDetector(
                      onTap: () {
                        if (_videoPlayerController.value.isPlaying) {
                          _videoPlayerController.pause();
                        } else {
                          _videoPlayerController.play();
                        }
                      },
                      child: AspectRatio(
                        aspectRatio: _videoPlayerController.value.aspectRatio,
                        child: VideoPlayer(_videoPlayerController),
                      ),
                    )
                  : Container(),
            ],
          ),
        ),
      ),
    );
  }
}

And here is the video demonstrating the problem. The video at the top is better_player, the one at the bottom is video_player.

https://user-images.githubusercontent.com/3977227/153762613-0971a362-3903-4415-ab85-f775537747e4.mp4

postacik avatar Feb 13 '22 16:02 postacik

Similar issues have been reported for video_player like the one below:

https://github.com/flutter/flutter/issues/46460#issue-534504607

postacik avatar Feb 13 '22 17:02 postacik

Does anyone find any workaround? Please reply.

Zaied avatar Jul 04 '22 21:07 Zaied

There is a fix for this problem on master channel: https://github.com/flutter/engine/pull/24888

I tested this commit (Flutter channel master 3.1.0-0.0.pre.2125) with my TV player application.

Although compilation gave some errors complaining about missing overrides and parameters in ".pub-cache/hosted/pub.dartlang.org/fwfh_text_style-2.7.3+1/lib/fwfh_text_style.dart", I managed to fix it with small additions.

I do confirm that the problem is solved on my Mi Smart Projector running Android TV 9.0.

postacik avatar Aug 06 '22 14:08 postacik

This seems to be the same problem...

I'm using a stream with multiple resolutions, it starts the video at a low resolution and then goes to full resolution. Here I have a problem that the video does not fit the screen size, it exceeds. Then I switched to the lowest resolution and the screen looked like this: screen2

rafaelrabaco avatar Jan 06 '23 18:01 rafaelrabaco

flutter/engine#24888

There is a fix for this problem on master channel: flutter/engine#24888

I tested this commit (Flutter channel master 3.1.0-0.0.pre.2125) with my TV player application.

Although compilation gave some errors complaining about missing overrides and parameters in ".pub-cache/hosted/pub.dartlang.org/fwfh_text_style-2.7.3+1/lib/fwfh_text_style.dart", I managed to fix it with small additions.

I do confirm that the problem is solved on my Mi Smart Projector running Android TV 9.0.

How can I install this version 3.1.0-0.0.pre.2125? There are no tags with this version Captura de Tela 2023-01-06 às 16 48 09

I installed the master and still with the same problem

rafaelrabaco avatar Jan 06 '23 19:01 rafaelrabaco