Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[video player] weird behaviour when the download speed is low #147438

Open
moham96 opened this issue Apr 26, 2024 · 10 comments
Open

[video player] weird behaviour when the download speed is low #147438

moham96 opened this issue Apr 26, 2024 · 10 comments
Labels
found in release: 3.19 Found to occur in 3.19 found in release: 3.22 Found to occur in 3.22 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: video_player The Video Player plugin P2 Important issues not at the top of the work list package flutter/packages repository. See also p: labels. team-ecosystem Owned by Ecosystem team triaged-ecosystem Triaged by Ecosystem team

Comments

@moham96
Copy link

moham96 commented Apr 26, 2024

Steps to reproduce

first simulate slow internet connection or slow server:
1- install some bandwidth limiter, for me i used the official network link conditioner.
2- set the bandwidth limiter to something really low, i have set mine to 300 kbps download

next test the video buffering
1- fire up some video player example, i will attach the example i used.
2- give it some time to buffer some part of the video but not the whole video, theoretically it should be able to play the video once it has buffered a part of it, but in this case, it can't for some reason

Expected results

I expect the player to act the same regardless of user connection speed or server speed, in other words it should be able to play the video as soon as some part of it is buffered

Actual results

Currently the player doesn't play the video until the whole video is buffered, watch the attached below video, it is very important in showing the issue

Code sample

video player example that i used
import 'dart:developer';

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

const mainColor = Color(0xFFF09636);

extension SaneFormat on Duration {
  String saneFormat() {
    return "${inHours.toString().padLeft(2, '0')}:${inMinutes.remainder(60).toString().padLeft(2, '0')}:${inSeconds.remainder(60).toString().padLeft(2, '0')}";
  }
}

class VideoPlayerScreen extends StatefulWidget {
  const VideoPlayerScreen({super.key});

  @override
  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  bool showcontrols = true;
  VideoPlayerController? videoController;
  bool isInitializing = false;
  bool isPortrait = true;
  double playerControlSize = 40;
  List<String> translationNames = [];
  List<String> resolutionNames = [];

  videoControllerListener() {
    setState(() {});
    if (videoController!.value.hasError) {
      print('video error');
      initVideo();
    }
  }

