fvp icon indicating copy to clipboard operation
fvp copied to clipboard

Can't alternate between external/internal subtitles

Open azukaar opened this issue 1 year ago • 9 comments

I am currently setting subtitles this way:

        var subList = subtitleTracks.asMap().entries.map((entry) {
          int index = entry.key;
          var track = entry.value;
          
          var title = track.metadata['title'] ??
              track.metadata['language'] ??
              'Unknown';
              
          return ListTile(
            title: Text(title),
            onTap: () {
              player.activeSubtitleTracks = [index];
              closeDialog();
            },
          );
        }).toList();

        for (var exSub in widget.item.subtitles) {
          subList.add(ListTile(
            title: Text(exSub.language),
            onTap: () {
              player.setMedia(
                  "${widget.item.originURL}${exSub.link}", MediaType.subtitle);
              closeDialog();
            },
          ));
        }

This is building a list of subtitles, that are clickable. I can individually either set an internal OR external subtitles on a video. BUT if I set an external subtitle, I have to call player.setMedia, and after that, subsequent attempts at setting internal subtitle will fail. I attempted to fix the issue by recalling setMedia() for internal subtitle like this (with the video stream URL):

  onTap: () {
              print('Selected subtitle track: $index');
              player.setMedia("${widget.item.originURL}${widget.item.stream}",
                  MediaType.subtitle);
              player.activeSubtitleTracks = [index];
              closeDialog();
            },

It works, if I select an external and then an internal one, it will select it. But when i do this, if I select an internal subtitle (which is going to call SetMedia with the video) THEN an external subtitle, the player just freezes, so I am assuming I'm not supposed to do that

Thanks for your help

azukaar avatar Oct 13 '24 16:10 azukaar

can you provide a complete minimal code so I can test?

wang-bin avatar Oct 17 '24 11:10 wang-bin

Experiencing the same.

// load file and then on tap:
             player.setMedia(
                  "<url-to-same-file-that-was-loaded-before>", MediaType.subtitle);

Just this causes memory usage to go crazy, stutters and eventually crashed my laptop in 30 seconds.

23doors avatar Dec 11 '24 23:12 23doors

@23doors what's your OS?

With the latest fvp, you can use VideoPlayerController to use subtitle apis. I tested modified version of this example with the following patch and it works as expected

