chewie icon indicating copy to clipboard operation
chewie copied to clipboard

Video src cannot be switched in full-screen mode

Open fe-spark opened this issue 1 year ago • 14 comments

Future<void> _initController() async {
    var list = widget.list;
    var originIndex = widget.originIndex;
    var teleplayIndex = widget.teleplayIndex;

    final originPlayList = list?[originIndex]?.linkList;
    final url = originPlayList?[teleplayIndex].link ?? '';

    setState(() {
      _loading = true;
      _error = false;
    });
    _videoPlayerController = VideoPlayerController.networkUrl(
      Uri.parse(url),
    )
      ..addListener(_listener)
      ..initialize().then(
        (value) {
          double? videoAspectRatio = _videoPlayerController?.value.aspectRatio;
          setState(() {
            _loading = false;
            _error = false;
          });
          _chewieController = ChewieController(
            videoPlayerController: _videoPlayerController!,
            allowFullScreen: true,
            autoPlay: true,
            looping: false,
            autoInitialize: true,
            startAt: Duration(seconds: widget.startAt ?? 0),
            showControlsOnInitialize: true,
            aspectRatio: videoAspectRatio ?? _aspectRatio,
            additionalOptions: (context) {
              return <OptionItem>[
                OptionItem(
                  onTap: () {
                    var originIndex = widget.originIndex;
                    var teleplayIndex = widget.teleplayIndex;
                    if (teleplayIndex < originPlayList!.length - 1) {
                      widget.callback(originIndex, teleplayIndex + 1);
                    }
                  },
                  title: 'Next video',
                ),
              ];
            },
           };
          setState(() { });
        },
      ).catchError((error) {
        // print(error);
        setState(() {
          _loading = false;
          _error = true;
          _errorMessage = error.message;
        });
      });
  }

  @override
  void didUpdateWidget(covariant Player oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.teleplayIndex != oldWidget.teleplayIndex) {
      _videoPlayerController?.dispose();
      _initController();
    }
  }

Result

Abnormal black screen is stuck

Error info

Another exception was thrown: A PlayerNotifier was used after being disposed.
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: A PlayerNotifier was used after being disposed.
Once you have called dispose() on a PlayerNotifier, it can no longer be used.
#0      ChangeNotifier.debugAssertNotDisposed.<anonymous closure> (package:flutter/src/foundation/change_notifier.dart:179:9)
#1      ChangeNotifier.debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:186:6)
#2      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:412:27)
#3      PlayerNotifier.hideStuff= (package:chewie/src/notifiers/player_notifier.dart:19:5)
#4      _CupertinoControlsState._startHideTimer.<anonymous closure>.<anonymous closure> (package:chewie/src/cupertino/cupertino_controls.dart:787:18)
#5      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
#6      _CupertinoControlsState._startHideTimer.<anonymous closure> (package:chewie/src/cupertino/cupertino_controls.dart:786:7)
#7      Timer._createTimer.<anonymous c<…>

fe-spark avatar Aug 16 '24 08:08 fe-spark

I got the same problem . How to resolve it

Chensp171254 avatar Aug 30 '24 09:08 Chensp171254

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

Chensp171254 avatar Aug 30 '24 10:08 Chensp171254

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

Did you resolve the issue after the move? I replaced it with the better_player module.

fe-spark avatar Aug 30 '24 12:08 fe-spark

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

Did you resolve the issue after the move? I replaced it with the better_player module. I tried various ways to solve it, but it didn't work I have decided to handle the interactive interface myself

Chensp171254 avatar Aug 31 '24 02:08 Chensp171254

same problem

zoozobib avatar Aug 31 '24 08:08 zoozobib

i had some test by offical example (ChewieDemo) , in the official example, ChewieDemo is loaded as the entree node: void main() { runApp( const ChewieDemo(), ); }

  1. If the "home" property of MaterialAPP is set to ChewieDemo() in a new page , when playing a full-screen video, the video switch fails and the screen stays still ( This is a more general scene, because ChewieDemo cannot always be used as the entry widget )

  2. but , use "builder" property with MaterialAPP , playing a full-screen video and switch another one is normal