  reloadVideo() async {
    Uri? videoUrl;

    videoUrl = Uri.parse(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4');

    videoController = VideoPlayerController.networkUrl(videoUrl);
    videoController!.addListener(videoControllerListener);
    initVideo();
  }

  initVideo() {
    if (isInitializing) return;
    isInitializing = true;
    videoController!.initialize().then((_) {
      videoController!.setVolume(0);
      videoController!.play();
    }).onError((e, st) {
      log('got error', error: e);
      initVideo();
    }).whenComplete(() {
      isInitializing = false;
    });
  }

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

  @override
  void dispose() async {
    super.dispose();
    videoController?.removeListener(videoControllerListener);
    await videoController?.dispose();
  }

  seekForward() async {
    videoController!.seekTo(
        (await videoController!.position)! + const Duration(seconds: 10));
  }

  seekBackwards() async {
    videoController!.seekTo(
        (await videoController!.position)! - const Duration(seconds: 10));
  }

  @override
  Widget build(BuildContext context) {
    var problem =
        videoController == null || !videoController!.value.isInitialized;

    playerControlSize = 40;
    return SafeArea(
      child: DefaultTextStyle(
        style: const TextStyle(color: Colors.white),
        child: Stack(
          children: [
            videoController != null
                ? Center(
                    child: AspectRatio(
                      aspectRatio: videoController!.value.aspectRatio,
                      child: VideoPlayer(videoController!),
                    ),
                  )
                : const Center(child: CircularProgressIndicator()),
            GestureDetector(
              onTap: () {
                setState(() {
                  showcontrols = !showcontrols;
                });
              },
              child: Container(
                color: Colors.transparent,
              ),
            ),
            Visibility(
              visible: showcontrols,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Align(
                    alignment: Alignment.topLeft,
                    child: Padding(
                      padding: const EdgeInsets.all(15),
                      child: Container(
                        color: Colors.black45,
                        child: ValueListenableBuilder(
                          valueListenable: videoController!,
                          builder: (context, value, child) => RichText(
                            text: TextSpan(
                              text: 'Stats',
                              children: [
                                const TextSpan(text: '\n'),
                                TextSpan(
                                    text:
                                        'isBuffering: ${value.isBuffering}\n'),
                                TextSpan(
                                    text:
                                        'isCompleted: ${value.isCompleted}\n'),
                                TextSpan(
                                    text:
                                        'isInitialized: ${value.isInitialized}\n'),
                                TextSpan(
                                    text: 'isLooping: ${value.isLooping}\n'),
                                TextSpan(
                                    text: 'isPlaying: ${value.isPlaying}\n'),
                                TextSpan(text: 'hasError: ${value.hasError}\n'),
                                TextSpan(
                                    text:
                                        'errorDescription: ${value.errorDescription}\n'),
                              ],
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                  Directionality(
                    textDirection: TextDirection.ltr,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        Material(
                          color: Colors.transparent,
                          child: IconButton(
                              onPressed: seekBackwards,
                              icon: Icon(
                                Icons.fast_rewind,
                                color: mainColor,
                                size: playerControlSize,
                              )),
                        ),
                        problem
                            ? const Icon(
                                Icons.block,
                                color: Colors.red,
                                size: 30,
                              )
                            : Material(
                                color: Colors.transparent,
                                child: IconButton(
                                  onPressed: () {
                                    videoController!.value.isPlaying
                                        ? videoController?.pause()
                                        : videoController?.play();

                                    setState(() {});
                                  },
                                  icon: Icon(
                                    videoController != null
                                        ? videoController!.value.isPlaying
                                            ? Icons.pause
                                            : Icons.play_arrow
                                        : Icons.sync_problem,
                                    color: mainColor,
                                    size: playerControlSize,
                                  ),
                                ),
                              ),
                        Material(
                          color: Colors.transparent,
                          child: IconButton(
                              onPressed: seekForward,
                              icon: Icon(
                                Icons.fast_forward,
                                color: mainColor,
                                size: playerControlSize,
                              )),
                        ),
                      ],
                    ),
                  ),
                  Container(
                    margin: const EdgeInsets.symmetric(horizontal: 5),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        videoController != null
                            ? ValueListenableBuilder(
                                valueListenable: videoController!,
                                builder: ((context, value, child) {
                                  return Text(value.duration.saneFormat());
                                }),
                              )
                            : const SizedBox.shrink(),
                        Directionality(
                          textDirection: TextDirection.ltr,
                          child: Expanded(
                            child: Container(
                              margin:
                                  const EdgeInsets.only(left: 10, right: 10),
                              child: videoController != null
                                  ? VideoProgressIndicator(
                                      videoController!,
                                      allowScrubbing: true,
                                      colors: const VideoProgressColors(
                                        playedColor: mainColor,
                                        bufferedColor: Colors.red,
                                      ),
                                    )
                                  : const SizedBox.shrink(),
                            ),
                          ),
                        ),
                        videoController != null
                            ? ValueListenableBuilder(
                                valueListenable: videoController!,
                                builder: ((context, value, child) {
                                  return Text(value.position.saneFormat());
                                }),
                              )
                            : const SizedBox.shrink(),
                      ],
                    ),
                  )
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

https://streamable.com/y53yra

i couldn't upload the video here because of size limit

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.19.5, on macOS 14.4.1 23E224 darwin-arm64, locale
    en-IQ)
    • Flutter version 3.19.5 on channel stable at /Users/rhx/dev/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 300451adae (4 weeks ago), 2024-03-27 21:54:07 -0500
    • Engine revision e76c956498
    • Dart version 3.3.3
    • DevTools version 2.31.1

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/rhx/Library/Android/sdk
    • Platform android-34, build-tools 34.0.0
    • Java binary at: /Applications/Android
      Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build
      17.0.9+0-17.0.9b1087.7-11185874)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15E204a
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2023.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build
      17.0.9+0-17.0.9b1087.7-11185874)

[✓] VS Code (version 1.88.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.86.0

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-arm64   • macOS 14.4.1 23E224
      darwin-arm64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 124.0.6367.92

[✓] Network resources
    • All expected network resources are available.

• No issues found!
@moham96
Copy link
Author

moham96 commented Apr 27, 2024

tested on MacOS and Android emulator with the same results, don't know about other platforms

@huycozy huycozy added the in triage Presently being triaged by the triage team label Apr 29, 2024
@huycozy
Copy link
Member

huycozy commented Apr 29, 2024

Thanks for the report. I can also reproduce this on iOS and macOS app targets. Current package version video_player: ^2.8.6.

For Android platform, this could be related to google/ExoPlayer#7413.

flutter doctor -v (stable and master)
[✓] Flutter (Channel stable, 3.19.6, on macOS 14.1 23B74 darwin-x64, locale en-VN)
    • Flutter version 3.19.6 on channel stable at /Users/huynq/Documents/GitHub/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 54e66469a9 (31 hours ago), 2024-04-17 13:08:03 -0700
    • Engine revision c4cd48e186
    • Dart version 3.3.4
    • DevTools version 2.31.1

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/huynq/Library/Android/sdk
    • Platform android-34, build-tools 34.0.0
    • ANDROID_HOME = /Users/huynq/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
    • Xcode at /Applications/Xcode15.3.app/Contents/Developer
    • Build 15E204a
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2023.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • android-studio-dir = /Applications/Android Studio.app/
    • Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)

[✓] VS Code (version 1.88.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.86.0

[✓] Connected device (3 available)
    • RMX2001 (mobile) • EUYTFEUSQSRGDA6D • android-arm64  • Android 11 (API 30)
    • macOS (desktop)  • macos            • darwin-x64     • macOS 14.1 23B74 darwin-x64
    • Chrome (web)     • chrome           • web-javascript • Google Chrome 123.0.6312.124

[✓] Network resources
    • All expected network resources are available.

• No issues found!
[!] Flutter (Channel master, 3.22.0-18.0.pre.53, on macOS 14.1 23B74 darwin-x64, locale en-VN)
    • Flutter version 3.22.0-18.0.pre.53 on channel master at /Users/huynq/Documents/GitHub/flutter_master
    ! Warning: `flutter` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path.
    ! Warning: `dart` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path.
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 098e7e7ff3 (2 hours ago), 2024-04-29 01:25:19 +0000
    • Engine revision 752b146df7
    • Dart version 3.5.0 (build 3.5.0-109.0.dev)
    • DevTools version 2.35.0-dev.16
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/huynq/Library/Android/sdk
    • Platform android-34, build-tools 34.0.0
    • ANDROID_HOME = /Users/huynq/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
    • Xcode at /Applications/Xcode15.3.app/Contents/Developer
    • Build 15E204a
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2023.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • android-studio-dir = /Applications/Android Studio.app/
    • Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)

[✓] VS Code (version 1.88.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.86.0

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-x64     • macOS 14.1 23B74 darwin-x64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 124.0.6367.92

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 1 category.

@huycozy huycozy added p: video_player The Video Player plugin package flutter/packages repository. See also p: labels. team-ecosystem Owned by Ecosystem team has reproducible steps The issue has been confirmed reproducible and is ready to work on found in release: 3.19 Found to occur in 3.19 found in release: 3.22 Found to occur in 3.22 and removed in triage Presently being triaged by the triage team labels Apr 29, 2024
@moham96
Copy link
Author

moham96 commented Apr 29, 2024

Just wanted to add something that might not be very clear in the original post;

  • The bandwidth limiter will limit the user's bandwidth but this was not the issue in my use cases, in my use case the user has good bandwidth but the server has a delay that is inherent to the nature of the server (e.g the server is relaying the video from another source, so an inherent delay is there because the server is downloading and then re-streaming).

  • The bug can be reproduced/tested without the bandwidth limiter by coding a simple HTTP server with a delay to reflect real world scenarios

@moham96
Copy link
Author

moham96 commented Apr 29, 2024

Tested the issue with media_kit on MacOS which should be using different platform implementation and it also suffers from this issue

@stuartmorgan stuartmorgan added P2 Important issues not at the top of the work list triaged-ecosystem Triaged by Ecosystem team labels Apr 30, 2024
@stuartmorgan
Copy link
Contributor

This may be underlying ExoPlayer/AVPlayer behavior, rather than something the plugin can control. It will need investigation and comparison to non-Flutter video apps using the same libraries.

@huycozy
Copy link
Member

huycozy commented May 3, 2024

@moham96 Could you help to check this with native players as mentioned above?

I was testing this with AVPlayer using this online example on iOS device, I had to set 500 kbps download because it's really slow to fetch data with 300 kbps; the issue doesn't seem to appear as I still can play/pause video.

@moham96
Copy link
Author

moham96 commented May 4, 2024

@huycozy i did a quick test using the provided sample and it does show the same issue, but there could be something wrong with my testing since the sample provided doesn't show the amount of buffered data.
It could be that when you tested it that the whole video was buffered since you raised the speed to 500kbps and since in my original test video i stated that if you let the whole video to buffer then the weird behavior is gone.

we need the native sample to have a slider that shows the amount of buffered data to be able to do any meaningful tests

@moham96
Copy link
Author

moham96 commented May 8, 2024

@huycozy do you have native examples that shows the buffered amount so i can test
I can test MacOS, Android and ios

@huycozy
Copy link
Member

huycozy commented May 8, 2024

@moham96 I have Android native sample for ExoPlayer here that you can try on: https://github.com/huycozy/AndroidNativeExoPlayer.

I checked this on Android emulator that allows me to set network speed; I went with umts - UMTS/3G (up: 384.0, down: 384.0) and edge - EDGE/EGPRS (up: 473.6, down: 473.6) (see network options here) but the video can't be loaded due to slow network even though I wait a long time for it. The same result occurs with video_player package when running on this Android emulator as well.

@moham96
Copy link
Author

moham96 commented May 8, 2024

@moham96 I have Android native sample for ExoPlayer here that you can try on: https://github.com/huycozy/AndroidNativeExoPlayer.

I checked this on Android emulator that allows me to set network speed; I went with umts - UMTS/3G (up: 384.0, down: 384.0) and edge - EDGE/EGPRS (up: 473.6, down: 473.6) (see network options here) but the video can't be loaded due to slow network even though I wait a long time for it. The same result occurs with video_player package when running on this Android emulator as well.

So i was able to test the provided demo with the following modifications:

the testing revealed a less buggy player than the flutter version but has partially the same issue:

The player doesn't play the buffered part, but unlike the flutter version it doesn't have to buffer the whole video to play, it was able to play the video when the video was buffered around 70-80% i don't know if this is different behavior or maybe it's the same for flutter but the updating of the buffered amount is incorrect in the flutter implementation.

testing using the bandwidth limiter of the emulator i was not able to play the video, which reveals one truth, the player is practically unusable on low bandwidth

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
found in release: 3.19 Found to occur in 3.19 found in release: 3.22 Found to occur in 3.22 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: video_player The Video Player plugin P2 Important issues not at the top of the work list package flutter/packages repository. See also p: labels. team-ecosystem Owned by Ecosystem team triaged-ecosystem Triaged by Ecosystem team
Projects
None yet
Development

No branches or pull requests

3 participants