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_tizen] Videos are playing with software codec #757

Open
huluwa-dev opened this issue Oct 24, 2024 · 37 comments
Open

[video_player_tizen] Videos are playing with software codec #757

huluwa-dev opened this issue Oct 24, 2024 · 37 comments

Comments

@huluwa-dev
Copy link

huluwa-dev commented Oct 24, 2024

I tried to play an HLS video with video_player_tizen, but when the video quality automatically updated to 1080P, the playback started stuttering significantly. I assume the video was played using a software codec, as other video-playing apps work well (like YouTube), even with 4K videos.

Could you help me confirm whether this plugin uses a software decoder for video playback? Thanks.

This is the code snippet:


class VideoPlayerWidget extends ConsumerStatefulWidget {
  const VideoPlayerWidget({
    super.key,
  });

  @override
  ConsumerState createState() => _VideoPlayerWidgetState();
}

class _VideoPlayerWidgetState extends ConsumerState<VideoPlayerWidget> {
  late VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.networkUrl(
        Uri.parse('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'))
      ..initialize().then((_) {
        // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
        setState(() {});
      });
  }

  bool get _videoReady => _controller.value.isInitialized == true;

  @override
  Widget build(BuildContext context) {
    return _buildVideoPlayer();
  }

  Widget _buildVideoPlayer() {
    return Container(
      width: double.infinity,
      height: double.infinity,
      color: context.theme.black,
      child: _videoReady
          ? VideoPlayer(_controller)
          : Center(
              child: CircularProgressIndicator(
                color: Colors.white,
              ),
            ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

@JSUYA
Copy link
Member

JSUYA commented Oct 24, 2024

Hi, thank you for your interest.

video_player_tizen is implemented using player.h provided by TizenSDK.
(https://docs.tizen.org/application/native/api/iot-headed/6.0/group__CAPI__MEDIA__PLAYER__MODULE.html)
Basic Media spcification can be found at the link below.
https://developer.samsung.com/smarttv/develop/specifications/media-specifications.html
https://developer.samsung.com/smarttv/develop/specifications/tv-model-groups.html

(This may vary depending on the device's model, specifications, and Tizen version.)
Tizen 8.0 provides an API that can find out the codec type (HW, SW, DEFAULT) of the video in the Player module.
When I checked on my test device, the DEFAULT value was returned, and this can be SW or HW depending on the system priority.
And I checked in the logs of my test tv device that the SW codec is being used. (Tizen 8.0)
Maybe, if nothing else is set, it will work as a SW decoder.

If you have a Tizen 8.0 development environment set up, you can test the test code below.
JSUYA@efe4140

@huluwa-dev
Copy link
Author

Hi @JSUYA , thanks for your quick reply.

Did you mean that you have ran the code I provided and confirmed that the Tizen OS decoded the video(https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8) with a SW codec?

If that is the case, could you help me to get an video example(url) which can be played with a HW codec? Thanks.

@xiaowei-guan
Copy link
Contributor

Hi @JSUYA , thanks for your quick reply.

Did you mean that you have ran the code I provided and confirmed that the Tizen OS decoded the video(https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8) with a SW codec?

If that is the case, could you help me to get an video example(url) which can be played with a HW codec? Thanks.

@huluwa-dev Hello, do you play video on TV device?

@JSUYA
Copy link
Member

JSUYA commented Oct 24, 2024

Hi @JSUYA , thanks for your quick reply.

Did you mean that you have ran the code I provided and confirmed that the Tizen OS decoded the video(https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8) with a SW codec?

yes I played https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8
Your code looks like it uses flutter riverpod. (because of ConsumerStatefulWidget). this will not affect the decoder.
I tested it in the example of video_player and only changed the url. (JSUYA@efe4140)

If that is the case, could you help me to get an video example(url) which can be played with a HW codec? Thanks.

Unfortunately I don't know much about content that forces the use of HW codec.

@huluwa-dev
Copy link
Author

Hi @xiaowei-guan Yes, I played the video on a Samsung TV with Tizen 6.0. Tomorrow I will post the TV's model information.

@huluwa-dev
Copy link
Author

Hi @JSUYA , thanks for your quick reply.
Did you mean that you have ran the code I provided and confirmed that the Tizen OS decoded the video(https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8) with a SW codec?

yes I played https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8 Your code looks like it uses flutter riverpod. (because of ConsumerStatefulWidget). this will not affect the decoder. I tested it in the example of video_player and only changed the url. (JSUYA@efe4140)

If that is the case, could you help me to get an video example(url) which can be played with a HW codec? Thanks.

Unfortunately I don't know much about content that forces the use of HW codec.

Thanks for the confirmation.

As you are one of the contributors, do you know anybody who can help to make this plugin work with HW codec. SW codec is inefficient and produces poor user experience. I believe it could improve the performance a lot by utilizing a HW codec.

Thank you for your time.

@huluwa-dev
Copy link
Author

huluwa-dev commented Oct 24, 2024

I downloaded the video with yt-dlp

yt-dlp https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8

and checked the video's information with FFmpeg.

ffmpeg -i x36xhzz\ \[x36xhzz\].mp4
ffmpeg version 7.0.2 Copyright (c) 2000-2024 the FFmpeg developers
  built with Apple clang version 16.0.0 (clang-1600.0.26.3)
  configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/7.0.2_1 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags='-Wl,-ld_classic' --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libharfbuzz --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoolbox --enable-neon
  libavutil      59.  8.100 / 59.  8.100
  libavcodec     61.  3.100 / 61.  3.100
  libavformat    61.  1.100 / 61.  1.100
  libavdevice    61.  1.100 / 61.  1.100
  libavfilter    10.  1.100 / 10.  1.100
  libswscale      8.  1.100 /  8.  1.100
  libswresample   5.  1.100 /  5.  1.100
  libpostproc    58.  1.100 / 58.  1.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'x36xhzz [x36xhzz].mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf61.1.100
  Duration: 00:10:34.59, start: 0.000000, bitrate: 6154 kb/s
  Stream #0:0[0x1](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 151 kb/s (default)
      Metadata:
        handler_name    : SoundHandler
        vendor_id       : [0][0][0][0]
  Stream #0:1[0x2](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 5985 kb/s, 60 fps, 60 tbr, 90k tbn (default)
      Metadata:
        handler_name    : VideoHandler
        vendor_id       : [0][0][0][0]

It is an H.264 video, it should be played with HW codec on most devices. But unfortunately, video_player_tizen plays it with SW codec.

It would be great if the tizen team could solve this problem.

@xiaowei-guan
Copy link
Contributor

Hi @JSUYA , thanks for your quick reply.
Did you mean that you have ran the code I provided and confirmed that the Tizen OS decoded the video(https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8) with a SW codec?

yes I played https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8 Your code looks like it uses flutter riverpod. (because of ConsumerStatefulWidget). this will not affect the decoder. I tested it in the example of video_player and only changed the url. (JSUYA@efe4140)

If that is the case, could you help me to get an video example(url) which can be played with a HW codec? Thanks.

Unfortunately I don't know much about content that forces the use of HW codec.

Thanks for the confirmation.

As you are one of the contributors, do you know anybody who can help to make this plugin work with HW codec. SW codec is inefficient and produces poor user experience. I believe it could improve the performance a lot by utilizing a HW codec.

Thank you for your time.

If you want to have a better performance, we suggest you use video_player_videohole.
If you are a partner of Samsung, we suggest you use video_player_avplay.

@xiaowei-guan
Copy link
Contributor

@huluwa-dev The video_player_videohole and video_player_avplay only support TV device.

@huluwa-dev
Copy link
Author

@huluwa-dev The video_player_videohole and video_player_avplay only support TV device.

Hi, thanks for the suggestion, I tried video_player_videohole, but some error occured, I think that's because the video I tried is not with DRM, I will post the detail of the errors tomorrow.

@EricLinPixelforce
Copy link

Hi @xiaowei-guan is video_player_videohole suitable for playing a content without DRM?

@xiaowei-guan
Copy link
Contributor

@huluwa-dev The video_player_videohole and video_player_avplay only support TV device.

Hi, thanks for the suggestion, I tried video_player_videohole, but some error occured, I think that's because the video I tried is not with DRM, I will post the detail of the errors tomorrow.

I can play the URL with video_player_videohole and video_player. video_player_videohole plugin has a goode performance, because video_player_videohole render the video on video plane layer, not on graphic layer.

if your app just for Tizen TV device, we suggest you use video_player_videohole plugin

@xiaowei-guan
Copy link
Contributor

Hi @xiaowei-guan is video_player_videohole suitable for playing a content without DRM?

@EricLinPixelforce Hi, if your app just run on Tizen TV device, we suggest you use video_player_videohole plugin, video_player_videohole also supports play DRM content, you need to be a partner of samsung.

@JSUYA
Copy link
Member

JSUYA commented Oct 25, 2024

As you are one of the contributors, do you know anybody who can help to make this plugin work with HW codec. SW codec is inefficient and produces poor user experience. I believe it could improve the performance a lot by utilizing a HW codec.

Thank you for your time.

@huluwa-dev
If you're just testing purposes, you can temporarily use the internal API(Does not guarantee operation) and I've created a commit to test it.
(There is absolutely no guarantee that this commit will be merged into master.)
JSUYA@c8f94db
With this patch, try using VideoPlayerTizen.forceUseHwDecoder = true; before calling Initialize of VideoController.

As I said before...Tizen 8.0 provides an API that can set the decoder. So if you are in an environment with Tizen 8.0, this operation maybe guaranteed, but in other environments (6.0, 6.5, 7.0), the operation may not be guaranteed. Also, when using the HW decoder, there was a abnormal behavior(?) in which certain contents on my device were initially displayed in a small screen size and then restored.

As far as I know, TV devices basically use HW decoder if HW decoder is available.
The basic settings are not fixed to HW or SW.
So, it is due to internal logic that your device uses SW decoder for playback.
(There may be various reasons, such as device performance. If there is a difference with other apps, there may be a difference in the video player.)
Therefore, if you want better performance or environment, you can use video_player_avplay or video_player_videohole.

@huluwa-dev
Copy link
Author

Hi @xiaowei-guan is video_player_videohole suitable for playing a content without DRM?

@EricLinPixelforce Hi, if your app just run on Tizen TV device, we suggest you use video_player_videohole plugin, video_player_videohole also supports play DRM content, you need to be a partner of samsung.

Hi, thanks for the great news.
Can video_player_videohole also play non-DRM contents?

I already regisered a Samsung seller account and will distribute a TV app to the Tizen store within a month.

@EricLinPixelforce
Copy link

EricLinPixelforce commented Oct 29, 2024

@huluwa-dev The video_player_videohole and video_player_avplay only support TV device.

Hi, thanks for the suggestion, I tried video_player_videohole, but some error occured, I think that's because the video I tried is not with DRM, I will post the detail of the errors tomorrow.

I can play the URL with video_player_videohole and video_player. video_player_videohole plugin has a goode performance, because video_player_videohole render the video on video plane layer, not on graphic layer.

if your app just for Tizen TV device, we suggest you use video_player_videohole plugin

Hi, I tried to play the URL with video_player_videohole, but the player keeps producing an error

 [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Pause, Player pause failed, null, null)
#0      VideoPlayerVideoholeApi.pause (package:video_player_videohole/src/messages.g.dart:790:7)
<asynchronous suspension>
#1      VideoPlayerController._applyPlayPause (package:video_player_videohole/video_player.dart:610:7)
<asynchronous suspension>

My code:


class VideoholdPlayerWidget extends StatefulWidget {

  const VideoholdPlayerWidget({
    super.key,
  });

  @override
  State<VideoholdPlayerWidget> createState() => _VideoholdPlayerWidgetState();
}

class _VideoholdPlayerWidgetState extends State<VideoholdPlayerWidget> {
  late VideoPlayerController _controller;

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

  void _initPlayer() {
    _controller = VideoPlayerController.network(
      // widget.videoUrl,
      "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
    );
    _controller.addListener(() => setState(() {}));
    _controller.initialize().then((_) => setState(() {}));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AspectRatio(
        aspectRatio: _controller.value.aspectRatio,
        child: Stack(
          alignment: Alignment.bottomCenter,
          children: <Widget>[
            VideoPlayer(_controller),
            ClosedCaption(text: _controller.value.caption.text),
            GestureDetector(
              onTap: () {
                _controller.value.isPlaying
                    ? _controller.pause()
                    : _controller.play();
              },
            ),
            VideoProgressIndicator(_controller, allowScrubbing: true),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

I would appreciate it if you could give me any suggestions. Thanks.

@EricLinPixelforce
Copy link

@xiaowei-guan I also tried to play it with video_player_avplayer. I got this error:

[E] 
(process:1400): GLib-CRITICAL **: 15:22:16.399: g_path_get_basename: assertion 'file_name != NULL' failed
[E] 
(<unknown>:1400): GStreamer-WARNING **: 15:22:16.621: Failed to load plugin '/opt/usr/apps/com.traininpink.tv/lib/libgsthls.so': libclearkey.so.0: cannot open shared object file: Operation not permitted
[I] size of instance = 1240
size of class = 1428
[I] size of basesrc = 448
size of basesrcclass = 404
[E] 
(<unknown>:1400): GStreamer-WARNING **: 15:22:17.423: Failed to load plugin '/usr/lib/gstreamer-1.0/libgsthls.so': libclearkey.so.0: cannot open shared object file: Operation not permitted

My code:

class AVPlayerWidget extends StatefulWidget {

  const AVPlayerWidget({
    super.key,
  });

  @override
  State<AVPlayerWidget> createState() => _AVPlayerWidgetState();
}

class _AVPlayerWidgetState extends State<AVPlayerWidget> {
  late VideoPlayerController _controller;

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

  void _initPlayer() {
    _controller = VideoPlayerController.network(
      "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
    );
    _controller.addListener(() => setState(() {}));
    _controller.initialize().then((_) => setState(() {}));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AspectRatio(
        aspectRatio: _controller.value.aspectRatio,
        child: Stack(
          alignment: Alignment.bottomCenter,
          children: <Widget>[
            VideoPlayer(_controller),
            ClosedCaption(text: _controller.value.caption.text),
            GestureDetector(
              onTap: () {
                _controller.value.isPlaying
                    ? _controller.pause()
                    : _controller.play();
              },
            ),
            VideoProgressIndicator(_controller, allowScrubbing: true),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

@EricLinPixelforce
Copy link

@JSUYA @xiaowei-guan The TV is a Samsung TV, model code: UA75BU8000WXXY (the model name should be 75" BU8000), with Tizen 6.5 running on it.

@EricLinPixelforce
Copy link

As you are one of the contributors, do you know anybody who can help to make this plugin work with HW codec. SW codec is inefficient and produces poor user experience. I believe it could improve the performance a lot by utilizing a HW codec.
Thank you for your time.

@huluwa-dev If you're just testing purposes, you can temporarily use the internal API(Does not guarantee operation) and I've created a commit to test it. (There is absolutely no guarantee that this commit will be merged into master.) JSUYA@c8f94db With this patch, try using VideoPlayerTizen.forceUseHwDecoder = true; before calling Initialize of VideoController.

As I said before...Tizen 8.0 provides an API that can set the decoder. So if you are in an environment with Tizen 8.0, this operation maybe guaranteed, but in other environments (6.0, 6.5, 7.0), the operation may not be guaranteed. Also, when using the HW decoder, there was a abnormal behavior(?) in which certain contents on my device were initially displayed in a small screen size and then restored.

As far as I know, TV devices basically use HW decoder if HW decoder is available. The basic settings are not fixed to HW or SW. So, it is due to internal logic that your device uses SW decoder for playback. (There may be various reasons, such as device performance. If there is a difference with other apps, there may be a difference in the video player.) Therefore, if you want better performance or environment, you can use video_player_avplay or video_player_videohole.

Thanks for the solution, I tried it on the TV, but still, it utilized HW codec to decode the video, because it was lagging.

@xiaowei-guan
Copy link
Contributor

@huluwa-dev The video_player_videohole and video_player_avplay only support TV device.

Hi, thanks for the suggestion, I tried video_player_videohole, but some error occured, I think that's because the video I tried is not with DRM, I will post the detail of the errors tomorrow.

I can play the URL with video_player_videohole and video_player. video_player_videohole plugin has a goode performance, because video_player_videohole render the video on video plane layer, not on graphic layer.
if your app just for Tizen TV device, we suggest you use video_player_videohole plugin

Hi, I tried to play the URL with video_player_videohole, but the player keeps producing an error

 [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Pause, Player pause failed, null, null)
#0      VideoPlayerVideoholeApi.pause (package:video_player_videohole/src/messages.g.dart:790:7)
<asynchronous suspension>
#1      VideoPlayerController._applyPlayPause (package:video_player_videohole/video_player.dart:610:7)
<asynchronous suspension>

My code:


class VideoholdPlayerWidget extends StatefulWidget {

  const VideoholdPlayerWidget({
    super.key,
  });

  @override
  State<VideoholdPlayerWidget> createState() => _VideoholdPlayerWidgetState();
}

class _VideoholdPlayerWidgetState extends State<VideoholdPlayerWidget> {
  late VideoPlayerController _controller;

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

  void _initPlayer() {
    _controller = VideoPlayerController.network(
      // widget.videoUrl,
      "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
    );
    _controller.addListener(() => setState(() {}));
    _controller.initialize().then((_) => setState(() {}));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AspectRatio(
        aspectRatio: _controller.value.aspectRatio,
        child: Stack(
          alignment: Alignment.bottomCenter,
          children: <Widget>[
            VideoPlayer(_controller),
            ClosedCaption(text: _controller.value.caption.text),
            GestureDetector(
              onTap: () {
                _controller.value.isPlaying
                    ? _controller.pause()
                    : _controller.play();
              },
            ),
            VideoProgressIndicator(_controller, allowScrubbing: true),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

I would appreciate it if you could give me any suggestions. Thanks.

Hello, @EricLinPixelforce I can play the URL(https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8) by videohole sample code.
code sample as follows:

class _HlsRomoteVideo extends StatefulWidget {
  @override
  State<_HlsRomoteVideo> createState() => _HlsRomoteVideoState();
}

class _HlsRomoteVideoState extends State<_HlsRomoteVideo> {
  late VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.network(
        'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8');

    _controller.addListener(() {
      if (_controller.value.hasError) {
        print(_controller.value.errorDescription);
      }
      setState(() {});
    });
    _controller.setLooping(true);
    _controller.initialize().then((_) => setState(() {}));
    _controller.play();
  }

plugin logs

I/VideoPlayerVideoHolePlugin(16752): media_player.cc: GetDuration(316) > [MediaPlayer] Video duration: 33002.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: Create(67) > [MediaPlayer] uri: https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8, drm_type: 0.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 0.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 1.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 2.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 3.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 4.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 5.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 6.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 7.
I/VideoPlayerVideoHolePlugin(16752): media_player.cc: OnBuffering(648) > [MediaPlayer] Buffering percent: 8.

@xiaowei-guan
Copy link
Contributor

@xiaowei-guan I also tried to play it with video_player_avplayer. I got this error:

[E] 
(process:1400): GLib-CRITICAL **: 15:22:16.399: g_path_get_basename: assertion 'file_name != NULL' failed
[E] 
(<unknown>:1400): GStreamer-WARNING **: 15:22:16.621: Failed to load plugin '/opt/usr/apps/com.traininpink.tv/lib/libgsthls.so': libclearkey.so.0: cannot open shared object file: Operation not permitted
[I] size of instance = 1240
size of class = 1428
[I] size of basesrc = 448
size of basesrcclass = 404
[E] 
(<unknown>:1400): GStreamer-WARNING **: 15:22:17.423: Failed to load plugin '/usr/lib/gstreamer-1.0/libgsthls.so': libclearkey.so.0: cannot open shared object file: Operation not permitted

My code:

class AVPlayerWidget extends StatefulWidget {

  const AVPlayerWidget({
    super.key,
  });

  @override
  State<AVPlayerWidget> createState() => _AVPlayerWidgetState();
}

class _AVPlayerWidgetState extends State<AVPlayerWidget> {
  late VideoPlayerController _controller;

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

  void _initPlayer() {
    _controller = VideoPlayerController.network(
      "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
    );
    _controller.addListener(() => setState(() {}));
    _controller.initialize().then((_) => setState(() {}));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AspectRatio(
        aspectRatio: _controller.value.aspectRatio,
        child: Stack(
          alignment: Alignment.bottomCenter,
          children: <Widget>[
            VideoPlayer(_controller),
            ClosedCaption(text: _controller.value.caption.text),
            GestureDetector(
              onTap: () {
                _controller.value.isPlaying
                    ? _controller.pause()
                    : _controller.play();
              },
            ),
            VideoProgressIndicator(_controller, allowScrubbing: true),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

If you want to use avplay plugin, you need to be a partner of samsung.

@EricLinPixelforce
Copy link

@xiaowei-guan Thanks for the reply. Now I can play it with your code sample in a created new project. But still got error in my existing project. That's weird. I will check the difference and find the reason.

@EricLinPixelforce
Copy link

@xiaowei-guan
I found the reason, if I call play after the video is initialized, the error will occur

 _controller.initialize().then((_) {
      setState(() {});
      _controller.play();
    });

But if I play it without waiting for the initilization, it works well.

 _controller.initialize().then((_) {
      setState(() {});
    });
    _controller.play();

@EricLinPixelforce
Copy link

Hi @xiaowei-guan Currently I am playing videos with video_player_videohole, I tried to detect if the video playing is finished with the following code

 _videoPlayerController.addListener(() {
      print(
          "_videoPlayerController.value.position = ${_videoPlayerController.value.position.inSeconds}, _videoPlayerController.value.duration.start = ${_videoPlayerController.value.duration.start.inSeconds}, _videoPlayerController.value.duration.end = ${_videoPlayerController.value.duration.end.inSeconds}");
      if (_videoPlayerController.value.position >=
              _videoPlayerController.value.duration.end &&
          _videoPlayerController.value.position.inMilliseconds != 0) {
        widget.onFinished?.call();
      }
    });

Finally I got this log and never detected it.

[I] flutter: _videoPlayerController.value.position = 23, _videoPlayerController.value.duration.start = 0, _videoPlayerController.value.duration.end = 26
[I] flutter: _videoPlayerController.value.position = 23, _videoPlayerController.value.duration.start = 0, _videoPlayerController.value.duration.end = 26
[I] flutter: _videoPlayerController.value.position = 23, _videoPlayerController.value.duration.start = 0, _videoPlayerController.value.duration.end = 26
[I] flutter: _videoPlayerController.value.position = 24, _videoPlayerController.value.duration.start = 0, _videoPlayerController.value.duration.end = 26
[I] flutter: _videoPlayerController.value.position = 24, _videoPlayerController.value.duration.start = 0, _videoPlayerController.value.duration.end = 26
[I] flutter: _videoPlayerController.value.position = 24, _videoPlayerController.value.duration.start = 0, _videoPlayerController.value.duration.end = 26
[I] flutter: _videoPlayerController.value.position = 24, _videoPlayerController.value.duration.start = 0, _videoPlayerController.value.duration.end = 26
[E] [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Pause, Player pause failed, null, null)
#0      VideoPlayerVideoholeApi.pause (package:video_player_videohole/src/messages.g.dart:790:7)
<asynchronous suspension>
#1      VideoPlayerController._applyPlayPause (package:video_player_videohole/video_player.dart:610:7)
<asynchronous suspension>
#2      VideoPlayerController.pause (package:video_player_videohole/video_player.dart:533:5)
<asynchronous suspension>
#3      VideoPlayerController.initialize.eventListener.<anonymous closure> (package:video_player_videohole/video_player.dart:426:24)
<asynchronous suspension>

_videoPlayerController.value.position never reached the end.

Could you help me to resolve it? Thanks.

@xiaowei-guan
Copy link
Contributor

Hello, @EricLinPixelforce , I have checked the position value, the value of position really cannot reach the end.
Do you need a completed event from the player?

@EricLinPixelforce
Copy link

Hello, @EricLinPixelforce , I have checked the position value, the value of position really cannot reach the end. Do you need a completed event from the player?

Yes, that's exactly what I want! I would be grateful if you could expose the event.

@xiaowei-guan
Copy link
Contributor

Hello, @EricLinPixelforce , I have checked the position value, the value of position really cannot reach the end. Do you need a completed event from the player?

Yes, that's exactly what I want! I would be grateful if you could expose the event.

Ok, I will add it.

@xiaowei-guan
Copy link
Contributor

@EricLinPixelforce Please verify this new PR #766

@EricLinPixelforce
Copy link

@EricLinPixelforce Please verify this new PR #766

Looks good, thanks. Have you tested it?

@xiaowei-guan
Copy link
Contributor

@EricLinPixelforce Please verify this new PR #766

Looks good, thanks. Have you tested it?

Yes, I have tested it at my side. you can verify this PR at your side.

@EricLinPixelforce
Copy link

@EricLinPixelforce Please verify this new PR #766

Looks good, thanks. Have you tested it?

Yes, I have tested it at my side. you can verify this PR at your side.

Hi @xiaowei-guan thank you for the solution, it works as expected, but an error message printed in the console.

[E] [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Pause, Player pause failed, null, null)
#0      VideoPlayerVideoholeApi.pause (package:video_player_videohole/src/messages.g.dart:790:7)
<asynchronous suspension>
#1      VideoPlayerController._applyPlayPause (package:video_player_videohole/video_player.dart:636:7)
<asynchronous suspension>
#2      VideoPlayerController.pause (package:video_player_videohole/video_player.dart:559:5)
<asynchronous suspension>
#3      VideoPlayerController.initialize.eventListener.<anonymous closure> (package:video_player_videohole/video_player.dart:448:24)
<asynchronous suspension>

@xiaowei-guan
Copy link
Contributor

@EricLinPixelforce Please verify this new PR #766

Looks good, thanks. Have you tested it?

Yes, I have tested it at my side. you can verify this PR at your side.

Hi @xiaowei-guan thank you for the solution, it works as expected, but an error message printed in the console.

[E] [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Pause, Player pause failed, null, null)
#0      VideoPlayerVideoholeApi.pause (package:video_player_videohole/src/messages.g.dart:790:7)
<asynchronous suspension>
#1      VideoPlayerController._applyPlayPause (package:video_player_videohole/video_player.dart:636:7)
<asynchronous suspension>
#2      VideoPlayerController.pause (package:video_player_videohole/video_player.dart:559:5)
<asynchronous suspension>
#3      VideoPlayerController.initialize.eventListener.<anonymous closure> (package:video_player_videohole/video_player.dart:448:24)
<asynchronous suspension>

I have checked the code, plugin need to stop position timer and keep position value = duration.end when receiving completed event, so plugin need call pause() and seekTo() to ensure the platfomr stops playing and seeks to the last frame of the video.
Because the native player's state not playing, so call pause() failed, got the pause failed message, I think we can ignore this message.

@EricLinPixelforce
Copy link

EricLinPixelforce commented Nov 6, 2024

Hi @xiaowei-guan thanks for the explanation.
The last issue I met is about the seekTo function. I saw the following limitation of the plugin

The seekTo method works only when the playback speed is 1.0, and it sets the video position to the nearest keyframe, not the exact value passed.

In my code, it listens the right arrow and left arrow buttons, the video will fast forward or backward when I click the buttons. But if I click a same button, say the right arrow button, multiple times in a short time, the current position of the video will back and forth, which make the progress bar looks weird.
My code about fast forward/backward:

final duration = _videoPlayerController.value.duration;
          final currentPosition = await _videoPlayerController.position;
          if (currentPosition != null) {
            final lengthSeconds =
                duration.end.inSeconds - duration.start.inSeconds;
            final currentSeconds = currentPosition.inSeconds;
            var newPosition = currentSeconds + 15;
            if (newPosition < 0) {
              newPosition = 0;
            }
            if (newPosition > lengthSeconds) {
              newPosition = lengthSeconds;
            }
            final newPositionDuration = Duration(seconds: newPosition);
            _videoPlayerController.seekTo(newPositionDuration);
          }

@EricLinPixelforce
Copy link

Hi @xiaowei-guan thanks for the explanation. The last issue I met is about the seekTo function. I saw the following limitation of the plugin

The seekTo method works only when the playback speed is 1.0, and it sets the video position to the nearest keyframe, not the exact value passed.

In my code, it listens the right arrow and left arrow buttons, the video will fast forward or backward when I click the buttons. But if I click a same button, say the right arrow button, multiple times in a short time, the current position of the video will back and forth, which make the progress bar looks weird. My code about fast forward/backward:

final duration = _videoPlayerController.value.duration;
          final currentPosition = await _videoPlayerController.position;
          if (currentPosition != null) {
            final lengthSeconds =
                duration.end.inSeconds - duration.start.inSeconds;
            final currentSeconds = currentPosition.inSeconds;
            var newPosition = currentSeconds + 15;
            if (newPosition < 0) {
              newPosition = 0;
            }
            if (newPosition > lengthSeconds) {
              newPosition = lengthSeconds;
            }
            final newPositionDuration = Duration(seconds: newPosition);
            _videoPlayerController.seekTo(newPositionDuration);
          }

Hi @xiaowei-guan @JSUYA could you help on this issue? Thanks

@xiaowei-guan
Copy link
Contributor

Hi @xiaowei-guan thanks for the explanation. The last issue I met is about the seekTo function. I saw the following limitation of the plugin

The seekTo method works only when the playback speed is 1.0, and it sets the video position to the nearest keyframe, not the exact value passed.

In my code, it listens the right arrow and left arrow buttons, the video will fast forward or backward when I click the buttons. But if I click a same button, say the right arrow button, multiple times in a short time, the current position of the video will back and forth, which make the progress bar looks weird. My code about fast forward/backward:

final duration = _videoPlayerController.value.duration;
          final currentPosition = await _videoPlayerController.position;
          if (currentPosition != null) {
            final lengthSeconds =
                duration.end.inSeconds - duration.start.inSeconds;
            final currentSeconds = currentPosition.inSeconds;
            var newPosition = currentSeconds + 15;
            if (newPosition < 0) {
              newPosition = 0;
            }
            if (newPosition > lengthSeconds) {
              newPosition = lengthSeconds;
            }
            final newPositionDuration = Duration(seconds: newPosition);
            _videoPlayerController.seekTo(newPositionDuration);
          }

Hi @xiaowei-guan @JSUYA could you help on this issue? Thanks

@EricLinPixelforce Below comment is native player seekTo guide:

  /**
   * @brief     Seek for playback, asynchronously.
   * @remarks   In case of non-seekable content, it will return @c False \n
   *            If application ignore this error,
   *             player will keep playing without changing play position. \n
   *            EventListener::OnSeekDone() will be called if seek operation is
   * finished \n Seek result can be succeeded or not at this moment. \n
   *            Resumeplay : Seek() can be called in #State::kIdle \n
   *            Call sequence of resumeplay : Open() -> Seek() with
   *            resume_position -> Prepare() -> Start()
   * @param     [in] time_millisecond : the absolute position(playingtime) of
   * the stream in milliseconds
   * @pre       The player state must be one of #State::kReady,
   *            #State::kPlaying or #State::kPaused or #State::kIdle
   * @post      None
   * @exception None
   * @return    @c True if seek operation is started without any problem
   * otherwise @c False
   * @see       EventListener::OnSeekDone()
   */
  virtual bool Seek(const uint64_t time_millisecond) { return false; }

Because SeekTo is asynchronously method, we need make sure previous seekTo operation is completed, then we can call next seekTo. If you call seekTo multiple times in a short time, it may only be processed once.

@EricLinPixelforce
Copy link

EricLinPixelforce commented Nov 12, 2024

@xiaowei-guan Hi, thanks for the reply. I understand the logic. However, if I call seekTo multiple times, currently it will wait for the completion of the first call, the other calls will be ignored. I believe it is ideal to take the last call as the valid one and cancel the previous calls.

@EricLinPixelforce
Copy link

@xiaowei-guan Hi, thanks for the reply. I understand the logic. However, if I call seekTo multiple times, currently it will wait for the completion of the first call, the other calls will be ignored. I believe it is ideal to take the last call as the valid one and cancel the previous calls.

@xiaowei-guan Hi, is it possible to fix this issue as well? Thanks.

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

4 participants