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

[BUG] Garbage collector problem #35

Open
Andrekarma opened this issue Jun 4, 2024 · 13 comments
Open

[BUG] Garbage collector problem #35

Andrekarma opened this issue Jun 4, 2024 · 13 comments

Comments

@Andrekarma
Copy link

if i initialize a video player and dont use it (i want to prepare it for later), this message occur:

[video_player_win] gc free a player that didn't dispose() yet !!!!!

And the player is no more initialized

@jakky1
Copy link
Owner

jakky1 commented Jun 4, 2024

I think the controller instance you created is not pointed by any variable anymore,
so dart garbage collection try to clean it.

Could you try to assign the controller instance to a static / global variable (though it is not a good idea),
ensure you just create ONE controller instance,
and check the message still occurs?

@Andrekarma
Copy link
Author

Andrekarma commented Jun 4, 2024

I am creating 2 instances of a class with the video controller.
i load 2 different videos at the time, but only one is showed. The second one is ready for the first one to be finished, then i switch them.
Sometimes (randomly) the "[video_player_win] gc free a player that didn't dispose() yet !!!! appears" and the instance that is not playing is gone.

`class PHFPlayerVideoPlayer extends PHFPlayer{
PHFPlayerVideoPlayer(index){
this.index = index;
}

VideoPlayerController? videoPlayerController;

@OverRide
closeVideoPlayer() async{
if(videoPlayerController != null) {
await videoPlayerController!.pause();
await videoPlayerController!.dispose();
videoPlayerController = null;
}
}

@OverRide
initializeVideoSlide(path) async{
videoPlayerController = VideoPlayerController.file(File(path));
videoPlayerController!.initialize();
videoPlayerController!.setVolume(PHFConfig.instance.moviesVolume * 100 * (PHFConfig.instance.disableMovieAudio == 0 ? 1 : 0));
}
`

@Andrekarma
Copy link
Author

I hope it is related to the fact that after some iterations of this switch beetween different videos, the app crashes with no specific error log

@jakky1
Copy link
Owner

jakky1 commented Jun 4, 2024

I write the following simple test code,
always preload the next video every time when clicking "Next" button.
I try to click the "Next" button every second about 50 times,
and it works well with no gc occurs.

Did this solve your problem?

Note: Please remember to edit the gFileList array in the following code if you try to run it.

import 'dart:io';

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

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

const gFileList = [
  "E:\\test_4k.mp4",
  "E:\\test_align.mp4",
  "E:\\test_av1.mp4",
  "E:\\test_clock.mp4",
  "E:\\test_youtube.mp4",
];

class PHFPlayerVideoPlayer {
  WinVideoPlayerController? controller;

  Future<void> initializeVideoSlide(String path) async {
    controller?.dispose();
    controller = WinVideoPlayerController.file(File(path));
    await controller!.initialize();
  }

  void play() {
    controller!.play();
  }

  void closeVideoPlayer() {
    controller?.dispose();
    controller = null;
  }
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  var nowPlayer = PHFPlayerVideoPlayer();
  var nextPlayer = PHFPlayerVideoPlayer();
  int nowIndex = 0;

  @override
  void initState() {
    super.initState();
    nowIndex = 0;
    nowPlayer.initializeVideoSlide(gFileList[nowIndex]).then((value) {
      nowPlayer.play();
      setState(() {}); // update UI
    });
    prepareNext();
  }

  void prepareNext() {
    int nextIndex = (nowIndex + 1) % gFileList.length; // loop the list
    nextPlayer.initializeVideoSlide(gFileList[nextIndex]);
  }

  void onNextButtonClicked() {
    nowPlayer.closeVideoPlayer();

    // swap 2 player pointer
    var tmp = nextPlayer;
    nextPlayer = nowPlayer;
    nowPlayer = tmp;
    nowIndex = (nowIndex + 1) % gFileList.length; // loop the list

    nowPlayer.play();
    prepareNext();
    setState(() {}); // update UI
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      body: Stack(children: [
        WinVideoPlayer(nowPlayer.controller!),
        ElevatedButton(
            onPressed: onNextButtonClicked, child: const Text("Next")),
      ]),
    ));
  }
}

@Andrekarma
Copy link
Author

It is still happening, and i don't understand why
The logic is the same of your example, but the Garbage collector is doing something that should not be doing.
I still think is something related to this library, because its not happening with other libraries

@jakky1
Copy link
Owner

jakky1 commented Jun 5, 2024

First,
did you try to run my sample code above, WITHOUT any modification, and ensure that the GC still occurs ?

Second,
you said it is not happen with other libraries,
Could you give me a SIMPLE sample code ( less than 200 lines ),
and tell me which library and which platform that no GC occurs.
I need more information to clarify issue.

@jakky1 jakky1 closed this as completed Jun 14, 2024
@Andrekarma
Copy link
Author

Andrekarma commented Jun 17, 2024

@jakky1 Here you have it

image

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

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

const gFileList = [
"C:\Users\andreacarmagnola\Desktop\VIDEOTEST\4kvert.mp4",
"C:\Users\andreacarmagnola\Desktop\VIDEOTEST\bene.mp4",
"C:\Users\andreacarmagnola\Desktop\VIDEOTEST\male.mp4",
];

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

@OverRide
State createState() => _MyAppState();
}

