diff --git a/android/build.gradle b/android/build.gradle
index 0fea6ef..de0c139 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -2,10 +2,10 @@ group 'net.ossrs.flutter_live'
version '1.0-SNAPSHOT'
buildscript {
- ext.kotlin_version = '1.3.50'
+ ext.kotlin_version = '1.7.10'
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
@@ -17,7 +17,7 @@ buildscript {
rootProject.allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
@@ -25,7 +25,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 29
+ compileSdkVersion 34
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..7454180
Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 01a286e..1c5f490 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
+#Fri May 10 08:31:03 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
diff --git a/android/gradlew b/android/gradlew
new file mode 100755
index 0000000..1b6c787
--- /dev/null
+++ b/android/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/android/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index 20ef635..20d793e 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -1,3 +1,9 @@
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+}
+
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
}
}
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@@ -21,12 +22,8 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
android {
- compileSdkVersion 29
+ compileSdkVersion 34
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@@ -40,7 +37,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "net.ossrs.flutter_live_example"
minSdkVersion 21
- targetSdkVersion 29
+ targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
@@ -63,5 +60,5 @@ flutter {
}
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index 886b12a..a4c64c5 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -6,11 +6,12 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
+
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 3100ad2..1e73c4f 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,20 +1,8 @@
-buildscript {
- ext.kotlin_version = '1.3.50'
- repositories {
- google()
- jcenter()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:3.5.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
@@ -26,6 +14,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
delete rootProject.buildDir
}
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index 296b146..cfe88f6 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/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.6.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index 44e62bc..127a17e 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -1,11 +1,35 @@
-include ':app'
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.7.10" apply false
+}
+
+include ':app'
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+//def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+//def properties = new Properties()
+//
+//assert localPropertiesFile.exists()
+//localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+//
+//def flutterSdkPath = properties.getProperty("flutter.sdk")
+//assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+//apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..8c6e561 100644
--- a/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 9.0
+ 12.0
diff --git a/example/ios/Podfile b/example/ios/Podfile
index 9411102..10f3c9b 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-platform :ios, '10.0'
+platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 9af9c1f..37cd782 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -1,54 +1,42 @@
PODS:
- - BIJKPlayer (0.7.15)
- - camera_with_rtmp (0.0.1):
- - Flutter
- - HaishinKit (~> 1.0.10)
- - fijkplayer (0.8.8):
- - BIJKPlayer (~> 0.7.10)
+ - BIJKPlayer (0.7.16)
+ - fijkplayer (0.11.0):
+ - BIJKPlayer (~> 0.7.16)
- Flutter
- Flutter (1.0.0)
- flutter_live (0.0.1):
- Flutter
- - flutter_webrtc (0.2.2):
+ - flutter_webrtc (0.9.36):
- Flutter
- - GoogleWebRTC (= 1.1.31999)
- - Libyuv (= 1703)
- - GoogleWebRTC (1.1.31999)
- - HaishinKit (1.0.13):
- - Logboard (~> 2.1.3)
- - Libyuv (1703)
- - Logboard (2.1.3)
+ - WebRTC-SDK (= 114.5735.10)
- package_info (0.0.1):
- Flutter
- - path_provider (0.0.1):
+ - path_provider_foundation (0.0.1):
- Flutter
- - shared_preferences (0.0.1):
+ - FlutterMacOS
+ - shared_preferences_foundation (0.0.1):
- Flutter
- - url_launcher (0.0.1):
+ - FlutterMacOS
+ - url_launcher_ios (0.0.1):
- Flutter
+ - WebRTC-SDK (114.5735.10)
DEPENDENCIES:
- - camera_with_rtmp (from `.symlinks/plugins/camera_with_rtmp/ios`)
- fijkplayer (from `.symlinks/plugins/fijkplayer/ios`)
- Flutter (from `Flutter`)
- flutter_live (from `.symlinks/plugins/flutter_live/ios`)
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`)
- - path_provider (from `.symlinks/plugins/path_provider/ios`)
- - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
+ - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
+ - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- BIJKPlayer
- - GoogleWebRTC
- - HaishinKit
- - Libyuv
- - Logboard
+ - WebRTC-SDK
EXTERNAL SOURCES:
- camera_with_rtmp:
- :path: ".symlinks/plugins/camera_with_rtmp/ios"
fijkplayer:
:path: ".symlinks/plugins/fijkplayer/ios"
Flutter:
@@ -59,29 +47,25 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_webrtc/ios"
package_info:
:path: ".symlinks/plugins/package_info/ios"
- path_provider:
- :path: ".symlinks/plugins/path_provider/ios"
- shared_preferences:
- :path: ".symlinks/plugins/shared_preferences/ios"
- url_launcher:
- :path: ".symlinks/plugins/url_launcher/ios"
+ path_provider_foundation:
+ :path: ".symlinks/plugins/path_provider_foundation/darwin"
+ shared_preferences_foundation:
+ :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+ url_launcher_ios:
+ :path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
- BIJKPlayer: 37fd8033360eedfb1afc3658e14fbf5a25ace540
- camera_with_rtmp: 606d566f89a21f1fed15ffda99cb247304beccd4
- fijkplayer: 0d3793a2822d030ef5bba77f904bff1f7a91a115
- Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
+ BIJKPlayer: 4c5d66e5cb99ae5bade6f22a4fcc031722a81c64
+ fijkplayer: 8b6d864948dc28e6e07248ed4fce61a7467db4df
+ Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_live: efecc5d284c5a3721d436eebd5ea4322eef31a08
- flutter_webrtc: 39898454258b54ba51996850d5da8d5d53bf1524
- GoogleWebRTC: b39a78c4f5cc6b0323415b9233db03a2faa7b0f0
- HaishinKit: d1786896ace24065d466bde02356c5352ac001b0
- Libyuv: 5f79ced0ee66e60a612ca97de1e6ccacd187a437
- Logboard: e74ec2d9941abd22f4db3a3ddcd0735011df92c7
+ flutter_webrtc: b33475c3a57d59ff05bf87b4f5d3feceac63f291
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
- path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
- shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
- url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
+ path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
+ url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586
+ WebRTC-SDK: 8c0edd05b880a39648118192c252667ea06dea51
-PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea
+PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
-COCOAPODS: 1.10.2
+COCOAPODS: 1.15.2
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index 5a41477..26b8bbb 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 51;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -155,7 +155,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1020;
+ LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -199,10 +199,12 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@@ -230,6 +232,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -339,7 +342,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -355,6 +358,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 4X75RZ9C85;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -369,7 +373,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- PRODUCT_BUNDLE_IDENTIFIER = net.ossrs.flutterLive;
+ PRODUCT_BUNDLE_IDENTIFIER = net.ossrs.flutterLive2;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -424,7 +428,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -473,7 +477,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -491,6 +495,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 4X75RZ9C85;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -505,7 +510,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- PRODUCT_BUNDLE_IDENTIFIER = net.ossrs.flutterLive;
+ PRODUCT_BUNDLE_IDENTIFIER = net.ossrs.flutterLive2;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -521,6 +526,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 4X75RZ9C85;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -535,7 +541,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- PRODUCT_BUNDLE_IDENTIFIER = net.ossrs.flutterLive;
+ PRODUCT_BUNDLE_IDENTIFIER = net.ossrs.flutterLive2;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index a28140c..5e31d3d 100644
--- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
-
-
-
-
+
+
-
-
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
@@ -22,6 +24,12 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ NSCameraUsageDescription
+ $(PRODUCT_NAME) Camera Usage!
+ NSMicrophoneUsageDescription
+ $(PRODUCT_NAME) Microphone Usage!
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,9 +49,5 @@
UIViewControllerBasedStatusBarAppearance
- NSCameraUsageDescription
- $(PRODUCT_NAME) Camera Usage!
- NSMicrophoneUsageDescription
- $(PRODUCT_NAME) Microphone Usage!
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 1784065..7273f2b 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -6,14 +6,14 @@ import 'dart:io' show Platform;
import 'package:flutter_live/flutter_live.dart' as flutter_live;
import 'package:flutter_webrtc/flutter_webrtc.dart' as webrtc;
import 'package:fijkplayer/fijkplayer.dart' as fijkplayer;
-import 'package:camera_with_rtmp/camera.dart' as camera;
+// import 'package:camera_with_rtmp/camera.dart' as camera;
import 'package:shared_preferences/shared_preferences.dart';
import 'privacy.dart';
void main() {
runApp(MaterialApp(
- debugShowCheckedModeBanner: false,
- home: Platform.isAndroid? Privacy() : Home(),
+ debugShowCheckedModeBanner: false,
+ home: Platform.isAndroid ? Privacy() : Home(),
));
}
@@ -24,17 +24,18 @@ class Home extends StatefulWidget {
class _HomeState extends State {
// Platform information.
- PackageInfo _info = PackageInfo(version: '0.0.0', buildNumber: '0');
+ PackageInfo _info = PackageInfo(
+ appName: '', packageName: '', version: '0.0.0', buildNumber: '0');
// The url to play or publish.
- String _url; // The final url, should equals to controller.text
+ String _url = ''; // The final url, should equals to controller.text
final TextEditingController _urlController = TextEditingController();
// For publisher.
bool _isPublish = false;
bool _isPublishing = false;
// The controller for publisher.
- camera.CameraController _cameraController = null;
+ // camera.CameraController? _cameraController = null;
@override
Widget build(BuildContext context) {
@@ -47,8 +48,9 @@ class _HomeState extends State {
body: Container(
child: ListView(children: [
UrlInputDisplay(_urlController),
- ControlDisplay(isUrlValid(), _onStartPlayOrPublish, _isPublish, _isPublishing, _onSwitchPublish),
- CameraDisplay(_isPublish, _cameraController),
+ ControlDisplay(isUrlValid(), _onStartPlayOrPublish, _isPublish,
+ _isPublishing, _onSwitchPublish),
+ // CameraDisplay(_isPublish, _cameraController!),
DemoUrlsDisplay(_url, _onUserSelectUrl, _isPublish),
PlatformDisplay(_info),
]),
@@ -64,7 +66,9 @@ class _HomeState extends State {
_urlController.addListener(_onUserEditUrl);
PackageInfo.fromPlatform().then((info) {
- setState(() { _info = info; });
+ setState(() {
+ _info = info;
+ });
}).catchError((e) {
print('Platform error $e');
});
@@ -87,34 +91,34 @@ class _HomeState extends State {
}
}
- void _onUserSelectUrl(String v) {
+ void _onUserSelectUrl(String? v) {
print('User select $v, url=$_url, text=${_urlController.text}');
if (_url != v) {
setState(() {
- _urlController.text = _url = v;
+ _urlController.text = _url = v!;
});
}
}
bool isUrlValid() {
- return _url != null && _url.contains('://');
+ return _url.contains('://');
}
void disposeCamera() async {
- if (_cameraController == null) {
- return;
- }
+ // if (_cameraController == null) {
+ // return;
+ // }
_isPublishing = false;
- await _cameraController.stopVideoStreaming();
- await _cameraController.dispose();
- _cameraController = null;
+ // await _cameraController?.stopVideoStreaming();
+ // await _cameraController?.dispose();
+ // _cameraController = null;
print('Camera disposed, publish=$_isPublish, publishing=$_isPublishing');
}
void stopPublish() async {
- await disposeCamera();
- setState(() { });
- print('Stop publish url=$_url, publishing=$_isPublishing, controller=${_cameraController?.value.isInitialized}');
+ disposeCamera();
+ setState(() {});
+ print('Stop publish url=$_url, publishing=$_isPublishing');
}
void _onStartPlayOrPublish(BuildContext context) async {
@@ -123,12 +127,15 @@ class _HomeState extends State {
return;
}
- print('${_isPublishing? "Stop":""} ${_isPublish? "Publish":"Play"} url=$_url, publishing=$_isPublishing');
+ print(
+ '${_isPublishing ? "Stop" : ""} ${_isPublish ? "Publish" : "Play"} url=$_url, publishing=$_isPublishing');
// For player.
if (!_isPublish) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
- return _url.startsWith('webrtc://')? WebRTCStreamingPlayer(_url) : LiveStreamingPlayer(_url);
+ return _url.startsWith('webrtc://')
+ ? WebRTCStreamingPlayer(_url)
+ : LiveStreamingPlayer(_url);
}));
return;
}
@@ -143,33 +150,38 @@ class _HomeState extends State {
if (_url.startsWith('rtmp://')) {
stopPublish();
- var cameras = await camera.availableCameras();
- if (cameras.isEmpty) {
- print('Error: No cameras');
- return;
- }
-
- camera.CameraDescription desc = cameras[0];
- for (var c in cameras) {
- if (c.lensDirection == camera.CameraLensDirection.front) {
- desc = c;
- break;
- }
- }
- print('Use camera ${desc.name} ${desc.lensDirection}');
-
- _cameraController = camera.CameraController(desc, camera.ResolutionPreset.low);
- _cameraController.addListener(() {
- setState(() { print('got camera event'); });
- });
-
- await _cameraController.initialize();
- print('Camera initialized ok');
-
- await _cameraController.startVideoStreaming(_url, bitrate: 300 * 1000);
+ // var cameras = await camera.availableCameras();
+ // if (cameras.isEmpty) {
+ // print('Error: No cameras');
+ // return;
+ // }
+
+ // camera.CameraDescription desc = cameras[0];
+ // for (var c in cameras) {
+ // if (c.lensDirection == camera.CameraLensDirection.front) {
+ // desc = c;
+ // break;
+ // }
+ // }
+ // print('Use camera ${desc.name} ${desc.lensDirection}');
+
+ // _cameraController =
+ // camera.CameraController(desc, camera.ResolutionPreset.low);
+ // _cameraController?.addListener(() {
+ // setState(() {
+ // print('got camera event');
+ // });
+ // });
+
+ // await _cameraController?.initialize();
+ // print('Camera initialized ok');
+
+ // await _cameraController?.startVideoStreaming(_url, bitrate: 300 * 1000);
print('Start streaming to $_url');
- setState(() { _isPublishing = true; });
+ setState(() {
+ _isPublishing = true;
+ });
}
}
@@ -177,7 +189,9 @@ class _HomeState extends State {
if (!v) {
stopPublish();
}
- setState(() { _isPublish = v; });
+ setState(() {
+ _isPublish = v;
+ });
}
}
@@ -189,9 +203,10 @@ class UrlInputDisplay extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
child: TextField(
- controller: _controller, autofocus: false,
- decoration: InputDecoration(hintText: 'Please select or input url...')
- ),
+ controller: _controller,
+ autofocus: false,
+ decoration:
+ InputDecoration(hintText: 'Please select or input url...')),
padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
);
}
@@ -199,86 +214,159 @@ class UrlInputDisplay extends StatelessWidget {
class DemoUrlsDisplay extends StatelessWidget {
final String _url;
- final ValueChanged _onUserSelectUrl;
+ final ValueChanged _onUserSelectUrl;
final bool _isPublish;
DemoUrlsDisplay(this._url, this._onUserSelectUrl, this._isPublish);
@override
Widget build(BuildContext context) {
- return Container(child:
- _isPublish? Column(children: [
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('RTMP', style: TextStyle(fontWeight: FontWeight.bold)),
- Text(flutter_live.FlutterLive.rtmp_publish, style: TextStyle(color: Colors.grey[500])),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.rtmp_publish), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.rtmp_publish, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('RTMP', style: TextStyle(fontWeight: FontWeight.bold)),
- Text(flutter_live.FlutterLive.rtmp_publish2, style: TextStyle(color: Colors.grey[500])),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.rtmp_publish2), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.rtmp_publish2, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ],) : Column(children: [
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('RTMP', style: TextStyle(fontWeight: FontWeight.bold)),
- Text(flutter_live.FlutterLive.rtmp, style: TextStyle(color: Colors.grey[500])),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.rtmp), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.rtmp, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('HLS', style: TextStyle(fontWeight: FontWeight.bold)),
- Text(flutter_live.FlutterLive.hls, style: TextStyle(color: Colors.grey[500])),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.hls), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.hls, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('HTTP-FLV', style: TextStyle(fontWeight: FontWeight.bold)),
- Text(flutter_live.FlutterLive.flv, style: TextStyle(color: Colors.grey[500])),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.flv), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.flv, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('WebRTC', style: TextStyle(fontWeight: FontWeight.bold)),
- Text(flutter_live.FlutterLive.rtc, style: TextStyle(color: Colors.grey[500], fontSize: 15)),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.rtc), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.rtc, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('HTTPS-FLV', style: TextStyle(fontWeight: FontWeight.bold)),
- Container(
- child: Text(flutter_live.FlutterLive.flvs, style: TextStyle(color: Colors.grey[500], fontSize: 15)),
- padding: EdgeInsets.only(top: 3, bottom:3),
- ),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.flvs), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.flvs, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ListTile(
- title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text('HTTPS-HLS', style: TextStyle(fontWeight: FontWeight.bold)),
- Container(
- child: Text(flutter_live.FlutterLive.hlss, style: TextStyle(color: Colors.grey[500], fontSize: 14)),
- padding: EdgeInsets.only(top: 3, bottom: 3),
- ),
- ]),
- onTap: () => _onUserSelectUrl(flutter_live.FlutterLive.hlss), contentPadding: EdgeInsets.zero,
- leading: Radio(value: flutter_live.FlutterLive.hlss, groupValue: _url, onChanged: _onUserSelectUrl),
- ),
- ]),
+ return Container(
+ child: _isPublish
+ ? Column(
+ children: [
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('RTMP',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Text(flutter_live.FlutterLive.rtmp_publish,
+ style: TextStyle(color: Colors.grey[500])),
+ ]),
+ onTap: () =>
+ _onUserSelectUrl!(flutter_live.FlutterLive.rtmp_publish),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.rtmp_publish,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('RTMP',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Text(flutter_live.FlutterLive.rtmp_publish2,
+ style: TextStyle(color: Colors.grey[500])),
+ ]),
+ onTap: () =>
+ _onUserSelectUrl(flutter_live.FlutterLive.rtmp_publish2),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.rtmp_publish2,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ],
+ )
+ : Column(children: [
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('RTMP',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Text(flutter_live.FlutterLive.rtmp,
+ style: TextStyle(color: Colors.grey[500])),
+ ]),
+ onTap: () => _onUserSelectUrl!(flutter_live.FlutterLive.rtmp),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.rtmp,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('HLS',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Text(flutter_live.FlutterLive.hls,
+ style: TextStyle(color: Colors.grey[500])),
+ ]),
+ onTap: () => _onUserSelectUrl!(flutter_live.FlutterLive.hls),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.hls,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('HTTP-FLV',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Text(flutter_live.FlutterLive.flv,
+ style: TextStyle(color: Colors.grey[500])),
+ ]),
+ onTap: () => _onUserSelectUrl!(flutter_live.FlutterLive.flv),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.flv,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('WebRTC',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Text(flutter_live.FlutterLive.rtc,
+ style:
+ TextStyle(color: Colors.grey[500], fontSize: 15)),
+ ]),
+ onTap: () => _onUserSelectUrl!(flutter_live.FlutterLive.rtc),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.rtc,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('HTTPS-FLV',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Container(
+ child: Text(flutter_live.FlutterLive.flvs,
+ style: TextStyle(
+ color: Colors.grey[500], fontSize: 15)),
+ padding: EdgeInsets.only(top: 3, bottom: 3),
+ ),
+ ]),
+ onTap: () => _onUserSelectUrl!(flutter_live.FlutterLive.flvs),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.flvs,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ListTile(
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('HTTPS-HLS',
+ style: TextStyle(fontWeight: FontWeight.bold)),
+ Container(
+ child: Text(flutter_live.FlutterLive.hlss,
+ style: TextStyle(
+ color: Colors.grey[500], fontSize: 14)),
+ padding: EdgeInsets.only(top: 3, bottom: 3),
+ ),
+ ]),
+ onTap: () => _onUserSelectUrl!(flutter_live.FlutterLive.hlss),
+ contentPadding: EdgeInsets.zero,
+ leading: Radio(
+ value: flutter_live.FlutterLive.hlss,
+ groupValue: _url,
+ onChanged: _onUserSelectUrl),
+ ),
+ ]),
);
}
}
@@ -289,7 +377,8 @@ class ControlDisplay extends StatelessWidget {
final bool _isPubslish;
final bool _isPublishing;
final ValueChanged _onSwitchPublish;
- ControlDisplay(this._urlAvailable, this._onStartPlayOrPublish, this._isPubslish, this._isPublishing, this._onSwitchPublish);
+ ControlDisplay(this._urlAvailable, this._onStartPlayOrPublish,
+ this._isPubslish, this._isPublishing, this._onSwitchPublish);
@override
Widget build(BuildContext context) {
@@ -297,7 +386,7 @@ class ControlDisplay extends StatelessWidget {
if (_isPublishing) {
return 'Stop';
}
- return _isPubslish? 'Publish' : 'Play';
+ return _isPubslish ? 'Publish' : 'Play';
};
return Row(
@@ -311,9 +400,9 @@ class ControlDisplay extends StatelessWidget {
),
Container(
width: 120,
- child:ElevatedButton(
+ child: ElevatedButton(
child: Text(actionText()),
- onPressed: _urlAvailable? _onStartPlayOrPublish : null,
+ onPressed: _urlAvailable ? _onStartPlayOrPublish : null,
),
),
],
@@ -321,33 +410,35 @@ class ControlDisplay extends StatelessWidget {
}
}
-class CameraDisplay extends StatelessWidget {
- final bool _isPublish;
- final camera.CameraController _cameraController;
- CameraDisplay(this._isPublish, this._cameraController);
-
- @override
- Widget build(BuildContext context) {
- if (!_isPublish) {
- return Container();
- }
-
- if (_cameraController == null) {
- return Container();
- }
-
- if (!_cameraController.value.isInitialized) {
- return Container(child: Center(child: Text(
- 'Camera not available', style: TextStyle(color: Colors.red[500]),
- )));
- }
-
- return AspectRatio(
- aspectRatio: _cameraController.value.aspectRatio,
- child: camera.CameraPreview(_cameraController)
- );
- }
-}
+// class CameraDisplay extends StatelessWidget {
+// final bool _isPublish;
+// final camera.CameraController _cameraController;
+// CameraDisplay(this._isPublish, this._cameraController);
+
+// @override
+// Widget build(BuildContext context) {
+// if (!_isPublish) {
+// return Container();
+// }
+
+// if (_cameraController == null) {
+// return Container();
+// }
+
+// if (!_cameraController.value.isInitialized) {
+// return Container(
+// child: Center(
+// child: Text(
+// 'Camera not available',
+// style: TextStyle(color: Colors.red[500]),
+// )));
+// }
+
+// return AspectRatio(
+// aspectRatio: _cameraController.value.aspectRatio,
+// child: camera.CameraPreview(_cameraController));
+// }
+// }
class PlatformDisplay extends StatelessWidget {
final PackageInfo _info;
@@ -361,10 +452,11 @@ class PlatformDisplay extends StatelessWidget {
Text.rich(
TextSpan(
text: 'SRS/v${_info.version}+${_info.buildNumber}',
- recognizer: TapGestureRecognizer() ..onTap = () async {
- SharedPreferences prefs = await SharedPreferences.getInstance();
- prefs.setBool("login", false);
- },
+ recognizer: TapGestureRecognizer()
+ ..onTap = () async {
+ SharedPreferences prefs = await SharedPreferences.getInstance();
+ prefs.setBool("login", false);
+ },
),
),
],
@@ -381,16 +473,17 @@ class LiveStreamingPlayer extends StatefulWidget {
}
class _LiveStreamingPlayerState extends State {
- final flutter_live.RealtimePlayer _player = flutter_live.RealtimePlayer(fijkplayer.FijkPlayer());
+ final flutter_live.RealtimePlayer _player =
+ flutter_live.RealtimePlayer(fijkplayer.FijkPlayer());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('SRS Live Streaming')),
body: fijkplayer.FijkView(
- player: _player.fijk, panelBuilder: fijkplayer.fijkPanel2Builder(),
- fsFit: fijkplayer.FijkFit.fill
- ),
+ player: _player.fijk,
+ panelBuilder: fijkplayer.fijkPanel2Builder(),
+ fsFit: fijkplayer.FijkFit.fill),
);
}
@@ -430,9 +523,11 @@ class _WebRTCStreamingPlayerState extends State {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('SRS WebRTC Streaming')),
- body: GestureDetector(onTap: _switchLoudspeaker, child: Container(
- child: webrtc.RTCVideoView(_video), decoration: BoxDecoration(color: Colors.grey[500])
- )),
+ body: GestureDetector(
+ onTap: _switchLoudspeaker,
+ child: Container(
+ child: webrtc.RTCVideoView(_video),
+ decoration: BoxDecoration(color: Colors.grey[500]))),
);
}
@@ -449,7 +544,9 @@ class _WebRTCStreamingPlayerState extends State {
// Render stream when got remote stream.
_player.onRemoteStream = (webrtc.MediaStream stream) {
// @remark It's very important to use setState to set the srcObject and notify render.
- setState(() { _video.srcObject = stream; });
+ setState(() {
+ _video.srcObject = stream;
+ });
};
// Auto start play WebRTC streaming.
@@ -457,7 +554,8 @@ class _WebRTCStreamingPlayerState extends State {
}
void _switchLoudspeaker() {
- print('setSpeakerphoneOn: $_loudspeaker(${_loudspeaker? "Loudspeaker":"Earpiece"})');
+ print(
+ 'setSpeakerphoneOn: $_loudspeaker(${_loudspeaker ? "Loudspeaker" : "Earpiece"})');
flutter_live.FlutterLive.setSpeakerphoneOn(_loudspeaker);
_loudspeaker = !_loudspeaker;
}
@@ -469,4 +567,3 @@ class _WebRTCStreamingPlayerState extends State {
_player.dispose();
}
}
-
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index a7eb10d..02ab851 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -4,10 +4,10 @@ version: 1.0.9+1
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
-publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+publish_to: "none" # Remove this line if you wish to publish to pub.dev
environment:
- sdk: ">=2.7.0 <3.0.0"
+ sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
@@ -28,7 +28,7 @@ dependencies:
shared_preferences: ^2.0.6
# To get the version in this file.
- package_info: '>=0.4.0 <2.0.0'
+ package_info: ">=0.4.0 <2.0.0"
dev_dependencies:
flutter_test:
@@ -39,7 +39,6 @@ dev_dependencies:
# The following section is specific to Flutter.
flutter:
-
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
diff --git a/lib/flutter_live.dart b/lib/flutter_live.dart
index 6cd7607..9b313de 100644
--- a/lib/flutter_live.dart
+++ b/lib/flutter_live.dart
@@ -20,7 +20,8 @@ class FlutterLive {
/// Set the speaker phone on.
// [enabled] Use Earpiece if false, or Loudspeaker if true.
static Future setSpeakerphoneOn(bool enabled) async {
- await _channel.invokeMethod('setSpeakerphoneOn', {'enabled': enabled});
+ await _channel.invokeMethod(
+ 'setSpeakerphoneOn', {'enabled': enabled});
}
/// RTMP demo stream by https://ossrs.net/
@@ -78,19 +79,29 @@ class RealtimePlayer {
Future play(String url) async {
print('Start play live streaming $url');
- await _player.setOption(fijkplayer.FijkOption.playerCategory, "mediacodec-all-videos", 1);
- await _player.setOption(fijkplayer.FijkOption.hostCategory, "request-screen-on", 1);
- await _player.setOption(fijkplayer.FijkOption.hostCategory, "request-audio-focus", 1);
+ await _player.setOption(
+ fijkplayer.FijkOption.playerCategory, "mediacodec-all-videos", 1);
+ await _player.setOption(
+ fijkplayer.FijkOption.hostCategory, "request-screen-on", 1);
+ await _player.setOption(
+ fijkplayer.FijkOption.hostCategory, "request-audio-focus", 1);
// Live low-latency: https://www.jianshu.com/p/d6a5d8756eec
// For all options, read https://github.com/Bilibili/ijkplayer/blob/master/ijkmedia/ijkplayer/ff_ffplay_options.h
- await _player.setOption(fijkplayer.FijkOption.formatCategory, "probesize", 16 * 1024); // in bytes
- await _player.setOption(fijkplayer.FijkOption.formatCategory, "analyzeduration", 100 * 1000); // in us
- await _player.setOption(fijkplayer.FijkOption.playerCategory, "packet-buffering", 0); // 0, no buffer.
- await _player.setOption(fijkplayer.FijkOption.playerCategory, "max_cached_duration", 800); // in ms
- await _player.setOption(fijkplayer.FijkOption.playerCategory, "max-buffer-size", 32 * 1024); // in bytes
- await _player.setOption(fijkplayer.FijkOption.playerCategory, "infbuf", 1); // 1 for realtime.
- await _player.setOption(fijkplayer.FijkOption.playerCategory, "min-frames", 1); // in frames
+ await _player.setOption(fijkplayer.FijkOption.formatCategory, "probesize",
+ 16 * 1024); // in bytes
+ await _player.setOption(fijkplayer.FijkOption.formatCategory,
+ "analyzeduration", 100 * 1000); // in us
+ await _player.setOption(fijkplayer.FijkOption.playerCategory,
+ "packet-buffering", 0); // 0, no buffer.
+ await _player.setOption(fijkplayer.FijkOption.playerCategory,
+ "max_cached_duration", 800); // in ms
+ await _player.setOption(fijkplayer.FijkOption.playerCategory,
+ "max-buffer-size", 32 * 1024); // in bytes
+ await _player.setOption(
+ fijkplayer.FijkOption.playerCategory, "infbuf", 1); // 1 for realtime.
+ await _player.setOption(
+ fijkplayer.FijkOption.playerCategory, "min-frames", 1); // in frames
await _player.setDataSource(url, autoPlay: true).catchError((e) {
print("setDataSource error: $e");
@@ -110,9 +121,10 @@ class RealtimePlayer {
/// streamUrl: "webrtc://d.ossrs.net:11985/live/livestream"
class WebRTCUri {
/// The api server url for WebRTC streaming.
- String api;
+ late String api;
+
/// The stream url to play or publish.
- String streamUrl;
+ late String streamUrl;
/// Parse the url to WebRTC uri.
static WebRTCUri parse(String url) {
@@ -120,21 +132,23 @@ class WebRTCUri {
var schema = 'https'; // For native, default to HTTPS
if (uri.queryParameters.containsKey('schema')) {
- schema = uri.queryParameters['schema'];
+ schema = uri.queryParameters['schema']!;
} else {
schema = 'https';
}
- var port = (uri.port > 0)? uri.port : 443;
+ schema = 'http';
+
+ var port = (uri.port > 0) ? uri.port : 443;
if (schema == 'https') {
- port = (uri.port > 0)? uri.port : 443;
+ port = (uri.port > 0) ? uri.port : 443;
} else if (schema == 'http') {
- port = (uri.port > 0)? uri.port : 1985;
+ port = (uri.port > 0) ? uri.port : 1985;
}
var api = '/rtc/v1/play/';
if (uri.queryParameters.containsKey('play')) {
- api = uri.queryParameters['play'];
+ api = uri.queryParameters['play']!;
}
var apiParams = [];
@@ -157,25 +171,26 @@ class WebRTCUri {
}
}
+typedef AddStreamCallback = void Function(webrtc.MediaStream stream);
+
/// A WebRTC player, using [flutter_webrtc](https://pub.dev/packages/flutter_webrtc)
class WebRTCPlayer {
- webrtc.AddStreamCallback _onRemoteStream;
- webrtc.RTCPeerConnection _pc;
+ late AddStreamCallback _onRemoteStream;
+ webrtc.RTCPeerConnection? _pc = null;
/// When got a remote stream.
- set onRemoteStream(webrtc.AddStreamCallback v) {
+ set onRemoteStream(AddStreamCallback v) {
_onRemoteStream = v;
}
/// Initialize the player.
- void initState() {
- }
+ void initState() {}
/// Start play a url.
/// [url] must a path parsed by [WebRTCUri.parse] in https://github.com/rtcdn/rtcdn-draft
Future play(String url) async {
if (_pc != null) {
- await _pc.close();
+ await _pc?.close();
}
// Create the peer connection.
@@ -187,7 +202,7 @@ class WebRTCPlayer {
print('WebRTC: createPeerConnection done');
// Setup the peer connection.
- _pc.onAddStream = (webrtc.MediaStream stream) {
+ _pc?.onAddStream = (webrtc.MediaStream stream) {
print('WebRTC: got stream ${stream.id}');
if (_onRemoteStream == null) {
print('Warning: Stream ${stream.id} is leak');
@@ -196,37 +211,44 @@ class WebRTCPlayer {
_onRemoteStream(stream);
};
- _pc.addTransceiver(
- kind: webrtc.RTCRtpMediaType.RTCRtpMediaTypeAudio,
- init: webrtc.RTCRtpTransceiverInit(direction: webrtc.TransceiverDirection.RecvOnly),
+ _pc?.addTransceiver(
+ kind: webrtc.RTCRtpMediaType.RTCRtpMediaTypeAudio,
+ init: webrtc.RTCRtpTransceiverInit(
+ direction: webrtc.TransceiverDirection.RecvOnly),
);
- _pc.addTransceiver(
+ _pc?.addTransceiver(
kind: webrtc.RTCRtpMediaType.RTCRtpMediaTypeVideo,
- init: webrtc.RTCRtpTransceiverInit(direction: webrtc.TransceiverDirection.RecvOnly),
+ init: webrtc.RTCRtpTransceiverInit(
+ direction: webrtc.TransceiverDirection.RecvOnly),
);
print('WebRTC: Setup PC done, A|V RecvOnly');
// Start SDP handshake.
- webrtc.RTCSessionDescription offer = await _pc.createOffer({
+ webrtc.RTCSessionDescription? offer = await _pc?.createOffer({
'mandatory': {'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true},
});
- await _pc.setLocalDescription(offer);
- print('WebRTC: createOffer, ${offer.type} is ${offer.sdp.replaceAll('\n', '\\n').replaceAll('\r', '\\r')}');
- webrtc.RTCSessionDescription answer = await _handshake(url, offer.sdp);
- print('WebRTC: got ${answer.type} is ${answer.sdp.replaceAll('\n', '\\n').replaceAll('\r', '\\r')}');
+ await _pc?.setLocalDescription(offer!);
+ print(
+ 'WebRTC: createOffer, ${offer!.type} is ${offer.sdp?.replaceAll('\n', '\\n').replaceAll('\r', '\\r')}');
+
+ webrtc.RTCSessionDescription answer = await _handshake(url, offer.sdp!);
+ print(
+ 'WebRTC: got ${answer.type} is ${answer.sdp?.replaceAll('\n', '\\n').replaceAll('\r', '\\r')}');
- await _pc.setRemoteDescription(answer);
+ await _pc?.setRemoteDescription(answer);
}
/// Handshake to exchange SDP, send offer and got answer.
- Future _handshake(String url, String offer) async {
+ Future _handshake(
+ String url, String offer) async {
// Setup the client for HTTP or HTTPS.
HttpClient client = HttpClient();
try {
// Allow self-sign certificate, see https://api.flutter.dev/flutter/dart-io/HttpClient/badCertificateCallback.html
- client.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
+ client.badCertificateCallback =
+ (X509Certificate cert, String host, int port) => true;
// Parsing the WebRTC uri form url.
WebRTCUri uri = WebRTCUri.parse(url);
@@ -238,9 +260,11 @@ class WebRTCPlayer {
// {api: "xxx", sdp: "offer", streamurl: "webrtc://d.ossrs.net:11985/live/livestream"}
// Response:
// {code: 0, sdp: "answer", sessionid: "007r51l7:X2Lv"}
+ print('${uri.api}');
HttpClientRequest req = await client.postUrl(Uri.parse(uri.api));
req.headers.set('Content-Type', 'application/json');
- req.add(utf8.encode(json.encode({'api': uri.api, 'streamurl': uri.streamUrl, 'sdp': offer})));
+ req.add(utf8.encode(json
+ .encode({'api': uri.api, 'streamurl': uri.streamUrl, 'sdp': offer})));
print('WebRTC request: ${uri.api} offer=${offer.length}B');
HttpClientResponse res = await req.close();
@@ -261,8 +285,7 @@ class WebRTCPlayer {
/// Dispose the player.
void dispose() {
if (_pc != null) {
- _pc.close();
+ _pc?.close();
}
}
}
-
diff --git a/pubspec.yaml b/pubspec.yaml
index 4d7c961..b1187ff 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -4,15 +4,16 @@ description: Live streaming player, iOS+Android, RTMP/HTTP-FLV/HLS/WebRTC, by Fl
homepage: https://github.com/ossrs/flutter_live
environment:
- sdk: ">=2.7.0 <3.0.0"
- flutter: ">=1.20.0 <2.0.0"
+ sdk: ">=3.0.0"
+ flutter: ">=1.20.0"
dependencies:
flutter:
sdk: flutter
- fijkplayer: ^0.8.8
- flutter_webrtc: ^0.6.3
- camera_with_rtmp: ^0.3.2
+ fijkplayer: ^0.11.0
+ flutter_webrtc: ^0.10.4
+ # camera_with_rtmp:
+ # path: ../flutter_rtmppublisher/
dev_dependencies:
flutter_test:
@@ -65,4 +66,3 @@ flutter:
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
-