diff --git a/flutter/simple/lib/main.dart b/flutter/simple/lib/main.dart
index e79afde..c04ed5a 100644
--- a/flutter/simple/lib/main.dart
+++ b/flutter/simple/lib/main.dart
@@ -192,6 +192,9 @@ class _VideoAppState extends State<VideoApp> {
               final result = await FilePicker.platform.pickFiles(
                 type: FileType.any,
               );
+              if (result?.files.isNotEmpty ?? false) {
+                _controller.setExternalSubtitle(result!.files.first.path!);
+              }
               if (result?.files.isNotEmpty ?? false) {
                 playFile(result!.files.first.path!);
               }
@@ -202,12 +205,16 @@ class _VideoAppState extends State<VideoApp> {
           FloatingActionButton(
             heroTag: 'uri',
             tooltip: 'Open [Uri]',
-            onPressed: () {
-              showURIPicker(context).then((value) {
-                if (value != null) {
-                  playUri(value);
-                }
-              });
+            onPressed: () async {
+              final result = await FilePicker.platform.pickFiles(
+                type: FileType.any,
+              );
+              if (result?.files.isNotEmpty ?? false) {
+                _controller.setExternalSubtitle(result!.files.first.path!);
+              }
+              if (result?.files.isNotEmpty ?? false) {
+                _controller.setExternalSubtitle(result!.files.first.path!);
+              }
             },
             child: const Icon(Icons.link),
           ),
@@ -242,15 +249,15 @@ class _VideoAppState extends State<VideoApp> {
 class _ControlsOverlay extends StatelessWidget {
   const _ControlsOverlay({required this.controller});
 
-  static const List<double> _examplePlaybackRates = <double>[
-    0.25,
-    0.5,
-    1.0,
-    1.5,
-    2.0,
-    3.0,
-    5.0,
-    10.0,
+  static const List<int> _InternalSubtileTracks = <int>[
+    0,
+    1,
+    2,
+    3,
+    4,
+    5,
+    6,
+    7,
   ];
 
   final VideoPlayerController controller;
@@ -283,18 +290,19 @@ class _ControlsOverlay extends StatelessWidget {
         ),
         Align(
           alignment: Alignment.topRight,
-          child: PopupMenuButton<double>(
-            initialValue: controller.value.playbackSpeed,
-            tooltip: 'Playback speed',
-            onSelected: (double speed) {
-              controller.setPlaybackSpeed(speed);
+          child: PopupMenuButton<int>(
+            initialValue: 0,
+            tooltip: 'Embedded subtitle track index',
+            onSelected: (int track) {
+              controller.setExternalSubtitle(""); // disable external subtitle
+              controller.setSubtitleTracks([track]);
             },
             itemBuilder: (BuildContext context) {
-              return <PopupMenuItem<double>>[
-                for (final double speed in _examplePlaybackRates)
-                  PopupMenuItem<double>(
-                    value: speed,
-                    child: Text('${speed}x'),
+              return <PopupMenuItem<int>>[
+                for (final int track in _InternalSubtileTracks)
+                  PopupMenuItem<int>(
+                    value: track,
+                    child: Text('${track}x'),
                   )
               ];
             },

wang-bin avatar Dec 12 '24 13:12 wang-bin

MacOS Sequoia 15.1.1 Just tested with fresh fvp example, only thing I changed was: in initstate used localfile:

    _controller = VideoPlayerController.file(
      File(
          '<path to local file>'),
      closedCaptionFile: _loadCaptions(),
      videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
    );

and in build added:

          TextButton(
              onPressed: () {
                _controller.setExternalSubtitle(
                    "file://<path to same file>");
              },
              child: Text("switch")),

When I click the button, memory goes crazy pretty much immediately and ends up crashing.

I just tried what you suggested instead - _controller.setExternalSubtitle("") to switch back to embedded subs - and this works fine, no issues!

23doors avatar Dec 12 '24 14:12 23doors

When I click the button, memory goes crazy pretty much immediately and ends up crashing

show me the log. iirc i had a similar result because some fonts are missing on macos15

wang-bin avatar Dec 12 '24 14:12 wang-bin

How are fonts related here? I play the file - works fine with embedded subs. I use _controller.setExternalSubtitle() with exactly same file - crashes.

And there is nothing in the logs.

# file was playing, I paused it here to keep logs clean
0x1358b1e00>1857 00:00:01.7/00:15:59 cache 0v 31.2s/47905KB 82374KB/s |-65|>4ms update infms 24.0fps draw 0/0 +41ms
# button pressed:
[AVCoderBase.subrip] EOF
0x1358b1e00>1833 00:00:01.8/00:15:59 cache 0v 2.1s/21993KB 825KB/s |+1833|>4ms update 2053.0ms 23.4fps draw 0/0 +42ms

And that's it. Nothing happens but memory pressure starts rising quickly.

23doors avatar Dec 12 '24 14:12 23doors

@23doors can you share your subtitle file so i can test?

wang-bin avatar Dec 12 '24 14:12 wang-bin

It's a movie with embedded srt track inside a mkv container. So I play this movie and called setExternalSubtitle with path to this specific movie as well. But if you want, I can extract the subs. Not sure if it will behave the same though. I can also test with a different movie file if you want and see if this happens. Just need to find some free to download mkv with subs.

23doors avatar Dec 12 '24 14:12 23doors

It's a movie with embedded srt track inside a mkv container. So I play this movie and called setExternalSubtitle with path to this specific movie as well. But if you want, I can extract the subs. Not sure if it will behave the same though. I can also test with a different movie file if you want and see if this happens. Just need to find some free to download mkv with subs.

@23doors i can't reproduce the issue with my videos. you can extract srt and let me try

wang-bin avatar Dec 14 '24 06:12 wang-bin