MaterialApp( // home: Scaffold( // body: Stack( // children: [ // ChewieDemo(), // ], // ), // ), builder: (_,child){ return ChewieDemo(); }, )

So far, I don't know what the problem is and need to debug it further

zoozobib avatar Sep 01 '24 04:09 zoozobib

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

Did you resolve the issue after the move? I replaced it with the better_player module. I tried various ways to solve it, but it didn't work I have decided to handle the interactive interface myself

当进入全屏后,假如外部包裹【Chiewe】的父组件【Dispose】,问题就会出现。我接到的产品需求是在【List】列表页中的每个【item】播放视频,在这种情况下全屏模式会随着【item】的复用出现问题。所以我禁用了全屏模式,添加了按钮跳转到一个独立的大屏播放界面过渡。在单一的新界面使用全屏模式是稳定的。

Chensp171254 avatar Sep 02 '24 06:09 Chensp171254

finally , i rebuild this module to resolve it that full screen change source problem . it means that i must be implement to fullscreen widget and state management and controller bar by myself.

zoozobib avatar Sep 02 '24 15:09 zoozobib

I got the same problem

duongtricn2c avatar Sep 05 '24 07:09 duongtricn2c

finally , i rebuild this module to resolve it that full screen change source problem . it means that i must be implement to fullscreen widget and state management and controller bar by myself.

how to you solve it please ?

AhmedAlaaGenina avatar Oct 01 '24 12:10 AhmedAlaaGenina

finally , i rebuild this module to resolve it that full screen change source problem . it means that i must be implement to fullscreen widget and state management and controller bar by myself.

how to you solve it please ?

This is brief implementation that about solve it :

  1. create 2 view page from State Widget and refer to Hero widget on the page
  2. on the Ontap event of the fullscreen icon to make sure keep playing video source in the second view page landscapeLeft Mode , that is why include the identical Hero Widget Tag
  3. it must be implement to SeekBar code related to scene by your self even the PlayPause and PlaySpeed and Duration or Other state
  4. i has using Stream Widget by sync time tick and player positions
  5. at last that animate the status bar to invisible mode

zoozobib avatar Oct 06 '24 12:10 zoozobib

same issue

junixapp avatar Nov 08 '24 03:11 junixapp

I guess that since full screen is entering a new page, the changes in page1 parameters cannot be responded to page2 in time (I tried to pass count from page1 to page2 and modify it, and it turned out that only page1 responded to the change).

Therefore, just encapsulate the parameters through ChangeNotifier and use them.

Here is a simple demo

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

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  MyChewieControllerNotifier myChewieControllerNotifier =
      MyChewieControllerNotifier();

  toggleLink() {
    myChewieControllerNotifier.updateChewieController(
      onTap: _handleTap,
      myChangeNotifier: myChewieControllerNotifier,
    );
  }

  void _handleTap() {
    toggleLink();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        myChewieControllerNotifier.chewieController == null
            ? const Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  CircularProgressIndicator(),
                  SizedBox(height: 20),
                  Text('Loading'),
                ],
              )
            : Expanded(
                child: Chewie(
                    controller: myChewieControllerNotifier.chewieController!)),
        TextButton(onPressed: _handleTap, child: Text('切换链接')),
      ],
    );
  }
}

class TestFullScreen extends StatefulWidget {
  final MyChewieControllerNotifier myChangeNotifier;
  final void Function() onTest1;
  const TestFullScreen({
    super.key,
    required this.onTest1,
    required this.myChangeNotifier,
  });

  @override
  State<TestFullScreen> createState() => _TestFullScreenState();
}

class _TestFullScreenState extends State<TestFullScreen> {
  @override
  void initState() {
    widget.myChangeNotifier.addListener(_update);
    super.initState();
  }

  _update() {
    setState(() {});
  }

  @override
  void setState(VoidCallback fn) {
    if (!mounted) return;
    super.setState(fn);
  }

  @override
  void dispose() {
    // TODO: implement dispose
    widget.myChangeNotifier.removeListener(_update);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      // appBar: AppBar(title: Text("测试全屏页面")),
      floatingActionButton: FloatingActionButton(onPressed: widget.onTest1),
      body: Container(
        alignment: Alignment.center,
        color: Colors.red,
        child: Chewie(controller: widget.myChangeNotifier.chewieController!),
      ),
    );
  }
}

