From 0b0cfce0a488fb4a48ce5039e1049e575d1ffea8 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 3 May 2020 13:40:43 +0900 Subject: [PATCH 01/29] support playback local file on Android --- .../fluttervlcplayer/FlutterVideoView.java | 22 ++++++++++++++++--- lib/flutter_vlc_player.dart | 12 +++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 77918b92..decc33cf 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.io.File; class FlutterVideoView implements PlatformView, MethodChannel.MethodCallHandler, MediaPlayer.EventListener { @@ -138,6 +139,7 @@ public void dispose() { @SuppressLint("WrongThread") @Override public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result result) { + Boolean isLocal=false; switch (methodCall.method) { case "initialize": if (textureView == null) { @@ -147,7 +149,6 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re ArrayList options = new ArrayList<>(); options.add("--no-drop-late-frames"); options.add("--no-skip-frames"); - options.add("--rtsp-tcp"); if(DISABLE_LOG_OUTPUT) { // Silence player log output. @@ -165,7 +166,16 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re vout.attachViews(); String initStreamURL = methodCall.argument("url"); - Media media = new Media(libVLC, Uri.parse(Uri.decode(initStreamURL))); + isLocal=methodCall.argument("isLocal"); + + Media media = null; + if (isLocal) + media=new Media(libVLC, Uri.fromFile(new File(initStreamURL))); + else { + options.add("--rtsp-tcp"); + media = new Media(libVLC, Uri.parse(Uri.decode(initStreamURL))); + } + mediaPlayer.setMedia(media); result.success(null); @@ -178,7 +188,13 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re mediaPlayer.stop(); String newURL = methodCall.argument("url"); - Media newMedia = new Media(libVLC, Uri.parse(Uri.decode(newURL))); + isLocal = methodCall.argument("isLocal"); + Media newMedia=null; + if (isLocal) + newMedia = new Media(libVLC, Uri.fromFile(new File(newURL))); + else + newMedia = new Media(libVLC, Uri.parse(Uri.decode(newURL))); + newMedia.setHWDecoderEnabled(true, true); mediaPlayer.setMedia(newMedia); result.success(null); diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index 033efb3a..f5ee7471 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -24,6 +24,7 @@ class Size { class VlcPlayer extends StatefulWidget { final double aspectRatio; final String url; + final bool isLocal; final Widget placeholder; final VlcPlayerController controller; @@ -41,6 +42,7 @@ class VlcPlayer extends StatefulWidget { /// This is the initial URL for the content. This also must be provided but [VlcPlayerController] implements /// [VlcPlayerController.setStreamUrl] method so this can be changed at any time. @required this.url, + this.isLocal=false, /// Before the platform view has initialized, this placeholder will be rendered instead of the video player. /// This can simply be a [CircularProgressIndicator] (see the example.) @@ -120,7 +122,7 @@ class _VlcPlayerState extends State // Once the controller has clients registered, we're good to register // with LibVLC on the platform side. if (_controller.hasClients) { - await _controller._initialize(widget.url); + await _controller._initialize(widget.url, widget.isLocal); } } @@ -244,10 +246,10 @@ class VlcPlayerController { _eventHandlers.forEach((handler) => handler()); } - Future _initialize(String url) async { + Future _initialize(String url, bool isLocal) async { //if(initialized) throw new Exception("Player already initialized!"); - await _methodChannel.invokeMethod("initialize", {'url': url}); + await _methodChannel.invokeMethod("initialize", {'url': url, 'isLocal':isLocal}); _position = 0; _eventChannel.receiveBroadcastStream().listen((event) { @@ -282,12 +284,12 @@ class VlcPlayerController { _onInit(); } - Future setStreamUrl(String url) async { + Future setStreamUrl(String url, bool isLocal) async { _initialized = false; _fireEventHandlers(); bool wasPlaying = _playingState != PlayingState.STOPPED; - await _methodChannel.invokeMethod("changeURL", {'url': url}); + await _methodChannel.invokeMethod("changeURL", {'url': url, 'isLocal':isLocal}); if (wasPlaying) play(); _initialized = true; From e17db9e976d1d15311fd107cbb503177a0e8a823 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 3 May 2020 14:02:15 +0900 Subject: [PATCH 02/29] Add more helper methods for android --- .../fluttervlcplayer/FlutterVideoView.java | 47 +++++++++++++++++-- lib/flutter_vlc_player.dart | 28 +++++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index decc33cf..4edd26c3 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -140,6 +140,9 @@ public void dispose() { @Override public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result result) { Boolean isLocal=false; + long time=0; + float rate= (float) 1.0; + int track=-1; switch (methodCall.method) { case "initialize": if (textureView == null) { @@ -219,10 +222,12 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re switch(playbackState){ case "play": textureView.forceLayout(); - mediaPlayer.play(); + if (!mediaPlayer.isPlaying()) + mediaPlayer.play(); break; case "pause": - mediaPlayer.pause(); + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); break; case "stop": mediaPlayer.stop(); @@ -239,14 +244,40 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re result.success(null); break; + case "getPlaybackSpeed": + rate = mediaPlayer.getRate(); + result.success(rate); + break; case "setTime": - - long time = Long.parseLong((String) methodCall.argument("time")); + time = Long.parseLong((String) methodCall.argument("time")); mediaPlayer.setTime(time); result.success(null); break; + case "getTime": + time = mediaPlayer.getTime(); + result.success(time); + break; + case "getDuration": + time = mediaPlayer.getLength(); + result.success(time); + break; + case "isPlaying": + result.success(mediaPlayer.isPlaying()); + break; + case "setSubtitleTrack": + track = methodCall.argument("track"); + mediaPlayer.setSpuTrack(track); + break; + case "getSubtitleTrackCount": + track=mediaPlayer.getSpuTracksCount(); + result.success(track); + break; + case "addSubtitle": + String subtitle = methodCall.argument("subtitle"); + mediaPlayer.addSlave(Media.Slave.Type.Subtitle, subtitle, true); + break; } } @@ -282,7 +313,13 @@ public void onEvent(MediaPlayer.Event event) { eventObject.put("length", mediaPlayer.getLength()); eventSink.success(eventObject.clone()); break; - + case MediaPlayer.Event.PositionChanged: + float pos = event.getPositionChanged(); + eventObject.put("name", "position"); + eventObject.put("value", pos); + eventSink.success(eventObject.clone()); + eventObject.clear(); + break; case MediaPlayer.Event.EndReached: mediaPlayer.stop(); eventObject.put("name", "ended"); diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index f5ee7471..9c54619e 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -311,15 +311,31 @@ class VlcPlayerController { .invokeMethod("setPlaybackState", {'playbackState': 'stop'}); } + Future isPlaying() async { + var result = await _methodChannel.invokeMethod("isPlaying"); + return result; + } + Future setTime(int time) async { await _methodChannel.invokeMethod("setTime", {'time': time.toString()}); } + //return ms position + Future getTime() async { + var result = await _methodChannel.invokeMethod("getTime"); + return result; + } + Future setPlaybackSpeed(double speed) async { await _methodChannel .invokeMethod("setPlaybackSpeed", {'speed': speed.toString()}); } + Future getPlaybackSpeed() async { + var result = await _methodChannel.invokeMethod("getPlaybackSpeed"); + return result; + } + Future takeSnapshot() async { var result = await _methodChannel.invokeMethod("getSnapshot"); var base64String = result['snapshot']; @@ -327,6 +343,18 @@ class VlcPlayerController { return imageBytes; } + Future setSubtitleTrack(int track) async { + await _methodChannel.invokeMethod("setSubtitleTrack", { + 'track': track, + }); + } + + Future getSubtitleTrackCount() async { + int cnt=await _methodChannel.invokeMethod("getSubtitleTrackCount"); + return cnt; + } + + void dispose() { _methodChannel.invokeMethod("dispose"); } From 00e39b7832631be7abe8c64d3266572411e594ee Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 3 May 2020 15:02:00 +0900 Subject: [PATCH 03/29] Add subtitle display support for Android --- .../solid/fluttervlcplayer/FlutterVideoView.java | 6 +++++- lib/flutter_vlc_player.dart | 16 +++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 4edd26c3..93c75841 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -143,6 +143,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re long time=0; float rate= (float) 1.0; int track=-1; + String subtitle=""; switch (methodCall.method) { case "initialize": if (textureView == null) { @@ -170,6 +171,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re String initStreamURL = methodCall.argument("url"); isLocal=methodCall.argument("isLocal"); + subtitle=methodCall.argument("subtitle"); Media media = null; if (isLocal) @@ -180,6 +182,8 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re } mediaPlayer.setMedia(media); + if (!subtitle.isEmpty()) + mediaPlayer.addSlave(Media.Slave.Type.Subtitle, subtitle, true); result.success(null); break; @@ -275,7 +279,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re result.success(track); break; case "addSubtitle": - String subtitle = methodCall.argument("subtitle"); + subtitle = methodCall.argument("subtitle"); mediaPlayer.addSlave(Media.Slave.Type.Subtitle, subtitle, true); break; } diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index 9c54619e..fa8dc28c 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -25,6 +25,7 @@ class VlcPlayer extends StatefulWidget { final double aspectRatio; final String url; final bool isLocal; + final String subtitle; final Widget placeholder; final VlcPlayerController controller; @@ -43,6 +44,7 @@ class VlcPlayer extends StatefulWidget { /// [VlcPlayerController.setStreamUrl] method so this can be changed at any time. @required this.url, this.isLocal=false, + this.subtitle="", /// Before the platform view has initialized, this placeholder will be rendered instead of the video player. /// This can simply be a [CircularProgressIndicator] (see the example.) @@ -122,7 +124,7 @@ class _VlcPlayerState extends State // Once the controller has clients registered, we're good to register // with LibVLC on the platform side. if (_controller.hasClients) { - await _controller._initialize(widget.url, widget.isLocal); + await _controller._initialize(widget.url, widget.isLocal, widget.subtitle); } } @@ -246,10 +248,10 @@ class VlcPlayerController { _eventHandlers.forEach((handler) => handler()); } - Future _initialize(String url, bool isLocal) async { + Future _initialize(String url, bool isLocal, String subtitle) async { //if(initialized) throw new Exception("Player already initialized!"); - await _methodChannel.invokeMethod("initialize", {'url': url, 'isLocal':isLocal}); + await _methodChannel.invokeMethod("initialize", {'url': url, 'isLocal':isLocal, 'subtitle':subtitle}); _position = 0; _eventChannel.receiveBroadcastStream().listen((event) { @@ -276,6 +278,10 @@ class VlcPlayerController { _playbackSpeed = event['speed']; _fireEventHandlers(); break; + case 'position': + _position = event['value']; + _fireEventHandlers(); + break; } }); @@ -284,12 +290,12 @@ class VlcPlayerController { _onInit(); } - Future setStreamUrl(String url, bool isLocal) async { + Future setStreamUrl(String url, bool isLocal, String subtitle) async { _initialized = false; _fireEventHandlers(); bool wasPlaying = _playingState != PlayingState.STOPPED; - await _methodChannel.invokeMethod("changeURL", {'url': url, 'isLocal':isLocal}); + await _methodChannel.invokeMethod("changeURL", {'url': url, 'isLocal':isLocal, 'subtitle':subtitle}); if (wasPlaying) play(); _initialized = true; From 329727e06c84bef44575b5bcc7b5959284182aab Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 3 May 2020 15:09:30 +0900 Subject: [PATCH 04/29] Add OnStatusChanged event for vlc player controller --- lib/flutter_vlc_player.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index fa8dc28c..417682bc 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +typedef StatusChanged = void Function(String status, T value); + enum PlayingState { STOPPED, BUFFERING, PLAYING } class Size { @@ -149,6 +151,8 @@ class VlcPlayerController { VoidCallback _onInit; List _eventHandlers; + StatusChanged onStatusChanged=null; + /// Once the [_methodChannel] and [_eventChannel] have been registered with /// the Flutter platform SDK counterparts, [hasClients] is set to true. /// At this point, the player is ready to begin playing content. @@ -221,7 +225,9 @@ class VlcPlayerController { /// This is a callback that will be executed once the platform view has been initialized. /// If you want the media to play as soon as the platform view has initialized, you could just call /// [VlcPlayerController.play] in this callback. (see the example) - VoidCallback onInit}) { + VoidCallback onInit, + this.onStatusChanged + }) { _onInit = onInit; _eventHandlers = new List(); } @@ -283,6 +289,9 @@ class VlcPlayerController { _fireEventHandlers(); break; } + + if (this.onStatusChanged!=null) + this.onStatusChanged(event['name'], event['value']); }); _initialized = true; From 2b3519242191257affb1570189e6631853dfa3d4 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Mon, 4 May 2020 01:24:02 +0900 Subject: [PATCH 05/29] support subtitle display --- .../fluttervlcplayer/FlutterVideoView.java | 139 +++++++++++++----- lib/flutter_vlc_player.dart | 2 +- 2 files changed, 100 insertions(+), 41 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 93c75841..f33b6ebe 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -3,20 +3,25 @@ import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.PixelFormat; import android.graphics.SurfaceTexture; import android.net.Uri; import android.util.Base64; import android.view.Surface; import android.view.TextureView; import android.view.View; + import androidx.annotation.NonNull; + import io.flutter.plugin.common.*; import io.flutter.plugin.platform.PlatformView; import io.flutter.view.TextureRegistry; + import org.videolan.libvlc.IVLCVout; import org.videolan.libvlc.LibVLC; import org.videolan.libvlc.Media; import org.videolan.libvlc.MediaPlayer; +import org.videolan.libvlc.util.VLCVideoLayout; import java.io.ByteArrayOutputStream; import java.util.ArrayList; @@ -39,10 +44,15 @@ class FlutterVideoView implements PlatformView, MethodChannel.MethodCallHandler, private LibVLC libVLC; private MediaPlayer mediaPlayer; + private VLCVideoLayout frameLayout; private TextureView textureView; + private TextureView subtitleView; private IVLCVout vout; private boolean playerDisposed; + private Boolean subtitleTextureValid=false; + private Boolean videoTextureValid=false; + public FlutterVideoView(Context context, PluginRegistry.Registrar _registrar, BinaryMessenger messenger, int id) { this.playerDisposed = false; @@ -53,34 +63,43 @@ public FlutterVideoView(Context context, PluginRegistry.Registrar _registrar, Bi eventChannel = new EventChannel(messenger, "flutter_video_plugin/getVideoEvents_" + id); eventChannel.setStreamHandler( - new EventChannel.StreamHandler() { - @Override - public void onListen(Object o, EventChannel.EventSink sink) { - eventSink.setDelegate(sink); - } + new EventChannel.StreamHandler() { + @Override + public void onListen(Object o, EventChannel.EventSink sink) { + eventSink.setDelegate(sink); + } - @Override - public void onCancel(Object o) { - eventSink.setDelegate(null); + @Override + public void onCancel(Object o) { + eventSink.setDelegate(null); + } } - } ); TextureRegistry.SurfaceTextureEntry textureEntry = registrar.textures().createSurfaceTexture(); - textureView = new TextureView(context); + TextureRegistry.SurfaceTextureEntry subtitleEntry = registrar.textures().createSurfaceTexture(); + createLayout(context); textureView.setSurfaceTexture(textureEntry.surfaceTexture()); - textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener(){ + subtitleView.setSurfaceTexture(subtitleEntry.surfaceTexture()); + +// subtitleView.setZOrderMediaOverlay(true); +// subtitleView.getHolder().setFormat(PixelFormat.TRANSLUCENT); + + textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { boolean wasPaused = false; @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - if(vout == null) return; + if (vout == null) return; vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); - vout.attachViews(); + videoTextureValid=true; + + if (subtitleTextureValid && videoTextureValid) + vout.attachViews(); textureView.forceLayout(); - if(wasPaused){ + if (wasPaused) { mediaPlayer.play(); wasPaused = false; } @@ -93,15 +112,15 @@ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int h @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - if(playerDisposed){ - if(mediaPlayer != null) { + if (playerDisposed) { + if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } return true; - }else{ - if(mediaPlayer != null && vout != null) { + } else { + if (mediaPlayer != null && vout != null) { mediaPlayer.pause(); wasPaused = true; vout.detachViews(); @@ -117,19 +136,57 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { }); + subtitleView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) { + if (vout == null) return; + + vout.setSubtitlesSurface(new Surface(subtitleView.getSurfaceTexture()), null); + subtitleTextureValid=true; + + if (subtitleTextureValid && videoTextureValid) + vout.attachViews(); + subtitleView.forceLayout(); + + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { + + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { + return false; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + + } + }); + methodChannel = new MethodChannel(messenger, "flutter_video_plugin/getVideoView_" + id); methodChannel.setMethodCallHandler(this); } + private void createLayout(Context context) { + frameLayout = new VLCVideoLayout(context); + textureView = new TextureView(context); + subtitleView =new TextureView(context); + frameLayout.addView(textureView); + frameLayout.addView(subtitleView); + } + @Override public View getView() { - return textureView; + return frameLayout; } @Override public void dispose() { - if(mediaPlayer != null) mediaPlayer.stop(); - if(vout != null) vout.detachViews(); + if (mediaPlayer != null) mediaPlayer.stop(); + if (vout != null) vout.detachViews(); playerDisposed = true; } @@ -139,22 +196,22 @@ public void dispose() { @SuppressLint("WrongThread") @Override public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result result) { - Boolean isLocal=false; - long time=0; - float rate= (float) 1.0; - int track=-1; - String subtitle=""; + Boolean isLocal = false; + long time = 0; + float rate = (float) 1.0; + int track = -1; + String subtitle = ""; switch (methodCall.method) { case "initialize": - if (textureView == null) { - textureView = new TextureView(context); + if (frameLayout == null) { + createLayout(context); } ArrayList options = new ArrayList<>(); options.add("--no-drop-late-frames"); options.add("--no-skip-frames"); - if(DISABLE_LOG_OUTPUT) { + if (DISABLE_LOG_OUTPUT) { // Silence player log output. options.add("--quiet"); } @@ -167,15 +224,16 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re textureView.forceLayout(); textureView.setFitsSystemWindows(true); vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); +// vout.setSubtitlesSurface(new Surface(textureView.getSurfaceTexture()), null); vout.attachViews(); String initStreamURL = methodCall.argument("url"); - isLocal=methodCall.argument("isLocal"); - subtitle=methodCall.argument("subtitle"); + isLocal = methodCall.argument("isLocal"); + subtitle = methodCall.argument("subtitle"); Media media = null; if (isLocal) - media=new Media(libVLC, Uri.fromFile(new File(initStreamURL))); + media = new Media(libVLC, Uri.fromFile(new File(initStreamURL))); else { options.add("--rtsp-tcp"); media = new Media(libVLC, Uri.parse(Uri.decode(initStreamURL))); @@ -191,12 +249,13 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re this.dispose(); break; case "changeURL": - if(libVLC == null) result.error("VLC_NOT_INITIALIZED", "The player has not yet been initialized.", false); + if (libVLC == null) + result.error("VLC_NOT_INITIALIZED", "The player has not yet been initialized.", false); mediaPlayer.stop(); String newURL = methodCall.argument("url"); isLocal = methodCall.argument("isLocal"); - Media newMedia=null; + Media newMedia = null; if (isLocal) newMedia = new Media(libVLC, Uri.fromFile(new File(newURL))); else @@ -221,9 +280,9 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re case "setPlaybackState": String playbackState = methodCall.argument("playbackState"); - if(playbackState == null) result.success(null); + if (playbackState == null) result.success(null); - switch(playbackState){ + switch (playbackState) { case "play": textureView.forceLayout(); if (!mediaPlayer.isPlaying()) @@ -231,7 +290,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re break; case "pause": if (mediaPlayer.isPlaying()) - mediaPlayer.pause(); + mediaPlayer.pause(); break; case "stop": mediaPlayer.stop(); @@ -275,7 +334,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re mediaPlayer.setSpuTrack(track); break; case "getSubtitleTrackCount": - track=mediaPlayer.getSpuTracksCount(); + track = mediaPlayer.getSpuTracksCount(); result.success(track); break; case "addSubtitle": @@ -302,7 +361,7 @@ public void onEvent(MediaPlayer.Event event) { int width = 0; Media.VideoTrack currentVideoTrack = (Media.VideoTrack) mediaPlayer.getMedia().getTrack( - mediaPlayer.getVideoTrack() + mediaPlayer.getVideoTrack() ); if (currentVideoTrack != null) { height = currentVideoTrack.height; @@ -334,7 +393,7 @@ public void onEvent(MediaPlayer.Event event) { eventObject.put("value", false); eventObject.put("reason", "EndReached"); eventSink.success(eventObject); - + case MediaPlayer.Event.Vout: vout.setWindowSize(textureView.getWidth(), textureView.getHeight()); break; diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index 417682bc..c49dbfa0 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -285,7 +285,7 @@ class VlcPlayerController { _fireEventHandlers(); break; case 'position': - _position = event['value']; + //_position = event['value']; _fireEventHandlers(); break; } From 8ec95ebfd80165075cc9450510535de0af5e8766 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Mon, 4 May 2020 02:44:53 +0900 Subject: [PATCH 06/29] comment out some not used code --- .../fluttervlcplayer/FlutterVideoView.java | 194 +++++++++--------- 1 file changed, 99 insertions(+), 95 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index f33b6ebe..e5e6f8c3 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -8,6 +8,7 @@ import android.net.Uri; import android.util.Base64; import android.view.Surface; +import android.view.SurfaceView; import android.view.TextureView; import android.view.View; @@ -46,12 +47,12 @@ class FlutterVideoView implements PlatformView, MethodChannel.MethodCallHandler, private MediaPlayer mediaPlayer; private VLCVideoLayout frameLayout; private TextureView textureView; - private TextureView subtitleView; +// private TextureView subtitleView; private IVLCVout vout; private boolean playerDisposed; - private Boolean subtitleTextureValid=false; - private Boolean videoTextureValid=false; +// private Boolean subtitleTextureValid=false; +// private Boolean videoTextureValid=false; public FlutterVideoView(Context context, PluginRegistry.Registrar _registrar, BinaryMessenger messenger, int id) { this.playerDisposed = false; @@ -76,95 +77,95 @@ public void onCancel(Object o) { } ); - TextureRegistry.SurfaceTextureEntry textureEntry = registrar.textures().createSurfaceTexture(); - TextureRegistry.SurfaceTextureEntry subtitleEntry = registrar.textures().createSurfaceTexture(); +// TextureRegistry.SurfaceTextureEntry textureEntry = registrar.textures().createSurfaceTexture(); +// TextureRegistry.SurfaceTextureEntry subtitleEntry = registrar.textures().createSurfaceTexture(); createLayout(context); - textureView.setSurfaceTexture(textureEntry.surfaceTexture()); - subtitleView.setSurfaceTexture(subtitleEntry.surfaceTexture()); - -// subtitleView.setZOrderMediaOverlay(true); -// subtitleView.getHolder().setFormat(PixelFormat.TRANSLUCENT); - - textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { - - boolean wasPaused = false; - - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - if (vout == null) return; - - vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); - videoTextureValid=true; - - if (subtitleTextureValid && videoTextureValid) - vout.attachViews(); - textureView.forceLayout(); - if (wasPaused) { - mediaPlayer.play(); - wasPaused = false; - } - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - if (playerDisposed) { - if (mediaPlayer != null) { - mediaPlayer.stop(); - mediaPlayer.release(); - mediaPlayer = null; - } - return true; - } else { - if (mediaPlayer != null && vout != null) { - mediaPlayer.pause(); - wasPaused = true; - vout.detachViews(); - } - return true; - } - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - - }); - - subtitleView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) { - if (vout == null) return; - - vout.setSubtitlesSurface(new Surface(subtitleView.getSurfaceTexture()), null); - subtitleTextureValid=true; - - if (subtitleTextureValid && videoTextureValid) - vout.attachViews(); - subtitleView.forceLayout(); - - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { - - } - }); +// textureView.setSurfaceTexture(textureEntry.surfaceTexture()); +// subtitleView.setSurfaceTexture(subtitleEntry.surfaceTexture()); +// +//// subtitleView.setZOrderMediaOverlay(true); +//// subtitleView.getHolder().setFormat(PixelFormat.TRANSLUCENT); +// +// textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { +// +// boolean wasPaused = false; +// +// @Override +// public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { +// if (vout == null) return; +// +// vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); +//// videoTextureValid=true; +//// +//// if (subtitleTextureValid && videoTextureValid) +// vout.attachViews(); +// textureView.forceLayout(); +// if (wasPaused) { +// mediaPlayer.play(); +// wasPaused = false; +// } +// } +// +// @Override +// public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { +// +// } +// +// @Override +// public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { +// if (playerDisposed) { +// if (mediaPlayer != null) { +// mediaPlayer.stop(); +// mediaPlayer.release(); +// mediaPlayer = null; +// } +// return true; +// } else { +// if (mediaPlayer != null && vout != null) { +// mediaPlayer.pause(); +// wasPaused = true; +// vout.detachViews(); +// } +// return true; +// } +// } +// +// @Override +// public void onSurfaceTextureUpdated(SurfaceTexture surface) { +// +// } +// +// }); + +// subtitleView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { +// @Override +// public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) { +// if (vout == null) return; +// +// vout.setSubtitlesSurface(new Surface(subtitleView.getSurfaceTexture()), null); +// subtitleTextureValid=true; +// +// if (subtitleTextureValid && videoTextureValid) +// vout.attachViews(); +// subtitleView.forceLayout(); +// +// } +// +// @Override +// public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { +// +// } +// +// @Override +// public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { +// return false; +// } +// +// @Override +// public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { +// +// } +// }); methodChannel = new MethodChannel(messenger, "flutter_video_plugin/getVideoView_" + id); methodChannel.setMethodCallHandler(this); @@ -173,9 +174,9 @@ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { private void createLayout(Context context) { frameLayout = new VLCVideoLayout(context); textureView = new TextureView(context); - subtitleView =new TextureView(context); +// subtitleView =new TextureView(context); frameLayout.addView(textureView); - frameLayout.addView(subtitleView); +// frameLayout.addView(subtitleView); } @Override @@ -223,8 +224,11 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re vout = mediaPlayer.getVLCVout(); textureView.forceLayout(); textureView.setFitsSystemWindows(true); - vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); -// vout.setSubtitlesSurface(new Surface(textureView.getSurfaceTexture()), null); + //subtitleView.forceLayout(); + //textureView.setFitsSystemWindows(true); + //vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); + vout.setVideoView(textureView); + //vout.setSubtitlesView(subtitleView); vout.attachViews(); String initStreamURL = methodCall.argument("url"); From 48c443491902961e876619dd39dc45840c98b87a Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sat, 9 May 2020 23:46:23 +0900 Subject: [PATCH 07/29] cleanup the player state management --- .../solid/fluttervlcplayer/FlutterVideoView.java | 15 +++++++++------ lib/flutter_vlc_player.dart | 14 ++++++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index e5e6f8c3..8f5536cc 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -211,6 +211,9 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re ArrayList options = new ArrayList<>(); options.add("--no-drop-late-frames"); options.add("--no-skip-frames"); +// options.add("--android-display-chroma"); +// options.add("RV16"); +// options.add("--vout=android_display,none"); if (DISABLE_LOG_OUTPUT) { // Silence player log output. @@ -412,14 +415,14 @@ public void onEvent(MediaPlayer.Event event) { case MediaPlayer.Event.EncounteredError: System.err.println("(flutter_vlc_plugin) A VLC error occurred."); case MediaPlayer.Event.Paused: - case MediaPlayer.Event.Stopped: - eventObject.put("name", "buffering"); - eventObject.put("value", false); + eventObject.clear(); + eventObject.put("name", "paused"); + eventObject.put("value", true); eventSink.success(eventObject); - + case MediaPlayer.Event.Stopped: eventObject.clear(); - eventObject.put("name", "playing"); - eventObject.put("value", false); + eventObject.put("name", "stopped"); + eventObject.put("value", true); eventSink.success(eventObject); break; } diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index c49dbfa0..4b8283cf 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -10,7 +10,7 @@ import 'package:flutter/services.dart'; typedef StatusChanged = void Function(String status, T value); -enum PlayingState { STOPPED, BUFFERING, PLAYING } +enum PlayingState { STOPPED, PAUSED, PLAYING } class Size { final int width; @@ -268,14 +268,16 @@ class VlcPlayerController { if (event['length'] != null) _duration = event['length']; if (event['ratio'] != null) _aspectRatio = event['ratio']; - _playingState = - event['value'] ? PlayingState.PLAYING : PlayingState.STOPPED; - + _playingState = PlayingState.PLAYING; _fireEventHandlers(); break; - case 'buffering': - if (event['value']) _playingState = PlayingState.BUFFERING; + case 'paused': + _playingState = PlayingState.PAUSED; + _fireEventHandlers(); + break; + case 'stopped': + _playingState = PlayingState.STOPPED; _fireEventHandlers(); break; From 0149e104683b0103f4468690cc94ce655226f351 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 10 May 2020 01:06:48 +0900 Subject: [PATCH 08/29] Support loop forever --- .../solid/fluttervlcplayer/FlutterVideoView.java | 14 +++++++++----- lib/flutter_vlc_player.dart | 8 +++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 8f5536cc..20663ac6 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -202,6 +202,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re float rate = (float) 1.0; int track = -1; String subtitle = ""; + Boolean loop=false; switch (methodCall.method) { case "initialize": if (frameLayout == null) { @@ -219,6 +220,14 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re // Silence player log output. options.add("--quiet"); } + String initStreamURL = methodCall.argument("url"); + isLocal = methodCall.argument("isLocal"); + subtitle = methodCall.argument("subtitle"); + loop=methodCall.argument("loop"); + if (loop) + options.add("--input-repeat=65535"); + if (!isLocal) + options.add("--rtsp-tcp"); libVLC = new LibVLC(context, options); mediaPlayer = new MediaPlayer(libVLC); @@ -234,15 +243,10 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re //vout.setSubtitlesView(subtitleView); vout.attachViews(); - String initStreamURL = methodCall.argument("url"); - isLocal = methodCall.argument("isLocal"); - subtitle = methodCall.argument("subtitle"); - Media media = null; if (isLocal) media = new Media(libVLC, Uri.fromFile(new File(initStreamURL))); else { - options.add("--rtsp-tcp"); media = new Media(libVLC, Uri.parse(Uri.decode(initStreamURL))); } diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index 4b8283cf..60f60379 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -28,6 +28,7 @@ class VlcPlayer extends StatefulWidget { final String url; final bool isLocal; final String subtitle; + final bool loop; final Widget placeholder; final VlcPlayerController controller; @@ -47,6 +48,7 @@ class VlcPlayer extends StatefulWidget { @required this.url, this.isLocal=false, this.subtitle="", + this.loop=true, /// Before the platform view has initialized, this placeholder will be rendered instead of the video player. /// This can simply be a [CircularProgressIndicator] (see the example.) @@ -126,7 +128,7 @@ class _VlcPlayerState extends State // Once the controller has clients registered, we're good to register // with LibVLC on the platform side. if (_controller.hasClients) { - await _controller._initialize(widget.url, widget.isLocal, widget.subtitle); + await _controller._initialize(widget.url, widget.isLocal, widget.subtitle, widget.loop); } } @@ -254,10 +256,10 @@ class VlcPlayerController { _eventHandlers.forEach((handler) => handler()); } - Future _initialize(String url, bool isLocal, String subtitle) async { + Future _initialize(String url, bool isLocal, String subtitle, bool loop) async { //if(initialized) throw new Exception("Player already initialized!"); - await _methodChannel.invokeMethod("initialize", {'url': url, 'isLocal':isLocal, 'subtitle':subtitle}); + await _methodChannel.invokeMethod("initialize", {'url': url, 'isLocal':isLocal, 'subtitle':subtitle, 'loop':loop}); _position = 0; _eventChannel.receiveBroadcastStream().listen((event) { From e6d7f88499b89c929efd246bc69fa688393a4680 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 10 May 2020 03:55:18 +0900 Subject: [PATCH 09/29] fix get subtitle track id --- .../fluttervlcplayer/FlutterVideoView.java | 18 ++++++++++++------ lib/flutter_vlc_player.dart | 6 +++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 20663ac6..1bd35dca 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -27,6 +27,7 @@ import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.io.File; @@ -47,7 +48,7 @@ class FlutterVideoView implements PlatformView, MethodChannel.MethodCallHandler, private MediaPlayer mediaPlayer; private VLCVideoLayout frameLayout; private TextureView textureView; -// private TextureView subtitleView; + // private TextureView subtitleView; private IVLCVout vout; private boolean playerDisposed; @@ -202,7 +203,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re float rate = (float) 1.0; int track = -1; String subtitle = ""; - Boolean loop=false; + Boolean loop = false; switch (methodCall.method) { case "initialize": if (frameLayout == null) { @@ -223,7 +224,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re String initStreamURL = methodCall.argument("url"); isLocal = methodCall.argument("isLocal"); subtitle = methodCall.argument("subtitle"); - loop=methodCall.argument("loop"); + loop = methodCall.argument("loop"); if (loop) options.add("--input-repeat=65535"); if (!isLocal) @@ -344,9 +345,14 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re track = methodCall.argument("track"); mediaPlayer.setSpuTrack(track); break; - case "getSubtitleTrackCount": - track = mediaPlayer.getSpuTracksCount(); - result.success(track); + case "getSubtitleTracks": + MediaPlayer.TrackDescription[] tracks = mediaPlayer.getSpuTracks(); + List list = new ArrayList(); + for (MediaPlayer.TrackDescription t : tracks) { + if (t.id >= 0) + list.add(t.id); + } + result.success(list); break; case "addSubtitle": subtitle = methodCall.argument("subtitle"); diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index 60f60379..bb02c394 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -368,9 +368,9 @@ class VlcPlayerController { }); } - Future getSubtitleTrackCount() async { - int cnt=await _methodChannel.invokeMethod("getSubtitleTrackCount"); - return cnt; + Future> getSubtitleTracks() async { + List list=await _methodChannel.invokeMethod("getSubtitleTracks"); + return list; } From df59304d446ada3b6c2b2bb6ee352438be0f00e3 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 10 May 2020 11:06:04 +0900 Subject: [PATCH 10/29] revert some changes that cause SetSurfaceTexture null exception --- .../fluttervlcplayer/FlutterVideoView.java | 53 ++----------------- 1 file changed, 3 insertions(+), 50 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 1bd35dca..6659b07e 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -48,13 +48,9 @@ class FlutterVideoView implements PlatformView, MethodChannel.MethodCallHandler, private MediaPlayer mediaPlayer; private VLCVideoLayout frameLayout; private TextureView textureView; - // private TextureView subtitleView; private IVLCVout vout; private boolean playerDisposed; -// private Boolean subtitleTextureValid=false; -// private Boolean videoTextureValid=false; - public FlutterVideoView(Context context, PluginRegistry.Registrar _registrar, BinaryMessenger messenger, int id) { this.playerDisposed = false; @@ -78,14 +74,9 @@ public void onCancel(Object o) { } ); -// TextureRegistry.SurfaceTextureEntry textureEntry = registrar.textures().createSurfaceTexture(); -// TextureRegistry.SurfaceTextureEntry subtitleEntry = registrar.textures().createSurfaceTexture(); + TextureRegistry.SurfaceTextureEntry textureEntry = registrar.textures().createSurfaceTexture(); createLayout(context); -// textureView.setSurfaceTexture(textureEntry.surfaceTexture()); -// subtitleView.setSurfaceTexture(subtitleEntry.surfaceTexture()); -// -//// subtitleView.setZOrderMediaOverlay(true); -//// subtitleView.getHolder().setFormat(PixelFormat.TRANSLUCENT); + textureView.setSurfaceTexture(textureEntry.surfaceTexture()); // // textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { // @@ -96,10 +87,7 @@ public void onCancel(Object o) { // if (vout == null) return; // // vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); -//// videoTextureValid=true; -//// -//// if (subtitleTextureValid && videoTextureValid) -// vout.attachViews(); +// vout.attachViews(); // textureView.forceLayout(); // if (wasPaused) { // mediaPlayer.play(); @@ -136,36 +124,6 @@ public void onCancel(Object o) { // // } // -// }); - -// subtitleView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { -// @Override -// public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) { -// if (vout == null) return; -// -// vout.setSubtitlesSurface(new Surface(subtitleView.getSurfaceTexture()), null); -// subtitleTextureValid=true; -// -// if (subtitleTextureValid && videoTextureValid) -// vout.attachViews(); -// subtitleView.forceLayout(); -// -// } -// -// @Override -// public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { -// -// } -// -// @Override -// public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { -// return false; -// } -// -// @Override -// public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { -// -// } // }); methodChannel = new MethodChannel(messenger, "flutter_video_plugin/getVideoView_" + id); @@ -175,9 +133,7 @@ public void onCancel(Object o) { private void createLayout(Context context) { frameLayout = new VLCVideoLayout(context); textureView = new TextureView(context); -// subtitleView =new TextureView(context); frameLayout.addView(textureView); -// frameLayout.addView(subtitleView); } @Override @@ -237,11 +193,8 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re vout = mediaPlayer.getVLCVout(); textureView.forceLayout(); textureView.setFitsSystemWindows(true); - //subtitleView.forceLayout(); - //textureView.setFitsSystemWindows(true); //vout.setVideoSurface(new Surface(textureView.getSurfaceTexture()), null); vout.setVideoView(textureView); - //vout.setSubtitlesView(subtitleView); vout.attachViews(); Media media = null; From ebebd32eadaa68e4d9f7fabb7e7b32676f08e576 Mon Sep 17 00:00:00 2001 From: chinshou Date: Wed, 13 May 2020 20:53:36 +0800 Subject: [PATCH 11/29] port the change from Android to IOS --- ios/Classes/FlutterVlcPlayerPlugin.m | 103 ++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index 7d120c32..bcb95f94 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -33,13 +33,27 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent if([call.method isEqualToString:@"initialize"]) { NSString *url = call.arguments[@"url"]; - - VLCMediaPlayer *player = [[VLCMediaPlayer alloc] init]; + bool isLocal = call.arguments[@"isLocal"]; + NSString *subtitle = call.arguments[@"subtitle"]; + bool loop = call.arguments[@"loop"]; + NSMutableArray *options= [[NSMutableArray alloc] init]; + if (!isLocal) + [options addObject:@"--rtsp-tcp"]; + if (loop) + [options addObject:@"--input-repeat=65535"]; + VLCMediaPlayer *player = [[VLCMediaPlayer alloc] initWithOptions:options]; player.delegate = eventChannelHandler; instance.player = player; - VLCMedia *media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + VLCMedia *media = nil; + if (isLocal) + media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + else + media = [VLCMedia mediaWithPath:url]; + //add subtitle + if ([subtitle length] > 0) + [player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; player.media = media; player.position = 0.5; player.drawable = instance.hostedView; @@ -93,11 +107,14 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent NSString *playbackState = call.arguments[@"playbackState"]; if([playbackState isEqualToString:@"play"]) { - [instance.player play]; + if (![instance.player isPlaying]) + [instance.player play]; } else if ([playbackState isEqualToString:@"pause"]) { - [instance.player pause]; + if ([instance.player isPlaying]) + [instance.player pause]; } else if ([playbackState isEqualToString:@"stop"]) { - [instance.player stop]; + if ([instance.player isPlaying]) + [instance.player stop]; } result(nil); @@ -112,7 +129,14 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent result(nil); return; - } else if ([call.method isEqualToString:@"setTime"]) { + } else if ([call.method isEqualToString:@"getPlaybackSpeed"]) { + + float rate= instance.player.rate; + + result([NSNumber numberWithDouble:rate]); + return; + + }else if ([call.method isEqualToString:@"setTime"]) { VLCTime *time = [VLCTime timeWithNumber:call.arguments[@"time"]]; instance.player.time = time; @@ -120,7 +144,46 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent result(nil); return; - } + } else if ([call.method isEqualToString:@"getTime"]) { + + int value= instance.player.time.intValue; + + result([NSNumber numberWithInt:value]); + return; + + } else if ([call.method isEqualToString:@"getDuration"]) { + + int value= instance.player.media.length.intValue; + + result([NSNumber numberWithInt:value]); + return; + + }else if ([call.method isEqualToString:@"isPlaying"]) { + + bool value= [instance.player isPlaying]; + + result([NSNumber numberWithBool:value]); + return; + + }else if ([call.method isEqualToString:@"setSubtitleTrack"]) { + + int track = call.arguments[@"track"]; + instance.player.currentVideoSubTitleIndex = track; + + return; + }else if ([call.method isEqualToString:@"getSubtitleTracks"]) { + + NSArray *videoSubTitlesNames = instance.player.videoSubTitlesNames; + + result(videoSubTitlesNames); + return; + }else if ([call.method isEqualToString:@"addSubtitle"]) { + + NSString* subtitle = call.arguments[@"subtitle"]; + [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; + + return; + } }]; @@ -230,11 +293,6 @@ - (void)mediaPlayerStateChanged:(NSNotification *)aNotification { return; case VLCMediaPlayerStatePlaying: - _eventSink(@{ - @"name": @"buffering", - @"value": @(NO) - }); - _eventSink(@{ @"name": @"playing", @"value": @(YES), @@ -262,16 +320,17 @@ - (void)mediaPlayerStateChanged:(NSNotification *)aNotification { return; case VLCMediaPlayerStatePaused: - case VLCMediaPlayerStateStopped: _eventSink(@{ - @"name": @"buffering", - @"value": @(NO) + @"name": @"paused", + @"value": @(YES) }); - + return; + case VLCMediaPlayerStateStopped: _eventSink(@{ - @"name": @"playing", - @"value": @(NO) + @"name": @"stopped", + @"value": @(YES) }); + return; } } @@ -286,6 +345,12 @@ - (void)mediaPlayerTimeChanged:(NSNotification *)aNotification { @"speed": @(player.rate), }); + _eventSink(@{ + @"name": @"position", + @"value": player.time.value, + @"speed": @(player.rate), + }); + return; } From ee64fdaf174c796f9564ed8dee8a2f7426dde42b Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Wed, 13 May 2020 22:16:11 +0900 Subject: [PATCH 12/29] start to support ios --- ios/Classes/FlutterVlcPlayerPlugin.m | 103 ++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index 7d120c32..bcb95f94 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -33,13 +33,27 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent if([call.method isEqualToString:@"initialize"]) { NSString *url = call.arguments[@"url"]; - - VLCMediaPlayer *player = [[VLCMediaPlayer alloc] init]; + bool isLocal = call.arguments[@"isLocal"]; + NSString *subtitle = call.arguments[@"subtitle"]; + bool loop = call.arguments[@"loop"]; + NSMutableArray *options= [[NSMutableArray alloc] init]; + if (!isLocal) + [options addObject:@"--rtsp-tcp"]; + if (loop) + [options addObject:@"--input-repeat=65535"]; + VLCMediaPlayer *player = [[VLCMediaPlayer alloc] initWithOptions:options]; player.delegate = eventChannelHandler; instance.player = player; - VLCMedia *media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + VLCMedia *media = nil; + if (isLocal) + media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + else + media = [VLCMedia mediaWithPath:url]; + //add subtitle + if ([subtitle length] > 0) + [player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; player.media = media; player.position = 0.5; player.drawable = instance.hostedView; @@ -93,11 +107,14 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent NSString *playbackState = call.arguments[@"playbackState"]; if([playbackState isEqualToString:@"play"]) { - [instance.player play]; + if (![instance.player isPlaying]) + [instance.player play]; } else if ([playbackState isEqualToString:@"pause"]) { - [instance.player pause]; + if ([instance.player isPlaying]) + [instance.player pause]; } else if ([playbackState isEqualToString:@"stop"]) { - [instance.player stop]; + if ([instance.player isPlaying]) + [instance.player stop]; } result(nil); @@ -112,7 +129,14 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent result(nil); return; - } else if ([call.method isEqualToString:@"setTime"]) { + } else if ([call.method isEqualToString:@"getPlaybackSpeed"]) { + + float rate= instance.player.rate; + + result([NSNumber numberWithDouble:rate]); + return; + + }else if ([call.method isEqualToString:@"setTime"]) { VLCTime *time = [VLCTime timeWithNumber:call.arguments[@"time"]]; instance.player.time = time; @@ -120,7 +144,46 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent result(nil); return; - } + } else if ([call.method isEqualToString:@"getTime"]) { + + int value= instance.player.time.intValue; + + result([NSNumber numberWithInt:value]); + return; + + } else if ([call.method isEqualToString:@"getDuration"]) { + + int value= instance.player.media.length.intValue; + + result([NSNumber numberWithInt:value]); + return; + + }else if ([call.method isEqualToString:@"isPlaying"]) { + + bool value= [instance.player isPlaying]; + + result([NSNumber numberWithBool:value]); + return; + + }else if ([call.method isEqualToString:@"setSubtitleTrack"]) { + + int track = call.arguments[@"track"]; + instance.player.currentVideoSubTitleIndex = track; + + return; + }else if ([call.method isEqualToString:@"getSubtitleTracks"]) { + + NSArray *videoSubTitlesNames = instance.player.videoSubTitlesNames; + + result(videoSubTitlesNames); + return; + }else if ([call.method isEqualToString:@"addSubtitle"]) { + + NSString* subtitle = call.arguments[@"subtitle"]; + [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; + + return; + } }]; @@ -230,11 +293,6 @@ - (void)mediaPlayerStateChanged:(NSNotification *)aNotification { return; case VLCMediaPlayerStatePlaying: - _eventSink(@{ - @"name": @"buffering", - @"value": @(NO) - }); - _eventSink(@{ @"name": @"playing", @"value": @(YES), @@ -262,16 +320,17 @@ - (void)mediaPlayerStateChanged:(NSNotification *)aNotification { return; case VLCMediaPlayerStatePaused: - case VLCMediaPlayerStateStopped: _eventSink(@{ - @"name": @"buffering", - @"value": @(NO) + @"name": @"paused", + @"value": @(YES) }); - + return; + case VLCMediaPlayerStateStopped: _eventSink(@{ - @"name": @"playing", - @"value": @(NO) + @"name": @"stopped", + @"value": @(YES) }); + return; } } @@ -286,6 +345,12 @@ - (void)mediaPlayerTimeChanged:(NSNotification *)aNotification { @"speed": @(player.rate), }); + _eventSink(@{ + @"name": @"position", + @"value": player.time.value, + @"speed": @(player.rate), + }); + return; } From 5b6566aa45c642d65c3d5f90e53eacbc298a00d5 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Thu, 14 May 2020 19:08:01 +0900 Subject: [PATCH 13/29] bug fixed --- ios/Classes/FlutterVlcPlayerPlugin.m | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index bcb95f94..ed14b7d2 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -48,9 +48,10 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent VLCMedia *media = nil; if (isLocal) - media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; - else media = [VLCMedia mediaWithPath:url]; + else + media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + //add subtitle if ([subtitle length] > 0) [player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; @@ -167,15 +168,21 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent }else if ([call.method isEqualToString:@"setSubtitleTrack"]) { - int track = call.arguments[@"track"]; + NSNumber* value=call.arguments[@"track"]; + int track = value.intValue; instance.player.currentVideoSubTitleIndex = track; return; }else if ([call.method isEqualToString:@"getSubtitleTracks"]) { - NSArray *videoSubTitlesNames = instance.player.videoSubTitlesNames; + NSArray *videoSubTitlesNames = instance.player.videoTrackIndexes; + NSMutableArray *subtitles=[NSMutableArray array]; + for (NSNumber* n in videoSubTitlesNames){ + if (n.intValue>=0) + [subtitles addObject:n]; + } - result(videoSubTitlesNames); + result(subtitles); return; }else if ([call.method isEqualToString:@"addSubtitle"]) { From 81549c5afa53bdeed378b9ddd36799b8e34def00 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Thu, 14 May 2020 19:26:57 +0900 Subject: [PATCH 14/29] update the interface --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 157dfd57..a1803691 100755 --- a/README.md +++ b/README.md @@ -112,6 +112,12 @@ const VlcPlayer({ /// This is the initial URL for the content. This also must be provided but [VlcPlayerController] implements /// [VlcPlayerController.setStreamUrl] method so this can be changed at any time. @required this.url, + /// whether the url is a local file instead of network stream url + this.isLocal=false, + /// external subtitle file + this.subtitle="", + /// True: loop the playback forever + this.loop=true, /// Before the platform view has initialized, this placeholder will be rendered instead of the video player. /// This can simply be a [CircularProgressIndicator] (see the example.) this.placeholder, @@ -140,7 +146,7 @@ VlcPlayerController({ /// Returns the current state of the player. /// Valid states: /// - PlayingState.PLAYING - /// - PlayingState.BUFFERING + /// - PlayingState.PAUSED /// - PlayingState.STOPPED /// - null (When the player is uninitialized) PlayingState playingState; @@ -190,6 +196,21 @@ VlcPlayerController({ /// Returns binary data for a snapshot of the media at the current frame. Future takeSnapshot(); + + /// Return subtitle track index list + Future> getSubtitleTracks(); + + /// [track] switch subtitle track by track index + Future setSubtitleTrack(int track); + + /// Return player speed + Future getPlaybackSpeed(); + + /// Return player current postion in milliseconds + Future getTime(); + + /// Return true if player is playing + Future isPlaying(); /// Disposes the platform view and unloads the VLC player. void dispose(); From 6c4002ddb468375106084900a967a83e8316ab98 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Fri, 15 May 2020 14:20:09 +0900 Subject: [PATCH 15/29] send duration information because sometime Playing state notification was not sent by player due to some problem I do not know. --- ios/Classes/FlutterVlcPlayerPlugin.m | 2 ++ lib/flutter_vlc_player.dart | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index ed14b7d2..76758b74 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -350,12 +350,14 @@ - (void)mediaPlayerTimeChanged:(NSNotification *)aNotification { @"name": @"timeChanged", @"value": player.time.value, @"speed": @(player.rate), + @"length": player.media.length.value ?: @0 }); _eventSink(@{ @"name": @"position", @"value": player.time.value, @"speed": @(player.rate), + @"length": player.media.length.value ?: @0 }); return; diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index bb02c394..1912ac72 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -285,6 +285,8 @@ class VlcPlayerController { case 'timeChanged': _position = event['value']; + if (event['length']!=null) + _duration = event['length']; _playbackSpeed = event['speed']; _fireEventHandlers(); break; From 8d6b229fa28a6e31bf751674292c5600b8f1a155 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Tue, 19 May 2020 23:15:43 +0900 Subject: [PATCH 16/29] fix subtitle track index bug --- ios/Classes/FlutterVlcPlayerPlugin.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index 76758b74..2d110264 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -175,9 +175,9 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent return; }else if ([call.method isEqualToString:@"getSubtitleTracks"]) { - NSArray *videoSubTitlesNames = instance.player.videoTrackIndexes; + NSArray *videoSubTitlesIndexes = instance.player.videoSubTitlesIndexes; NSMutableArray *subtitles=[NSMutableArray array]; - for (NSNumber* n in videoSubTitlesNames){ + for (NSNumber* n in videoSubTitlesIndexes){ if (n.intValue>=0) [subtitles addObject:n]; } From 521f433d01979859b24285c32a990db95cd73b80 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 24 May 2020 19:26:42 +0900 Subject: [PATCH 17/29] fix compile error --- android/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3dfedec1..f4039202 100755 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip From d5011c32d1fd7c97b14a837049647fce6aed6d7c Mon Sep 17 00:00:00 2001 From: chinshou Date: Sun, 31 May 2020 00:41:33 +0800 Subject: [PATCH 18/29] fix change url --- ios/Classes/FlutterVlcPlayerPlugin.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index 2d110264..e360e408 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -81,7 +81,17 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent [instance.player stop]; NSString *url = call.arguments[@"url"]; - VLCMedia *media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + bool isLocal = call.arguments[@"isLocal"]; + NSString *subtitle = call.arguments[@"subtitle"]; + VLCMedia *media = nil; + if (isLocal) + media = [VLCMedia mediaWithPath:url]; + else + media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + + //add subtitle + if ([subtitle length] > 0) + [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; instance.player.media = media; result(nil); From 5a4020092b8e5ae2b370ce838beeacc1d4f151a1 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 31 May 2020 01:45:00 +0900 Subject: [PATCH 19/29] fix change url on ios --- ios/Classes/FlutterVlcPlayerPlugin.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index 2d110264..e360e408 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -81,7 +81,17 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent [instance.player stop]; NSString *url = call.arguments[@"url"]; - VLCMedia *media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + bool isLocal = call.arguments[@"isLocal"]; + NSString *subtitle = call.arguments[@"subtitle"]; + VLCMedia *media = nil; + if (isLocal) + media = [VLCMedia mediaWithPath:url]; + else + media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]]; + + //add subtitle + if ([subtitle length] > 0) + [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; instance.player.media = media; result(nil); From 231af1e5e000cd14a3ef9a0b47532683b43d1c9c Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 31 May 2020 02:08:58 +0900 Subject: [PATCH 20/29] deadlock fix --- ios/Classes/FlutterVlcPlayerPlugin.m | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index e360e408..ce834b85 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -70,19 +70,20 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent } else if ([call.method isEqualToString:@"changeURL"]) { - if(instance.player == nil) { - result([FlutterError errorWithCode:@"VLC_NOT_INITIALIZED" - message:@"The player has not yet been initialized." - details:nil]); - - return; - } - - [instance.player stop]; - NSString *url = call.arguments[@"url"]; bool isLocal = call.arguments[@"isLocal"]; NSString *subtitle = call.arguments[@"subtitle"]; + bool loop = true;//call.arguments[@"loop"]; + NSMutableArray *options= [[NSMutableArray alloc] init]; + if (!isLocal) + [options addObject:@"--rtsp-tcp"]; + if (loop) + [options addObject:@"--input-repeat=65535"]; + VLCMediaPlayer *player = [[VLCMediaPlayer alloc] initWithOptions:options]; + player.delegate = eventChannelHandler; + + instance.player = player; + VLCMedia *media = nil; if (isLocal) media = [VLCMedia mediaWithPath:url]; @@ -91,8 +92,11 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent //add subtitle if ([subtitle length] > 0) - [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; - instance.player.media = media; + [player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; + player.media = media; + player.position = 0.5; + player.drawable = instance.hostedView; + [player addObserver:instance forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:nil]; result(nil); return; From 939fc5d8ea302751ae5f2778eb6689532b5df388 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 31 May 2020 08:31:26 +0900 Subject: [PATCH 21/29] revert previous change that more unstable --- ios/Classes/FlutterVlcPlayerPlugin.m | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index ce834b85..e360e408 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -70,20 +70,19 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent } else if ([call.method isEqualToString:@"changeURL"]) { + if(instance.player == nil) { + result([FlutterError errorWithCode:@"VLC_NOT_INITIALIZED" + message:@"The player has not yet been initialized." + details:nil]); + + return; + } + + [instance.player stop]; + NSString *url = call.arguments[@"url"]; bool isLocal = call.arguments[@"isLocal"]; NSString *subtitle = call.arguments[@"subtitle"]; - bool loop = true;//call.arguments[@"loop"]; - NSMutableArray *options= [[NSMutableArray alloc] init]; - if (!isLocal) - [options addObject:@"--rtsp-tcp"]; - if (loop) - [options addObject:@"--input-repeat=65535"]; - VLCMediaPlayer *player = [[VLCMediaPlayer alloc] initWithOptions:options]; - player.delegate = eventChannelHandler; - - instance.player = player; - VLCMedia *media = nil; if (isLocal) media = [VLCMedia mediaWithPath:url]; @@ -92,11 +91,8 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent //add subtitle if ([subtitle length] > 0) - [player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; - player.media = media; - player.position = 0.5; - player.drawable = instance.hostedView; - [player addObserver:instance forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:nil]; + [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; + instance.player.media = media; result(nil); return; From 81725bab00e4b898508165e5aa1a3bdfb583aa27 Mon Sep 17 00:00:00 2001 From: chinshou Date: Sun, 31 May 2020 08:52:44 +0800 Subject: [PATCH 22/29] fix bug --- ios/Classes/FlutterVlcPlayerPlugin.m | 11 ++++++++--- lib/flutter_vlc_player.dart | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index e360e408..3ff6c87c 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -78,7 +78,9 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent return; } - [instance.player stop]; + bool isplaying=[instance.player isPlaying]; + if (isplaying) + [instance.player stop]; NSString *url = call.arguments[@"url"]; bool isLocal = call.arguments[@"isLocal"]; @@ -93,6 +95,8 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent if ([subtitle length] > 0) [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; instance.player.media = media; + if (isplaying) + [instance.player play]; result(nil); return; @@ -218,7 +222,7 @@ -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS if (self.aspectSet) return; if (!self.player.isPlaying) return; - + [_player setDrawable:_hostedView]; [_player setVideoAspectRatio:"0.7"]; [_player setCurrentVideoTrackIndex:0]; @@ -302,7 +306,8 @@ - (void)mediaPlayerStateChanged:(NSNotification *)aNotification { ratio = width.floatValue / height.floatValue; } } - + + switch(player.state){ case VLCMediaPlayerStateESAdded: case VLCMediaPlayerStateBuffering: diff --git a/lib/flutter_vlc_player.dart b/lib/flutter_vlc_player.dart index 1912ac72..194a6191 100755 --- a/lib/flutter_vlc_player.dart +++ b/lib/flutter_vlc_player.dart @@ -309,9 +309,9 @@ class VlcPlayerController { _initialized = false; _fireEventHandlers(); - bool wasPlaying = _playingState != PlayingState.STOPPED; + //bool wasPlaying = _playingState != PlayingState.STOPPED; await _methodChannel.invokeMethod("changeURL", {'url': url, 'isLocal':isLocal, 'subtitle':subtitle}); - if (wasPlaying) play(); + //if (wasPlaying) play(); _initialized = true; _fireEventHandlers(); From 32c0b789495a23d67ac823a2f2de0b7f70e8b459 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 31 May 2020 10:25:11 +0900 Subject: [PATCH 23/29] also fix the android change url --- .../software/solid/fluttervlcplayer/FlutterVideoView.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 6659b07e..887f5ee3 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -217,7 +217,9 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re if (libVLC == null) result.error("VLC_NOT_INITIALIZED", "The player has not yet been initialized.", false); - mediaPlayer.stop(); + bool playing=mediaPlayer.isPlaying(); + if (playing) + mediaPlayer.stop(); String newURL = methodCall.argument("url"); isLocal = methodCall.argument("isLocal"); Media newMedia = null; @@ -227,6 +229,8 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re newMedia = new Media(libVLC, Uri.parse(Uri.decode(newURL))); newMedia.setHWDecoderEnabled(true, true); mediaPlayer.setMedia(newMedia); + if (playing) + mediaPlayer.play(); result.success(null); break; From 340fc5ddbe6583e21a99dbbb79cd09adc28d7550 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Sun, 31 May 2020 10:30:21 +0900 Subject: [PATCH 24/29] fix compile error --- .../java/software/solid/fluttervlcplayer/FlutterVideoView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 887f5ee3..5c741c61 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -160,6 +160,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re int track = -1; String subtitle = ""; Boolean loop = false; + Boolean playing = false; switch (methodCall.method) { case "initialize": if (frameLayout == null) { @@ -217,7 +218,7 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re if (libVLC == null) result.error("VLC_NOT_INITIALIZED", "The player has not yet been initialized.", false); - bool playing=mediaPlayer.isPlaying(); + playing=mediaPlayer.isPlaying(); if (playing) mediaPlayer.stop(); String newURL = methodCall.argument("url"); From ff53c837802ad0823e43786390ac1a1d25759caf Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Tue, 2 Jun 2020 19:59:11 +0900 Subject: [PATCH 25/29] do not call mediaplayer.stop when end reached --- .../java/software/solid/fluttervlcplayer/FlutterVideoView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 5c741c61..a099c506 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -359,7 +359,7 @@ public void onEvent(MediaPlayer.Event event) { eventObject.clear(); break; case MediaPlayer.Event.EndReached: - mediaPlayer.stop(); + //mediaPlayer.stop(); eventObject.put("name", "ended"); eventSink.success(eventObject); From 5b170087f4a9ece60d576ffa4fcdee7a725842cd Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Tue, 2 Jun 2020 20:33:38 +0900 Subject: [PATCH 26/29] revert changes --- .../java/software/solid/fluttervlcplayer/FlutterVideoView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index a099c506..5c741c61 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -359,7 +359,7 @@ public void onEvent(MediaPlayer.Event event) { eventObject.clear(); break; case MediaPlayer.Event.EndReached: - //mediaPlayer.stop(); + mediaPlayer.stop(); eventObject.put("name", "ended"); eventSink.success(eventObject); From 84e72e6394f1828cb071352c337c5dccce5bec63 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Tue, 2 Jun 2020 20:38:12 +0900 Subject: [PATCH 27/29] always play the stream when change url --- .../software/solid/fluttervlcplayer/FlutterVideoView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java index 5c741c61..cd347f7e 100755 --- a/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java +++ b/android/src/main/java/software/solid/fluttervlcplayer/FlutterVideoView.java @@ -230,8 +230,8 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re newMedia = new Media(libVLC, Uri.parse(Uri.decode(newURL))); newMedia.setHWDecoderEnabled(true, true); mediaPlayer.setMedia(newMedia); - if (playing) - mediaPlayer.play(); + //if (playing) + mediaPlayer.play(); result.success(null); break; From 61838ebea41c40d1f83c314bd24b18a944b0d114 Mon Sep 17 00:00:00 2001 From: SUNNET-chin-shou Date: Tue, 2 Jun 2020 20:44:09 +0900 Subject: [PATCH 28/29] do the same fix for ios --- ios/Classes/FlutterVlcPlayerPlugin.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index 3ff6c87c..fbb902f8 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -95,8 +95,8 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent if ([subtitle length] > 0) [instance.player addPlaybackSlave:[NSURL URLWithString:subtitle] type:VLCMediaPlaybackSlaveTypeSubtitle enforce:true]; instance.player.media = media; - if (isplaying) - [instance.player play]; + //if (isplaying) + [instance.player play]; result(nil); return; @@ -326,6 +326,7 @@ - (void)mediaPlayerStateChanged:(NSNotification *)aNotification { return; case VLCMediaPlayerStateEnded: + [player stop]; _eventSink(@{ @"name": @"ended" }); From c3a9000891ea89c3d97e6a84db31907dcec00d94 Mon Sep 17 00:00:00 2001 From: chinshou Date: Thu, 4 Jun 2020 23:04:42 +0800 Subject: [PATCH 29/29] fix get loop parameter --- ios/Classes/FlutterVlcPlayerPlugin.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Classes/FlutterVlcPlayerPlugin.m b/ios/Classes/FlutterVlcPlayerPlugin.m index fbb902f8..117e81d6 100755 --- a/ios/Classes/FlutterVlcPlayerPlugin.m +++ b/ios/Classes/FlutterVlcPlayerPlugin.m @@ -33,9 +33,9 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent if([call.method isEqualToString:@"initialize"]) { NSString *url = call.arguments[@"url"]; - bool isLocal = call.arguments[@"isLocal"]; + bool isLocal = [call.arguments[@"isLocal"] boolValue]; NSString *subtitle = call.arguments[@"subtitle"]; - bool loop = call.arguments[@"loop"]; + bool loop = [call.arguments[@"loop"] boolValue]; NSMutableArray *options= [[NSMutableArray alloc] init]; if (!isLocal) [options addObject:@"--rtsp-tcp"]; @@ -83,7 +83,7 @@ + (instancetype)initWithChannels: (FlutterMethodChannel*) methodChannel andEvent [instance.player stop]; NSString *url = call.arguments[@"url"]; - bool isLocal = call.arguments[@"isLocal"]; + bool isLocal = [call.arguments[@"isLocal"] boolValue]; NSString *subtitle = call.arguments[@"subtitle"]; VLCMedia *media = nil; if (isLocal) @@ -328,7 +328,7 @@ - (void)mediaPlayerStateChanged:(NSNotification *)aNotification { case VLCMediaPlayerStateEnded: [player stop]; _eventSink(@{ - @"name": @"ended" + @"name": @"ended", }); _eventSink(@{