diff --git a/.github/funding.yml b/.github/funding.yml
deleted file mode 100644
index 240f2f0f..00000000
--- a/.github/funding.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-# These are supported funding model platforms
-
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: solidsoftwarehq
-open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
\ No newline at end of file
diff --git a/.github/workflows/code_check_for_flutter_vlc_player.yaml b/.github/workflows/code_check_for_flutter_vlc_player.yaml
new file mode 100644
index 00000000..75b3c46f
--- /dev/null
+++ b/.github/workflows/code_check_for_flutter_vlc_player.yaml
@@ -0,0 +1,58 @@
+name: Library ON Push & PR DO Code check
+on: [push, pull_request]
+
+jobs:
+ code-check:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Setup flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+
+ - name: Check flutter sdk version
+ run: flutter --version
+
+ - name: Get dependencies
+ working-directory: ./flutter_vlc_player
+ run: flutter pub get
+
+ - name: Setup Dart Code Metrics
+ working-directory: ./flutter_vlc_player
+ run: dart pub get dart_code_metrics
+
+ - name: Dart Code Metrics
+ working-directory: ./flutter_vlc_player
+ run: |
+ dirs_to_analyze=""
+ if [ -d lib ]; then dirs_to_analyze+=" lib"; fi
+ if [ -d test ]; then dirs_to_analyze+=" test"; fi
+ if [ -d example ]; then dirs_to_analyze+=" example"; fi
+ if [ dirs_to_analyze != "" ]
+ then
+ dart run dart_code_metrics:metrics \
+ analyze \
+ $dirs_to_analyze \
+ --fatal-warnings \
+ --fatal-performance \
+ --fatal-style
+ dart run dart_code_metrics:metrics \
+ check-unused-files \
+ $dirs_to_analyze \
+ --fatal-unused
+ fi
+ - name: Check formatting
+ run: dart format . --set-exit-if-changed
+
+ - name: Run tests
+ run: |
+ # run tests if `test` folder exists
+ if [ -d test ]
+ then
+ flutter test -r expanded
+ else
+ echo "Tests not found."
+ fi
diff --git a/.github/workflows/code_check_for_flutter_vlc_player_platform_interface.yaml b/.github/workflows/code_check_for_flutter_vlc_player_platform_interface.yaml
new file mode 100644
index 00000000..8c213bab
--- /dev/null
+++ b/.github/workflows/code_check_for_flutter_vlc_player_platform_interface.yaml
@@ -0,0 +1,61 @@
+name: Interface ON Push & PR DO Code check
+on: [push, pull_request]
+
+jobs:
+ code-check:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Setup flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+
+ - name: Check flutter sdk version
+ working-directory: ./flutter_vlc_player_platform_interface
+ run: flutter --version
+
+ - name: Get dependencies
+ working-directory: ./flutter_vlc_player_platform_interface
+ run: flutter pub get
+
+ - name: Setup Dart Code Metrics
+ working-directory: ./flutter_vlc_player_platform_interface
+ run: dart pub get dart_code_metrics
+
+ - name: Dart Code Metrics
+ working-directory: ./flutter_vlc_player_platform_interface
+ run: |
+ dirs_to_analyze=""
+ if [ -d lib ]; then dirs_to_analyze+=" lib"; fi
+ if [ -d test ]; then dirs_to_analyze+=" test"; fi
+ if [ -d example ]; then dirs_to_analyze+=" example"; fi
+ if [ dirs_to_analyze != "" ]
+ then
+ dart run dart_code_metrics:metrics \
+ analyze \
+ $dirs_to_analyze \
+ --fatal-warnings \
+ --fatal-performance \
+ --fatal-style
+ dart run dart_code_metrics:metrics \
+ check-unused-files \
+ $dirs_to_analyze \
+ --fatal-unused
+ fi
+ - name: Check formatting
+ working-directory: ./flutter_vlc_player_platform_interface
+ run: dart format . --set-exit-if-changed
+
+ - name: Run tests
+ working-directory: ./flutter_vlc_player_platform_interface
+ run: |
+ # run tests if `test` folder exists
+ if [ -d test ]
+ then
+ flutter test -r expanded
+ else
+ echo "Tests not found."
+ fi
diff --git a/README.md b/README.md
index 2badc4d1..2d59680f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# Flutter VLC Player Plugin
[](https://discord.gg/mNY4fjVk)
[](https://patreon.com/solidsoftwarehq)
+[](https://nokycucwgzweensacwfy.supabase.co/functions/v1/get_project_url?projectId=148)
A VLC-powered alternative to Flutter's video_player that supports iOS and Android.
@@ -90,7 +91,6 @@ android {
buildTypes {
release {
minifyEnabled true
- useProguard true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
diff --git a/flutter_vlc_player/CHANGELOG.md b/flutter_vlc_player/CHANGELOG.md
index 610c2575..55b89de4 100644
--- a/flutter_vlc_player/CHANGELOG.md
+++ b/flutter_vlc_player/CHANGELOG.md
@@ -1,3 +1,47 @@
+## 7.4.2
+* fixed getVolume #486
+ Credits to pinpong (https://github.com/pinpong)
+* updated MobileVLCKit & libvlc
+ Credits to pinpong (https://github.com/pinpong)
+* Fix: Unable to replay when status is stopped #449
+ Credits to Virczz (https://github.com/Virczz)
+
+## 7.4.1
+* Add support for Flutter 3.16
+ Credits to thearaks (https://github.com/thearaks)
+
+## 7.4.0
+* Important change: Removed [AutomaticKeepAliveClientMixin](https://api.flutter.dev/flutter/widgets/AutomaticKeepAliveClientMixin-mixin.html) from plugin widget
+* Mobile VLC update to 3.6.0-eap9
+* Allow background playback
+ Credits to Oliver Nitzschke (https://github.com/pinpong)
+* fix instructions for proguard
+ Credits to Luiz Fernando Baldo Marques (https://github.com/luizbaldo)
+
+## 7.3.1
+* Restore Flutter 3.3-3.7 compatibility
+ Credits to Yang Fang (https://github.com/yangsfang)
+
+## 7.3.0
+* Fix http-user-agent & reuse options on iOS
+ Credits to Afriza N. Arief (https://github.com/afriza)
+* Update to Dart 3 and Flutter 3.13
+ Credits to romain.gyh (https://github.com/romaingyh)
+
+## 7.2.0
+* Update to latest VLCKit sdks
+Credits to Mitch Ross (https://github.com/mitchross)
+
+## 7.1.5
+* Fix plugin destructor (https://github.com/solid-software/flutter_vlc_player/issues/237)
+
+## 7.1.4
+* Interim release to fix Flutter 3 issues
+
+## 7.1.3
+* Added support for multi-window mode in Android.
+Credits to Andy Chentsov (https://github.com/andyduke).
+
## 7.1.2
* Add Hybrid composition support for Android.
diff --git a/flutter_vlc_player/README.md b/flutter_vlc_player/README.md
index 86c442d6..7133947c 100644
--- a/flutter_vlc_player/README.md
+++ b/flutter_vlc_player/README.md
@@ -1,4 +1,8 @@
# VLC Player Plugin
+[](https://pub.dev/packages/solid_lints)
+[](https://nokycucwgzweensacwfy.supabase.co/functions/v1/get_project_url?projectId=148)
+
+
A VLC-powered alternative to Flutter's video_player that supports iOS and Android.
@@ -87,7 +91,6 @@ android {
buildTypes {
release {
minifyEnabled true
- useProguard true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
@@ -100,6 +103,25 @@ android {
```proguard
-keep class org.videolan.libvlc.** { *; }
```
+
+
+#### Android multi-window support
+
+To enable multi-window support in your Android application, you need to make changes to `AndroidManifest.xml`, add the `android:resizeableActivity` key for the main activity, as well as the `android.allow_multiple_resumed_activities` metadata for application:
+```xml
+
+
+
+ ...
+
+ ...
+
+
+
+```
diff --git a/flutter_vlc_player/analysis_options.yaml b/flutter_vlc_player/analysis_options.yaml
index a3be6b82..803a5f78 100644
--- a/flutter_vlc_player/analysis_options.yaml
+++ b/flutter_vlc_player/analysis_options.yaml
@@ -1 +1,12 @@
-include: package:flutter_lints/flutter.yaml
\ No newline at end of file
+include: package:solid_lints/analysis_options.yaml
+
+dart_code_metrics:
+ metrics:
+ cyclomatic-complexity: 30
+
+linter:
+ rules:
+ lines_longer_than_80_chars: false
+ comment_references: false
+ public_member_api_docs: false
+ avoid_positional_boolean_parameters: false
diff --git a/flutter_vlc_player/android/build.gradle b/flutter_vlc_player/android/build.gradle
index 1a546ce1..70b9b7cc 100644
--- a/flutter_vlc_player/android/build.gradle
+++ b/flutter_vlc_player/android/build.gradle
@@ -4,28 +4,30 @@ version '1.0-SNAPSHOT'
buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.1.3'
+ classpath 'com.android.tools.build:gradle:7.4.2'
}
}
rootProject.allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
apply plugin: 'com.android.library'
android {
- compileSdkVersion 31
+ namespace 'software.solid.fluttervlcplayer'
+
+ compileSdk 34
defaultConfig {
- minSdkVersion 20
+ minSdkVersion 19
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
@@ -39,7 +41,7 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
- implementation 'org.videolan.android:libvlc-all:3.5.0-eap6'
+ implementation 'org.videolan.android:libvlc-all:3.6.0-eap12'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.annotation:annotation:1.2.0'
diff --git a/flutter_vlc_player/android/gradle/wrapper/gradle-wrapper.properties b/flutter_vlc_player/android/gradle/wrapper/gradle-wrapper.properties
index 13ab0660..79dd5b14 100644
--- a/flutter_vlc_player/android/gradle/wrapper/gradle-wrapper.properties
+++ b/flutter_vlc_player/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-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
diff --git a/flutter_vlc_player/android/src/main/AndroidManifest.xml b/flutter_vlc_player/android/src/main/AndroidManifest.xml
index a3fe31b3..a2f47b60 100644
--- a/flutter_vlc_player/android/src/main/AndroidManifest.xml
+++ b/flutter_vlc_player/android/src/main/AndroidManifest.xml
@@ -1,3 +1,2 @@
-
+
diff --git a/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayer.java b/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayer.java
index c2f69268..c48e0cad 100644
--- a/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayer.java
+++ b/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayer.java
@@ -6,18 +6,16 @@
import org.videolan.libvlc.RendererDiscoverer;
import org.videolan.libvlc.RendererItem;
import org.videolan.libvlc.interfaces.IMedia;
+import org.videolan.libvlc.interfaces.IVLCVout;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Base64;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;
-import android.view.TextureView;
import android.view.View;
import io.flutter.plugin.common.BinaryMessenger;
@@ -39,7 +37,7 @@ final class FlutterVlcPlayer implements PlatformView {
private final boolean debug = false;
//
private final Context context;
- private final TextureView textureView;
+ private final VLCTextureView textureView;
private final TextureRegistry.SurfaceTextureEntry textureEntry;
//
private final QueuingEventSink mediaEventSink = new QueuingEventSink();
@@ -66,11 +64,13 @@ public void dispose() {
if (isDisposed)
return;
//
+ textureView.dispose();
textureEntry.release();
mediaEventChannel.setStreamHandler(null);
rendererEventChannel.setStreamHandler(null);
if (mediaPlayer != null) {
mediaPlayer.stop();
+ mediaPlayer.setEventListener(null);
mediaPlayer.getVLCVout().detachViews();
mediaPlayer.release();
mediaPlayer = null;
@@ -115,7 +115,7 @@ public void onCancel(Object o) {
});
//
textureEntry = textureRegistry.createSurfaceTexture();
- textureView = new TextureView(context);
+ textureView = new VLCTextureView(context);
textureView.setSurfaceTexture(textureEntry.surfaceTexture());
textureView.forceLayout();
textureView.setFitsSystemWindows(true);
@@ -126,7 +126,7 @@ public void onCancel(Object o) {
// }
public void initialize(List options) {
- this.options = options;
+ this.options = options;
libVLC = new LibVLC(context, options);
mediaPlayer = new MediaPlayer(libVLC);
setupVlcMediaPlayer();
@@ -134,81 +134,11 @@ public void initialize(List options) {
private void setupVlcMediaPlayer() {
- // method 1
- textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
-
- boolean wasPlaying = false;
-
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- log("onSurfaceTextureAvailable");
-
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- if (mediaPlayer == null)
- return;
- mediaPlayer.getVLCVout().setWindowSize(width, height);
- mediaPlayer.getVLCVout().setVideoSurface(surface);
- if (!mediaPlayer.getVLCVout().areViewsAttached())
- mediaPlayer.getVLCVout().attachViews();
- mediaPlayer.setVideoTrackEnabled(true);
- if (wasPlaying)
- mediaPlayer.play();
- wasPlaying = false;
- }, 100L);
-
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- if (mediaPlayer != null)
- mediaPlayer.getVLCVout().setWindowSize(width, height);
- }
-
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- log("onSurfaceTextureDestroyed");
-
- if (mediaPlayer != null) {
- wasPlaying = mediaPlayer.isPlaying();
- mediaPlayer.pause();
- mediaPlayer.setVideoTrackEnabled(false);
- mediaPlayer.getVLCVout().detachViews();
- }
- return false; //do not return true if you reuse it.
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- }
-
- });
-
-// method 2
- textureView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
- log("onLayoutChange");
- //
- if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
- mediaPlayer.pause();
- mediaPlayer.setVideoTrackEnabled(false);
- mediaPlayer.getVLCVout().detachViews();
- mediaPlayer.getVLCVout().setWindowSize(view.getWidth(), view.getHeight());
- mediaPlayer.getVLCVout().setVideoView((TextureView) view);
- mediaPlayer.getVLCVout().attachViews();
- mediaPlayer.setVideoTrackEnabled(true);
- // hacky way to prevent video pixeling, it might be larger than buffer size
- long tmpTime = mediaPlayer.getTime() - 500;
- if (tmpTime > 0)
- mediaPlayer.setTime(tmpTime);
- mediaPlayer.play();
- }
- }
- });
//
mediaPlayer.getVLCVout().setWindowSize(textureView.getWidth(), textureView.getHeight());
mediaPlayer.getVLCVout().setVideoSurface(textureView.getSurfaceTexture());
- mediaPlayer.getVLCVout().attachViews();
+ textureView.setTextureEntry(textureEntry);
+ textureView.setMediaPlayer(mediaPlayer);
mediaPlayer.setVideoTrackEnabled(true);
//
mediaPlayer.setEventListener(
@@ -313,28 +243,40 @@ public void onEvent(MediaPlayer.Event event) {
}
void play() {
- mediaPlayer.play();
+ if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
+ mediaPlayer.play();
+ }
}
void pause() {
- mediaPlayer.pause();
+ if (mediaPlayer != null && mediaPlayer.isPlaying()) {
+ mediaPlayer.pause();
+ }
}
void stop() {
- mediaPlayer.stop();
+ if (mediaPlayer != null) {
+ mediaPlayer.stop();
+ }
}
boolean isPlaying() {
+ if (mediaPlayer == null) return false;
return mediaPlayer.isPlaying();
}
boolean isSeekable() {
+ if (mediaPlayer == null) return false;
return mediaPlayer.isSeekable();
}
void setStreamUrl(String url, boolean isAssetUrl, boolean autoPlay, long hwAcc) {
+ if (mediaPlayer == null) return;
+
try {
- mediaPlayer.stop();
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ }
//
Media media;
if (isAssetUrl)
@@ -356,15 +298,14 @@ void setStreamUrl(String url, boolean isAssetUrl, boolean autoPlay, long hwAcc)
media.addOption(":no-omxil-dr");
}
if (options != null) {
- for (String option: options)
+ for (String option : options)
media.addOption(option);
}
mediaPlayer.setMedia(media);
media.release();
//
- mediaPlayer.play();
- if (!autoPlay) {
- mediaPlayer.stop();
+ if (autoPlay) {
+ mediaPlayer.play();
}
} catch (IOException e) {
log(e.getMessage());
@@ -379,23 +320,33 @@ void setLooping(boolean value) {
}
void setVolume(long value) {
+ if (mediaPlayer == null) return;
+
long bracketedValue = Math.max(0, Math.min(100, value));
mediaPlayer.setVolume((int) bracketedValue);
}
int getVolume() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getVolume();
}
void setPlaybackSpeed(double value) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setRate((float) value);
}
float getPlaybackSpeed() {
+ if (mediaPlayer == null) return -1.0f;
+
return mediaPlayer.getRate();
}
void seekTo(int location) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setTime(location);
}
@@ -404,18 +355,26 @@ void setPosition(float position) {
}
long getPosition() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getTime();
}
long getDuration() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getLength();
}
int getSpuTracksCount() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getSpuTracksCount();
}
HashMap getSpuTracks() {
+ if (mediaPlayer == null) return new HashMap();
+
MediaPlayer.TrackDescription[] spuTracks = mediaPlayer.getSpuTracks();
HashMap subtitles = new HashMap<>();
if (spuTracks != null)
@@ -427,30 +386,44 @@ HashMap getSpuTracks() {
}
void setSpuTrack(int index) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setSpuTrack(index);
}
int getSpuTrack() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getSpuTrack();
}
void setSpuDelay(long delay) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setSpuDelay(delay);
}
long getSpuDelay() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getSpuDelay();
}
void addSubtitleTrack(String url, boolean isSelected) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.addSlave(Media.Slave.Type.Subtitle, Uri.parse(url), isSelected);
}
int getAudioTracksCount() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getAudioTracksCount();
}
HashMap getAudioTracks() {
+ if (mediaPlayer == null) return new HashMap();
+
MediaPlayer.TrackDescription[] audioTracks = mediaPlayer.getAudioTracks();
HashMap audios = new HashMap<>();
if (audioTracks != null)
@@ -462,30 +435,44 @@ HashMap getAudioTracks() {
}
void setAudioTrack(int index) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setAudioTrack(index);
}
int getAudioTrack() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getAudioTrack();
}
void setAudioDelay(long delay) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setAudioDelay(delay);
}
long getAudioDelay() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getAudioDelay();
}
void addAudioTrack(String url, boolean isSelected) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.addSlave(Media.Slave.Type.Audio, Uri.parse(url), isSelected);
}
int getVideoTracksCount() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getVideoTracksCount();
}
HashMap getVideoTracks() {
+ if (mediaPlayer == null) return new HashMap();
+
MediaPlayer.TrackDescription[] videoTracks = mediaPlayer.getVideoTracks();
HashMap videos = new HashMap<>();
if (videoTracks != null)
@@ -497,30 +484,43 @@ HashMap getVideoTracks() {
}
void setVideoTrack(int index) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setVideoTrack(index);
}
int getVideoTrack() {
+ if (mediaPlayer == null) return -1;
+
return mediaPlayer.getVideoTrack();
}
void setVideoScale(float scale) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setScale(scale);
}
float getVideoScale() {
+ if (mediaPlayer == null) return -1.0f;
+
return mediaPlayer.getScale();
}
void setVideoAspectRatio(String aspectRatio) {
+ if (mediaPlayer == null) return;
+
mediaPlayer.setAspectRatio(aspectRatio);
}
String getVideoAspectRatio() {
+ if (mediaPlayer == null) return "";
+
return mediaPlayer.getAspectRatio();
}
void startRendererScanning(String rendererService) {
+ if (libVLC == null) return;
//
// android -> chromecast -> "microdns"
@@ -572,6 +572,8 @@ public void onEvent(RendererDiscoverer.Event event) {
}
void stopRendererScanning() {
+ if (mediaPlayer == null) return;
+
if (isDisposed)
return;
//
@@ -591,6 +593,8 @@ void stopRendererScanning() {
}
ArrayList getAvailableRendererServices() {
+ if (libVLC == null) return new ArrayList();
+
RendererDiscoverer.Description[] renderers = RendererDiscoverer.list(libVLC);
ArrayList availableRendererServices = new ArrayList<>();
for (RendererDiscoverer.Description renderer : renderers) {
@@ -609,11 +613,12 @@ HashMap getRendererDevices() {
}
void castToRenderer(String rendererDevice) {
+ if (mediaPlayer == null) return;
+
if (isDisposed) {
return;
}
- boolean isPlaying = mediaPlayer.isPlaying();
- if (isPlaying)
+ if (mediaPlayer.isPlaying())
mediaPlayer.pause();
// if you set it to null, it will start to render normally (i.e. locally) again
@@ -631,6 +636,8 @@ void castToRenderer(String rendererDevice) {
}
String getSnapshot() {
+ if (textureView == null) return "";
+
Bitmap bitmap = textureView.getBitmap();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
@@ -642,6 +649,7 @@ Boolean startRecording(String directory) {
}
Boolean stopRecording() {
+ if (mediaPlayer == null) return true;
return mediaPlayer.record(null);
}
diff --git a/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayerPlugin.java b/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayerPlugin.java
index e2f7e944..38cbf87c 100644
--- a/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayerPlugin.java
+++ b/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/FlutterVlcPlayerPlugin.java
@@ -48,16 +48,8 @@ public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registra
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
flutterPluginBinding = binding;
- }
- @Override
- public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
- flutterPluginBinding = null;
- }
-
- @RequiresApi(api = Build.VERSION_CODES.N)
- @Override
- public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
+ //
if (flutterVlcPlayerFactory == null) {
final FlutterInjector injector = FlutterInjector.instance();
//
@@ -79,20 +71,30 @@ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
startListening();
}
+ @Override
+ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+ stopListening();
+ //
+
+ flutterPluginBinding = null;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
+ }
+
@Override
public void onDetachedFromActivityForConfigChanges() {
- onDetachedFromActivity();
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
- onAttachedToActivity(binding);
}
@Override
public void onDetachedFromActivity() {
- stopListening();
}
// extra methods
@@ -103,7 +105,9 @@ private static void startListening() {
}
private static void stopListening() {
- if (flutterVlcPlayerFactory != null)
+ if (flutterVlcPlayerFactory != null) {
flutterVlcPlayerFactory.stopListening();
+ flutterVlcPlayerFactory = null;
+ }
}
}
diff --git a/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/VLCTextureView.java b/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/VLCTextureView.java
new file mode 100644
index 00000000..520aa011
--- /dev/null
+++ b/flutter_vlc_player/android/src/main/java/software/solid/fluttervlcplayer/VLCTextureView.java
@@ -0,0 +1,213 @@
+package software.solid.fluttervlcplayer;
+
+import org.videolan.libvlc.MediaPlayer;
+import org.videolan.libvlc.interfaces.IVLCVout;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.view.TextureView;
+import android.view.View;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+
+import io.flutter.view.TextureRegistry;
+
+public class VLCTextureView extends TextureView implements TextureView.SurfaceTextureListener, View.OnLayoutChangeListener, IVLCVout.OnNewVideoLayoutListener {
+
+ private MediaPlayer mMediaPlayer = null;
+ private TextureRegistry.SurfaceTextureEntry mTextureEntry = null;
+ protected Context mContext;
+ private SurfaceTexture mSurfaceTexture = null;
+ private boolean wasPlaying = false;
+
+ private Handler mHandler;
+ private Runnable mLayoutChangeRunnable = null;
+
+ public VLCTextureView(final Context context) {
+ super(context);
+ mContext = context;
+ initVideoView();
+ }
+
+ public VLCTextureView(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ initVideoView();
+ }
+
+ public VLCTextureView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mContext = context;
+ initVideoView();
+ }
+
+ public void dispose() {
+ setSurfaceTextureListener(null);
+ removeOnLayoutChangeListener(this);
+
+ if (mLayoutChangeRunnable != null) {
+ mHandler.removeCallbacks(mLayoutChangeRunnable);
+ mLayoutChangeRunnable = null;
+ }
+
+ if (mSurfaceTexture != null) {
+ if (!mSurfaceTexture.isReleased()) {
+ mSurfaceTexture.release();
+ }
+ mSurfaceTexture = null;
+ }
+ mTextureEntry = null;
+ mMediaPlayer = null;
+ mContext = null;
+ }
+
+ private void initVideoView() {
+ mHandler = new Handler(Looper.getMainLooper());
+
+ setFocusable(false);
+ setSurfaceTextureListener(this);
+ addOnLayoutChangeListener(this);
+ }
+
+ public void setMediaPlayer(MediaPlayer mediaPlayer) {
+ if (mediaPlayer == null) {
+ mMediaPlayer.getVLCVout().detachViews();
+ }
+
+ mMediaPlayer = mediaPlayer;
+
+ if (mMediaPlayer != null) {
+ mMediaPlayer.getVLCVout().attachViews(this);
+ }
+ }
+
+ public void setTextureEntry(TextureRegistry.SurfaceTextureEntry textureEntry) {
+ this.mTextureEntry = textureEntry;
+ this.updateSurfaceTexture();
+ }
+
+ private void updateSurfaceTexture() {
+ if (this.mTextureEntry != null) {
+ final SurfaceTexture texture = this.mTextureEntry.surfaceTexture();
+ if (!texture.isReleased() && (getSurfaceTexture() != texture)) {
+ setSurfaceTexture(texture);
+ }
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ if (mSurfaceTexture == null || mSurfaceTexture.isReleased()) {
+ mSurfaceTexture = surface;
+
+ if (mMediaPlayer != null) {
+ mMediaPlayer.getVLCVout().setWindowSize(width, height);
+ if (!mMediaPlayer.getVLCVout().areViewsAttached()) {
+ mMediaPlayer.getVLCVout().setVideoSurface(mSurfaceTexture);
+ if (!mMediaPlayer.getVLCVout().areViewsAttached()) {
+ mMediaPlayer.getVLCVout().attachViews(this);
+ }
+ mMediaPlayer.setVideoTrackEnabled(true);
+ if (wasPlaying) {
+ mMediaPlayer.play();
+ }
+ }
+ }
+
+ wasPlaying = false;
+
+ } else {
+ if (getSurfaceTexture() != mSurfaceTexture) {
+ setSurfaceTexture(mSurfaceTexture);
+ }
+ }
+
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ setSize(width, height);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ if (mMediaPlayer != null) {
+ wasPlaying = mMediaPlayer.isPlaying();
+ }
+
+ if (mSurfaceTexture != surface) {
+ if (mSurfaceTexture != null) {
+ if (!mSurfaceTexture.isReleased()) {
+ mSurfaceTexture.release();
+ }
+ }
+ mSurfaceTexture = surface;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+
+ }
+
+ @Override
+ public void onNewVideoLayout(IVLCVout vlcVout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) {
+ if (width * height == 0) return;
+
+ setSize(width, height);
+ }
+
+ @Override
+ public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
+ updateLayoutSize(view);
+ }
+ }
+
+ public void updateLayoutSize(View view) {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.getVLCVout().setWindowSize(view.getWidth(), view.getHeight());
+ updateSurfaceTexture();
+ }
+ }
+
+ private void setSize(int width, int height) {
+ int mVideoWidth = 0;
+ int mVideoHeight = 0;
+ mVideoWidth = width;
+ mVideoHeight = height;
+ if (mVideoWidth * mVideoHeight <= 1) return;
+
+ // Screen size
+ int w = this.getWidth();
+ int h = this.getHeight();
+
+ // Size
+ if (w > h && w < h) {
+ int i = w;
+ w = h;
+ h = i;
+ }
+
+ float videoAR = (float) mVideoWidth / (float) mVideoHeight;
+ float screenAR = (float) w / (float) h;
+
+ if (screenAR < videoAR) {
+ h = (int) (w / videoAR);
+ } else {
+ w = (int) (h * videoAR);
+ }
+
+ // Layout fit
+ ViewGroup.LayoutParams lp = this.getLayoutParams();
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ lp.height = h;
+ this.setLayoutParams(lp);
+ this.invalidate();
+ }
+
+}
\ No newline at end of file
diff --git a/flutter_vlc_player/example/android/app/build.gradle b/flutter_vlc_player/example/android/app/build.gradle
index f862e07a..eafb2c4e 100644
--- a/flutter_vlc_player/example/android/app/build.gradle
+++ b/flutter_vlc_player/example/android/app/build.gradle
@@ -25,7 +25,9 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
- compileSdkVersion 31
+ namespace 'software.solid.fluttervlcplayerexample'
+
+ compileSdkVersion 34
lintOptions {
disable 'InvalidPackage'
@@ -33,8 +35,8 @@ android {
defaultConfig {
applicationId "software.solid.fluttervlcplayerexample"
- minSdkVersion 17
- targetSdkVersion 30
+ minSdkVersion 23
+ targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -44,6 +46,8 @@ android {
release {
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
+ minifyEnabled false
+ shrinkResources false
}
}
}
@@ -53,7 +57,7 @@ flutter {
}
dependencies {
- testImplementation 'junit:junit:4.12'
+ testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
diff --git a/flutter_vlc_player/example/android/app/src/main/AndroidManifest.xml b/flutter_vlc_player/example/android/app/src/main/AndroidManifest.xml
index e8ae7d35..62cd9ea4 100644
--- a/flutter_vlc_player/example/android/app/src/main/AndroidManifest.xml
+++ b/flutter_vlc_player/example/android/app/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
+