class MyChewieControllerNotifier extends ChangeNotifier {
  ChewieController? chewieController;

  int currPlayIndex = 0;

  List<String> srcs = [
    "https://assets.mixkit.co/videos/preview/mixkit-spinning-around-the-earth-29351-large.mp4",
    "https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4",
    "https://assets.mixkit.co/videos/preview/mixkit-a-girl-blowing-a-bubble-gum-at-an-amusement-park-1226-large.mp4"
  ];

  updateChewieController({
    required void Function() onTap,
    required MyChewieControllerNotifier myChangeNotifier,
  }) {
    currPlayIndex += 1;
    if (currPlayIndex >= srcs.length) {
      currPlayIndex = 0;
    }
    String link = srcs[currPlayIndex];
    print("切换了链接  $link");
    chewieController?.videoPlayerController.dispose();
    chewieController?.dispose();
    chewieController = ChewieController(
      videoPlayerController: VideoPlayerController.networkUrl(
        Uri.parse(srcs[currPlayIndex]),
      ),
      autoPlay: true,
      autoInitialize: true,
      aspectRatio: 16 / 9,
      routePageBuilder:
          (context, animation, secondaryAnimation, controllerProvider) =>
              AnimatedBuilder(
        animation: animation,
        builder: (BuildContext context, Widget? child) {
          return TestFullScreen(
            onTest1: onTap,
            myChangeNotifier: myChangeNotifier,
          );
        },
      ),
    );
    notifyListeners();
  }
}

azhezzzz avatar Nov 08 '24 03:11 azhezzzz

Based on your answer @azhezzzz , an alternative with ChangeNotifier.


List<String> srcs = [
  "https://samplelib.com/lib/preview/mp4/sample-5s.mp4",
  "https://samplelib.com/lib/preview/mp4/sample-10s.mp4",
  "https://samplelib.com/lib/preview/mp4/sample-5s.mp4",
];

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

  @override
  State<NotFullScreen> createState() => _NotFullScreenState();
}

class _NotFullScreenState extends State<NotFullScreen> {
  final VideoNotifier _videoNotifier = VideoNotifier();
  int indexVideoPlayed = 0;
  bool videoIsPlaying = false;

  @override
  void initState() {
    super.initState();
    loadVideo(indexVideoPlayed);
  }

  void loadVideo(indexVideoPlayed) async {
    final String videoUrl = srcs[indexVideoPlayed];
    final videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(videoUrl));
    await videoPlayerController.initialize();
    setState(() {});
    _videoNotifier.initChewieController(videoPlayerController);

    videoPlayerController.addListener(() {
      if (videoPlayerController.value.isCompleted && videoIsPlaying) {
        // videoIsPlaying prevent listener to call multiple time at end.
        videoIsPlaying = false;
        indexVideoPlayed++;
        loadVideo(indexVideoPlayed);
      }
      videoIsPlaying = true;
    });
  }

  @override
  void dispose() {
    _videoNotifier.chewieController?.dispose();
    _videoNotifier.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _videoNotifier.chewieController != null
          ? SizedBox(
              height: 400,
              child: ChewieVideo(
                notifier: _videoNotifier,
              ),
            )
          : const Center(
              child: CircularProgressIndicator(),
            ),
    );
  }
}

class ChewieVideo extends StatelessWidget {
  final VideoNotifier _videoNotifier;

  const ChewieVideo({super.key, notifier}) : _videoNotifier = notifier;

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: _videoNotifier,
      builder: (context, child) => Chewie(
        controller: _videoNotifier.chewieController!,
      ),
    );
  }
}

class VideoNotifier extends ChangeNotifier {
  ChewieController? _chewieController;

  ChewieController? get chewieController => _chewieController;

  void initChewieController(videoPlayerController) {
    _chewieController = ChewieController(
      autoPlay: true,
      videoPlayerController: videoPlayerController,
      routePageBuilder: (context, animation, secondaryAnimation, controllerProvider) {
        return ChewieVideo(notifier: this);
      },
    );
    notifyListeners();
  }
}

loicbrigardis avatar Dec 02 '24 10:12 loicbrigardis