class _MyAppState extends State {
PHFPlayer player1 = PHFPlayer.createVideoPlayer(PHFPlayer.VIDEO_PLAYER_LIBRARY, 1);
PHFPlayer player2 = PHFPlayer.createVideoPlayer(PHFPlayer.VIDEO_PLAYER_LIBRARY, 2);
late PHFPlayer currentPlayer;
bool showPlayer =false;
int nowIndex = 0;

@OverRide
initState(){
super.initState();
nowIndex = 0;
Future.delayed(Duration(seconds: 1), () async {
await player1.initializeVideoSlide(gFileList[nowIndex]);
int nextIndex = (nowIndex + 1) % gFileList.length; // loop the list
await player2.initializeVideoSlide(gFileList[nextIndex]);
currentPlayer = player1;

  setState(() {
    showPlayer = true;
  });
});

}

@OverRide
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(children: [
showPlayer?currentPlayer.getVideoWidget(false):Container()

    ]),
  ),
);

}
}

abstract class PHFPlayer {
static const int VIDEO_PLAYER_LIBRARY = 2;

late int libraryType;
late int index;
late Image image;

Future initializeVideoSlide(videoFile);

Widget getVideoWidget(useBlur);

static PHFPlayer createVideoPlayer(int libraryType, index) {
return PHFPlayerVideoPlayer(index);

}

}

class PHFPlayerVideoPlayer extends PHFPlayer{
PHFPlayerVideoPlayer(index){
this.index = index;
libraryType = PHFPlayer.VIDEO_PLAYER_LIBRARY;
}

VideoPlayerController? videoPlayerController;

@OverRide
initializeVideoSlide(videoFile) async{
videoPlayerController = VideoPlayerController.file(File(videoFile));
await videoPlayerController!.initialize();
}

@OverRide
Widget getVideoWidget(useBlur){
videoPlayerController!.play();
return VideoPlayer(videoPlayerController!);
}

}

@jakky1 jakky1 reopened this Jun 17, 2024
@jakky1
Copy link
Owner

jakky1 commented Jun 17, 2024

Thanks for reporting this issue, and the sample code.

It seems there is really something wrong in this package.
In my sample code last week, I use WinVideoPlayerController, and it really work well.
In your sample code, you use official VideoPlayerController, and gc occurs.

It is confirmed that if any VideoPlayerController created but not displayed,
then the WinVideoPlayerController which is created by this VideoPlayerController will be GC collection.
I have never test this case with official VideoPlayerController before... orz

Now it is fixed,
and I only push to github now.
Please try it and let me know if the solution works for you.

dependencies:
  video_player_win:
    git:
      url: https://github.com/jakky1/video_player_win.git
      ref: master

@Andrekarma
Copy link
Author

The GC issue seems now resolved

But not there is another problem.
Randomly after some time playing, i see this error and the app crashes

[ERROR:flutter/shell/platform/windows/external_texture_d3d.cc(107)] Binding D3D surface failed.

@jakky1
Copy link
Owner

jakky1 commented Jun 18, 2024

Because it rarely occurs,
again I need a simple sample code (< 300 lines) and a more easy way to duplicate this issue.

I have no way to fix this before I can duplicate this issue on my PC.

@Andrekarma
Copy link
Author

import 'dart:async';
import 'dart:io';

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

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

const gFileList = [
"C:\Users\andreacarmagnola\Desktop\VIDEOTEST\4kvert.mp4",
"C:\Users\andreacarmagnola\Desktop\VIDEOTEST\bene.mp4"
];

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

@OverRide
State createState() => _MyAppState();
}

class _MyAppState extends State {
PHFPlayerVideoPlayer player1 = PHFPlayerVideoPlayer();
PHFPlayerVideoPlayer player2 = PHFPlayerVideoPlayer();
late PHFPlayerVideoPlayer currentPlayer;
bool showPlayer =false;
int nowIndex = 0;

@OverRide
initState(){
super.initState();
nowIndex = 0;
Future.delayed(Duration(seconds: 1), () async {
await player1.initializeVideoSlide(gFileList[0]);
player1.index =1 ;
int nextIndex = (nowIndex + 1) % gFileList.length; // loop the list
await player2.initializeVideoSlide(gFileList[1]);
player2.index = 2;
currentPlayer = player1;

  Timer.periodic(Duration(seconds: 5), (Timer) async {
    await switchPlayer();
  });
  setState(() {
    showPlayer = true;
  });
});

}

switchPlayer() async{
if (currentPlayer.index == 1) {
currentPlayer = player2;
await player1.videoPlayerController!.dispose();
await player1.initializeVideoSlide(gFileList[0]);
} else {
currentPlayer = player1;
await player2.videoPlayerController!.dispose();
await player2.initializeVideoSlide(gFileList[1]);
}

setState(() {});

}

@OverRide
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(children: [
showPlayer?currentPlayer.getVideoWidget(false):Container()

    ]),
  ),
);

}
}

class PHFPlayerVideoPlayer {

int index = 0;
WinVideoPlayerController? videoPlayerController;

initializeVideoSlide(videoFile) async{
videoPlayerController = WinVideoPlayerController.file(File(videoFile));
await videoPlayerController!.initialize();
}

Widget getVideoWidget(useBlur){
videoPlayerController!.play();
return WinVideoPlayer(videoPlayerController!);
}

}

@Andrekarma
Copy link
Author

@jakky1 do you have any update on this?

@jakky1
Copy link
Owner

jakky1 commented Jul 3, 2024

I have run your code in debug mode (5 hours) and release mode (8 hours)
without any error occurs...
I have no idea how to produce & fix the error Binding D3D surface failed you mentioned...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants