downloadManagerMap;
403 |
404 | private FlutterTencentplayerPlusPlugin(Registrar registrar) {
405 | this.registrar = registrar;
406 | this.videoPlayers = new LongSparseArray<>();
407 | this.downloadManagerMap = new HashMap<>();
408 |
409 |
410 |
411 | }
412 |
413 |
414 | /**
415 | * Plugin registration.
416 | */
417 | public static void registerWith(Registrar registrar) {
418 | final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_tencentplayer");
419 | final FlutterTencentplayerPlusPlugin plugin = new FlutterTencentplayerPlusPlugin(registrar);
420 |
421 | channel.setMethodCallHandler(plugin);
422 |
423 | registrar.addViewDestroyListener(
424 | new PluginRegistry.ViewDestroyListener() {
425 | @Override
426 | public boolean onViewDestroy(FlutterNativeView flutterNativeView) {
427 | plugin.onDestroy();
428 | return false;
429 | }
430 | }
431 | );
432 | }
433 |
434 | @Override
435 | public void onMethodCall(MethodCall call, Result result) {
436 | TextureRegistry textures = registrar.textures();
437 | if (call.method.equals("getPlatformVersion")) {
438 | result.success("Android " + android.os.Build.VERSION.RELEASE);
439 | }
440 |
441 | switch (call.method) {
442 | case "init":
443 | disposeAllPlayers();
444 | break;
445 | case "create":
446 | TextureRegistry.SurfaceTextureEntry handle = textures.createSurfaceTexture();
447 |
448 | EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_tencentplayer/videoEvents" + handle.id());
449 |
450 |
451 | TencentPlayer player = new TencentPlayer(registrar, eventChannel, handle, call, result);
452 | videoPlayers.put(handle.id(), player);
453 | break;
454 | case "download":
455 | String urlOrFileId = call.argument("urlOrFileId").toString();
456 | EventChannel downloadEventChannel = new EventChannel(registrar.messenger(), "flutter_tencentplayer/downloadEvents" + urlOrFileId);
457 | TencentDownload tencentDownload = new TencentDownload(registrar, downloadEventChannel, call, result);
458 |
459 | downloadManagerMap.put(urlOrFileId, tencentDownload);
460 | break;
461 | case "stopDownload":
462 | downloadManagerMap.get(call.argument("urlOrFileId").toString()).stopDownload();
463 | result.success(null);
464 | break;
465 | default:
466 | long textureId = ((Number) call.argument("textureId")).longValue();
467 | TencentPlayer tencentPlayer = videoPlayers.get(textureId);
468 | if (tencentPlayer == null) {
469 | result.error(
470 | "Unknown textureId",
471 | "No video player associated with texture id " + textureId,
472 | null);
473 | return;
474 | }
475 | onMethodCall(call, result, textureId, tencentPlayer);
476 | break;
477 |
478 | }
479 | }
480 |
481 | // flutter 发往android的命令
482 | private void onMethodCall(MethodCall call, Result result, long textureId, TencentPlayer player) {
483 | switch (call.method) {
484 | case "play":
485 | player.play();
486 | result.success(null);
487 | break;
488 | case "pause":
489 | player.pause();
490 | result.success(null);
491 | break;
492 | case "seekTo":
493 | int location = ((Number) call.argument("location")).intValue();
494 | player.seekTo(location);
495 | result.success(null);
496 | break;
497 | case "setRate":
498 | float rate = ((Number) call.argument("rate")).floatValue();
499 | player.setRate(rate);
500 | result.success(null);
501 | break;
502 | case "setBitrateIndex":
503 | int bitrateIndex = ((Number) call.argument("index")).intValue();
504 | player.setBitrateIndex(bitrateIndex);
505 | result.success(null);
506 | break;
507 | case "dispose":
508 | player.dispose();
509 | videoPlayers.remove(textureId);
510 | result.success(null);
511 | break;
512 | default:
513 | result.notImplemented();
514 | break;
515 | }
516 |
517 | }
518 |
519 |
520 | private void disposeAllPlayers() {
521 | for (int i = 0; i < videoPlayers.size(); i++) {
522 | videoPlayers.valueAt(i).dispose();
523 | }
524 | videoPlayers.clear();
525 | }
526 |
527 | private void onDestroy() {
528 | disposeAllPlayers();
529 | }
530 | }
531 |
--------------------------------------------------------------------------------
/android/src/main/java/plus/tencentplayer/wilson/flutter/flutter_tencentplayer_plus/TencentQueuingEventSink.java:
--------------------------------------------------------------------------------
1 | package plus.tencentplayer.wilson.flutter.flutter_tencentplayer_plus;
2 |
3 | import java.util.ArrayList;
4 |
5 | import io.flutter.plugin.common.EventChannel;
6 |
7 | /**
8 | * And implementation of {@link EventChannel.EventSink} which can wrap an underlying sink.
9 | *
10 | * It delivers messages immediately when downstream is available, but it queues messages before
11 | * the delegate event sink is set with setDelegate.
12 | *
13 | *
This class is not thread-safe. All calls must be done on the same thread or synchronized
14 | * externally.
15 | */
16 | final class TencentQueuingEventSink implements EventChannel.EventSink {
17 | private EventChannel.EventSink delegate;
18 | private ArrayList eventQueue = new ArrayList<>();
19 | private boolean done = false;
20 |
21 | public void setDelegate(EventChannel.EventSink delegate) {
22 | this.delegate = delegate;
23 | maybeFlush();
24 | }
25 |
26 | @Override
27 | public void endOfStream() {
28 | enqueue(new EndOfStreamEvent());
29 | maybeFlush();
30 | done = true;
31 | }
32 |
33 | @Override
34 | public void error(String code, String message, Object details) {
35 | enqueue(new ErrorEvent(code, message, details));
36 | maybeFlush();
37 | }
38 |
39 | @Override
40 | public void success(Object event) {
41 | enqueue(event);
42 | maybeFlush();
43 | }
44 |
45 | private void enqueue(Object event) {
46 | if (done) {
47 | return;
48 | }
49 | eventQueue.add(event);
50 | }
51 |
52 | private void maybeFlush() {
53 | if (delegate == null) {
54 | return;
55 | }
56 | for (Object event : eventQueue) {
57 | if (event instanceof EndOfStreamEvent) {
58 | delegate.endOfStream();
59 | } else if (event instanceof ErrorEvent) {
60 | ErrorEvent errorEvent = (ErrorEvent) event;
61 | delegate.error(errorEvent.code, errorEvent.message, errorEvent.details);
62 | } else {
63 | delegate.success(event);
64 | }
65 | }
66 | eventQueue.clear();
67 | }
68 |
69 | private static class EndOfStreamEvent {}
70 |
71 | private static class ErrorEvent {
72 | String code;
73 | String message;
74 | Object details;
75 |
76 | ErrorEvent(String code, String message, Object details) {
77 | this.code = code;
78 | this.message = message;
79 | this.details = details;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/android/src/main/java/plus/tencentplayer/wilson/flutter/flutter_tencentplayer_plus/Util.java:
--------------------------------------------------------------------------------
1 | package plus.tencentplayer.wilson.flutter.flutter_tencentplayer_plus;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.HashMap;
5 |
6 | public class Util {
7 | public static HashMap convertToMap(Object obj) {
8 |
9 | HashMap map = new HashMap();
10 | Field[] fields = obj.getClass().getDeclaredFields();
11 | for (int i = 0, len = fields.length; i < len; i++) {
12 | String varName = fields[i].getName();
13 | boolean accessFlag = fields[i].isAccessible();
14 | fields[i].setAccessible(true);
15 |
16 | Object o = null;
17 | try {
18 | o = fields[i].get(obj);
19 | } catch (IllegalAccessException e) {
20 | e.printStackTrace();
21 | }
22 | if (o != null)
23 | map.put(varName, o.toString());
24 |
25 | fields[i].setAccessible(accessFlag);
26 | }
27 |
28 | return map;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .packages
28 | .pub-cache/
29 | .pub/
30 | /build/
31 |
32 | # Android related
33 | **/android/**/gradle-wrapper.jar
34 | **/android/.gradle
35 | **/android/captures/
36 | **/android/gradlew
37 | **/android/gradlew.bat
38 | **/android/local.properties
39 | **/android/**/GeneratedPluginRegistrant.java
40 |
41 | # iOS/XCode related
42 | **/ios/**/*.mode1v3
43 | **/ios/**/*.mode2v3
44 | **/ios/**/*.moved-aside
45 | **/ios/**/*.pbxuser
46 | **/ios/**/*.perspectivev3
47 | **/ios/**/*sync/
48 | **/ios/**/.sconsign.dblite
49 | **/ios/**/.tags*
50 | **/ios/**/.vagrant/
51 | **/ios/**/DerivedData/
52 | **/ios/**/Icon?
53 | **/ios/**/Pods/
54 | **/ios/**/.symlinks/
55 | **/ios/**/profile
56 | **/ios/**/xcuserdata
57 | **/ios/.generated/
58 | **/ios/Flutter/App.framework
59 | **/ios/Flutter/Flutter.framework
60 | **/ios/Flutter/Generated.xcconfig
61 | **/ios/Flutter/app.flx
62 | **/ios/Flutter/app.zip
63 | **/ios/Flutter/flutter_assets/
64 | **/ios/Flutter/flutter_export_environment.sh
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 68587a0916366e9512a78df22c44163d041dd5f3
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_tencentplayer_plus_example
2 |
3 | Demonstrates how to use the flutter_tencentplayer_plus plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 |
34 | defaultConfig {
35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
36 | applicationId "plus.tencentplayer.wilson.flutter.flutter_tencentplayer_plus_example"
37 | minSdkVersion 16
38 | targetSdkVersion 28
39 | versionCode flutterVersionCode.toInteger()
40 | versionName flutterVersionName
41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
42 | }
43 |
44 | buildTypes {
45 | release {
46 | // TODO: Add your own signing config for the release build.
47 | // Signing with the debug keys for now, so `flutter run --release` works.
48 | signingConfig signingConfigs.debug
49 | }
50 | }
51 | }
52 |
53 | flutter {
54 | source '../..'
55 | }
56 |
57 | dependencies {
58 | testImplementation 'junit:junit:4.12'
59 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
61 | }
62 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
37 |
44 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/plus/tencentplayer/wilson/flutter/flutter_tencentplayer_plus_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package plus.tencentplayer.wilson.flutter.flutter_tencentplayer_plus_example;
2 |
3 | import android.os.Bundle;
4 | import io.flutter.app.FlutterActivity;
5 | import io.flutter.plugins.GeneratedPluginRegistrant;
6 |
7 | public class MainActivity extends FlutterActivity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | GeneratedPluginRegistrant.registerWith(this);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.2.1'
9 | }
10 | }
11 |
12 |
13 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
14 | def plugins = new Properties()
15 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
16 | if (pluginsFile.exists()) {
17 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
18 | }
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | flatDir {
24 | dirs "${plugins.get("flutter_tencentplayer_plus")}android/libs"
25 | }
26 | }
27 | }
28 |
29 |
30 | rootProject.buildDir = '../build'
31 | subprojects {
32 | project.buildDir = "${rootProject.buildDir}/${project.name}"
33 | }
34 | subprojects {
35 | project.evaluationDependsOn(':app')
36 | }
37 |
38 | task clean(type: Delete) {
39 | delete rootProject.buildDir
40 | }
41 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
3 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def parse_KV_file(file, separator='=')
14 | file_abs_path = File.expand_path(file)
15 | if !File.exists? file_abs_path
16 | return [];
17 | end
18 | pods_ary = []
19 | skip_line_start_symbols = ["#", "/"]
20 | File.foreach(file_abs_path) { |line|
21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
22 | plugin = line.split(pattern=separator)
23 | if plugin.length == 2
24 | podname = plugin[0].strip()
25 | path = plugin[1].strip()
26 | podpath = File.expand_path("#{path}", file_abs_path)
27 | pods_ary.push({:name => podname, :path => podpath});
28 | else
29 | puts "Invalid plugin specification: #{line}"
30 | end
31 | }
32 | return pods_ary
33 | end
34 |
35 | target 'Runner' do
36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
37 | # referring to absolute paths on developers' machines.
38 | system('rm -rf .symlinks')
39 | system('mkdir -p .symlinks/plugins')
40 |
41 | # Flutter Pods
42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
43 | if generated_xcode_build_settings.empty?
44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first."
45 | end
46 | generated_xcode_build_settings.map { |p|
47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
48 | symlink = File.join('.symlinks', 'flutter')
49 | File.symlink(File.dirname(p[:path]), symlink)
50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
51 | end
52 | }
53 |
54 | # Plugin Pods
55 | plugin_pods = parse_KV_file('../.flutter-plugins')
56 | plugin_pods.map { |p|
57 | symlink = File.join('.symlinks', 'plugins', p[:name])
58 | File.symlink(p[:path], symlink)
59 | pod p[:name], :path => File.join(symlink, 'ios')
60 | }
61 | end
62 |
63 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
64 | install! 'cocoapods', :disable_input_output_paths => true
65 |
66 | post_install do |installer|
67 | installer.pods_project.targets.each do |target|
68 | target.build_configurations.each do |config|
69 | config.build_settings['ENABLE_BITCODE'] = 'NO'
70 | end
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | NSAppTransportSecurity
7 |
8 | NSAllowsArbitraryLoads
9 |
10 |
11 | CFBundleDevelopmentRegion
12 | $(DEVELOPMENT_LANGUAGE)
13 | CFBundleExecutable
14 | $(EXECUTABLE_NAME)
15 | CFBundleIdentifier
16 | $(PRODUCT_BUNDLE_IDENTIFIER)
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | flutter_tencentplayer_plus_example
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | $(FLUTTER_BUILD_NAME)
25 | CFBundleSignature
26 | ????
27 | CFBundleVersion
28 | $(FLUTTER_BUILD_NUMBER)
29 | LSRequiresIPhoneOS
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UIViewControllerBasedStatusBarAppearance
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/home_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_tencentplayer_plus_example/video_play_page.dart';
3 |
4 | var launch = MaterialApp(
5 | title: "App",
6 | home: HomePage(),
7 | );
8 |
9 | class HomePage extends StatelessWidget {
10 | @override
11 | Widget build(BuildContext context) {
12 | return Scaffold(
13 | appBar: AppBar(
14 | centerTitle: true,
15 | title: Text('主界面'),
16 | ),
17 | body: Center(
18 | child: Padding(
19 | padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
20 | child: RaisedButton(
21 | onPressed: () {
22 | Navigator.push(context,
23 | MaterialPageRoute(builder: (context) => VideoPlayPage()));
24 | },
25 | child: const Text('进入播放器')),
26 | )),
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'dart:async';
3 | import 'package:flutter_tencentplayer_plus/flutter_tencentplayer_plus.dart';
4 | import 'package:flutter_tencentplayer_plus_example/home_page.dart';
5 |
6 | //void main() => runApp(MyApp());
7 | void main() => runApp(launch);
8 |
9 | class MyApp extends StatefulWidget {
10 | @override
11 | _MyAppState createState() => _MyAppState();
12 | }
13 |
14 | class _MyAppState extends State {
15 | TencentPlayerController _controller;
16 | VoidCallback listener;
17 |
18 | DownloadController _downloadController;
19 | VoidCallback downloadListener;
20 |
21 | String videoUrl =
22 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4.flv';
23 | String videoUrlB =
24 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4_550.flv';
25 | String videoUrlG =
26 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4_900.flv';
27 | String videoUrlAAA = 'http://file.jinxianyun.com/2018-06-12_16_58_22.mp4';
28 | String videoUrlBBB = 'http://file.jinxianyun.com/testhaha.mp4';
29 | String mu =
30 | 'http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8';
31 | String spe1 =
32 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f10.mp4';
33 | String spe2 =
34 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f20.mp4';
35 | String spe3 =
36 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f30.mp4';
37 |
38 | String testDownload =
39 | 'http://1253131631.vod2.myqcloud.com/26f327f9vodgzp1253131631/f4bdff799031868222924043041/playlist.m3u8';
40 | String downloadRes =
41 | '/storage/emulated/0/tencentdownload/txdownload/2c58873a5b9916f9fef5103c74f0ce5e.m3u8.sqlite';
42 | String downloadRes2 =
43 | '/storage/emulated/0/tencentdownload/txdownload/cf3e281653e562303c8c2b14729ba7f5.m3u8.sqlite';
44 |
45 | @override
46 | void initState() {
47 | super.initState();
48 |
49 | addListener();
50 | initPlatformState();
51 | }
52 |
53 | addListener() {
54 | // listener = () {
55 | // if (!mounted) {
56 | // return;
57 | // }
58 | // setState(() {});
59 | // };
60 | downloadListener = () {
61 | if (!mounted) {
62 | return;
63 | }
64 | setState(() {});
65 | };
66 | }
67 |
68 | Future initPlatformState() async {
69 | //点播
70 | // _controller = TencentPlayerController.network(null, playerConfig: PlayerConfig(
71 | // auth: {"appId": , "fileId": ''}
72 | // ))
73 | _controller = TencentPlayerController.network(spe3,
74 | playerConfig: PlayerConfig(autoPlay: false))
75 |
76 | // _controller = TencentPlayerController.asset('static/tencent1.mp4')
77 | // _controller = TencentPlayerController.file('/storage/emulated/0/test.mp4')
78 | ..initialize().then((_) {
79 | setState(() {});
80 | });
81 |
82 | // _controller.addListener(listener);
83 | /// 下载目录 ios 的和android的不一致 注意在ios项目运行的时候传递ios的目录
84 | _downloadController = DownloadController(
85 | '/storage/emulated/0/tencentdownload',
86 | appId: 1252463788);
87 | _downloadController.addListener(downloadListener);
88 | }
89 |
90 | @override
91 | void dispose() {
92 | super.dispose();
93 | // _controller.removeListener(listener);
94 | _downloadController.removeListener(downloadListener);
95 | }
96 |
97 | @override
98 | Widget build(BuildContext context) {
99 | return MaterialApp(
100 | title: 'Video Demo',
101 | home: ValueListenableBuilder(
102 | valueListenable: _controller,
103 | builder:
104 | (BuildContext context, TencentPlayerValue value, Widget child) {
105 | return Scaffold(
106 | body: Container(
107 | child: Column(
108 | children: [
109 | Container(
110 | child: Stack(
111 | alignment: AlignmentDirectional.center,
112 | children: [
113 | // _controller.value.initialized ?
114 | AspectRatio(
115 | aspectRatio: _controller.value.aspectRatio,
116 | child: TencentPlayer(_controller),
117 | ),
118 | Center(
119 | child: _controller.value.isLoading
120 | ? CircularProgressIndicator()
121 | : SizedBox(),
122 | ),
123 | ],
124 | ),
125 | ),
126 | Expanded(
127 | child: ListView(
128 | children: [
129 | Text(
130 | "播放网速:" + _controller.value.netSpeed.toString(),
131 | style: TextStyle(color: Colors.pink),
132 | ),
133 | Text(
134 | "错误:" + _controller.value.errorDescription.toString(),
135 | style: TextStyle(color: Colors.pink),
136 | ),
137 | Text(
138 | "播放进度:" + _controller.value.position.toString(),
139 | style: TextStyle(color: Colors.pink),
140 | ),
141 | Text(
142 | "缓冲进度:" + _controller.value.playable.toString(),
143 | style: TextStyle(color: Colors.pink),
144 | ),
145 | Text(
146 | "总时长:" + _controller.value.duration.toString(),
147 | style: TextStyle(color: Colors.pink),
148 | ),
149 | FlatButton(
150 | onPressed: () {
151 | _controller.seekTo(Duration(seconds: 5));
152 | },
153 | child: Text(
154 | 'seekTo 00:00:05',
155 | style: TextStyle(color: Colors.blue),
156 | )),
157 | Row(
158 | children: [
159 | FlatButton(
160 | onPressed: () {
161 | _controller.setRate(1.0);
162 | },
163 | child: Text(
164 | 'setRate 1.0',
165 | style: TextStyle(
166 | color: _controller.value.rate == 1.0
167 | ? Colors.red
168 | : Colors.blue),
169 | )),
170 | FlatButton(
171 | onPressed: () {
172 | _controller.setRate(1.5);
173 | },
174 | child: Text(
175 | 'setRate 1.5',
176 | style: TextStyle(
177 | color: _controller.value.rate == 1.5
178 | ? Colors.red
179 | : Colors.blue),
180 | )),
181 | FlatButton(
182 | onPressed: () {
183 | _controller.setRate(2.0);
184 | },
185 | child: Text(
186 | 'setRate 2.0',
187 | style: TextStyle(
188 | color: _controller.value.rate == 2.0
189 | ? Colors.red
190 | : Colors.blue),
191 | )),
192 | ],
193 | ),
194 | Row(
195 | children: [
196 | FlatButton(
197 | onPressed: () {
198 | _controller =
199 | TencentPlayerController.network(mu);
200 | _controller.initialize().then((_) {
201 | setState(() {});
202 | });
203 | // _controller.addListener(listener);
204 | },
205 | child: Text(
206 | 'm3u8点播',
207 | style: TextStyle(
208 | color:
209 | _controller.dataSource == videoUrlAAA
210 | ? Colors.red
211 | : Colors.blue),
212 | )),
213 | FlatButton(
214 | onPressed: () {
215 | _controller =
216 | TencentPlayerController.network(spe1);
217 | _controller.initialize().then((_) {
218 | setState(() {});
219 | });
220 | // _controller.addListener(listener);
221 | },
222 | child: Text(
223 | '普通点播',
224 | style: TextStyle(
225 | color: _controller.dataSource == videoUrlBBB
226 | ? Colors.red
227 | : Colors.blue),
228 | ),
229 | ),
230 | ],
231 | ),
232 | Row(
233 | children: [
234 | Container(
235 | margin: EdgeInsets.only(left: 15),
236 | child: Text(
237 | 'm3u8点播 : ',
238 | style: TextStyle(color: Colors.orange),
239 | ),
240 | ),
241 | FlatButton(
242 | child: Text(
243 | '标',
244 | style: TextStyle(
245 | color: _controller.value.bitrateIndex == 0
246 | ? Colors.yellow
247 | : Colors.green),
248 | ),
249 | onPressed: () {
250 | _controller.setBitrateIndex(0);
251 | },
252 | ),
253 | FlatButton(
254 | child: Text(
255 | '高',
256 | style: TextStyle(
257 | color: _controller.value.bitrateIndex == 1
258 | ? Colors.yellow
259 | : Colors.green),
260 | ),
261 | onPressed: () {
262 | _controller.setBitrateIndex(1);
263 | },
264 | ),
265 | FlatButton(
266 | child: Text(
267 | '超',
268 | style: TextStyle(
269 | color: _controller.value.bitrateIndex == 2
270 | ? Colors.yellow
271 | : Colors.green),
272 | ),
273 | onPressed: () {
274 | _controller.setBitrateIndex(2);
275 | },
276 | ),
277 | ],
278 | ),
279 | Row(
280 | children: [
281 | Container(
282 | margin: EdgeInsets.only(left: 15),
283 | child: Text(
284 | '普通点播 : ',
285 | style: TextStyle(color: Colors.orange),
286 | ),
287 | ),
288 | FlatButton(
289 | onPressed: () {
290 | _controller = TencentPlayerController.network(
291 | spe1,
292 | playerConfig: PlayerConfig(
293 | startTime: _controller
294 | .value.position.inSeconds));
295 | _controller.initialize().then((_) {
296 | setState(() {});
297 | });
298 | // _controller.addListener(listener);
299 | },
300 | child: Text(
301 | '标',
302 | style: TextStyle(
303 | color: _controller.dataSource == videoUrlB
304 | ? Colors.red
305 | : Colors.blue),
306 | )),
307 | FlatButton(
308 | onPressed: () {
309 | _controller = TencentPlayerController.network(
310 | spe2,
311 | playerConfig: PlayerConfig(
312 | startTime: _controller
313 | .value.position.inSeconds));
314 | _controller.initialize().then((_) {
315 | setState(() {});
316 | });
317 | // _controller.addListener(listener);
318 | },
319 | child: Text(
320 | '高',
321 | style: TextStyle(
322 | color: _controller.dataSource == videoUrlG
323 | ? Colors.red
324 | : Colors.blue),
325 | )),
326 | FlatButton(
327 | onPressed: () {
328 | _controller = TencentPlayerController.network(
329 | spe3,
330 | playerConfig: PlayerConfig(
331 | startTime: _controller
332 | .value.position.inSeconds));
333 | _controller.initialize().then((_) {
334 | setState(() {});
335 | });
336 | // _controller.addListener(listener);
337 | },
338 | child: Text(
339 | '超',
340 | style: TextStyle(
341 | color: _controller.dataSource == videoUrl
342 | ? Colors.red
343 | : Colors.blue),
344 | ),
345 | ),
346 | ],
347 | ),
348 | Row(
349 | children: [
350 | FlatButton(
351 | onPressed: () {
352 | _downloadController.dowload(
353 | "4564972819220421305",
354 | quanlity: 2);
355 | },
356 | child: Text(
357 | 'download1',
358 | style: TextStyle(color: Colors.blue),
359 | ),
360 | ),
361 | FlatButton(
362 | onPressed: () {
363 | _downloadController
364 | .pauseDownload("4564972819220421305");
365 | },
366 | child: Text(
367 | 'download1 - stop',
368 | style: TextStyle(color: Colors.blue),
369 | ),
370 | ),
371 | ],
372 | ),
373 | Row(
374 | children: [
375 | FlatButton(
376 | onPressed: () {
377 | _downloadController.dowload(testDownload);
378 | },
379 | child: Text(
380 | 'download2',
381 | style: TextStyle(color: Colors.blue),
382 | ),
383 | ),
384 | FlatButton(
385 | onPressed: () {
386 | _downloadController.pauseDownload(testDownload);
387 | },
388 | child: Text(
389 | 'download2 - stop',
390 | style: TextStyle(color: Colors.blue),
391 | ),
392 | ),
393 | ],
394 | ),
395 | Text(_downloadController.value != null
396 | ? _downloadController.value.toString()
397 | : '')
398 | ],
399 | ),
400 | )
401 | ],
402 | ),
403 | ),
404 | floatingActionButton: FloatingActionButton(
405 | onPressed: () {
406 | setState(() {
407 | _controller.value.isPlaying
408 | ? _controller.pause()
409 | : _controller.play();
410 | });
411 | },
412 | child: Icon(
413 | _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
414 | ),
415 | ),
416 | );
417 | },
418 | ),
419 | );
420 | }
421 | }
422 |
--------------------------------------------------------------------------------
/example/lib/video_play_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'dart:async';
3 | import 'package:flutter_tencentplayer_plus/flutter_tencentplayer_plus.dart';
4 | import 'package:flutter_tencentplayer_plus_example/video_play_page2.dart';
5 |
6 | class VideoPlayPage extends StatefulWidget {
7 | @override
8 | _VideoPlayPageState createState() => _VideoPlayPageState();
9 | }
10 |
11 | class _VideoPlayPageState extends State {
12 | TencentPlayerController _controller;
13 | VoidCallback listener;
14 |
15 | DownloadController _downloadController;
16 | VoidCallback downloadListener;
17 |
18 | String videoUrl =
19 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4.flv';
20 | String videoUrlB =
21 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4_550.flv';
22 | String videoUrlG =
23 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4_900.flv';
24 | String videoUrlAAA = 'http://file.jinxianyun.com/2018-06-12_16_58_22.mp4';
25 | String videoUrlBBB = 'http://file.jinxianyun.com/testhaha.mp4';
26 | String mu =
27 | 'http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8';
28 | String spe1 =
29 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f10.mp4';
30 | String spe2 =
31 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f20.mp4';
32 | String spe3 =
33 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f30.mp4';
34 |
35 | String testDownload =
36 | 'http://1253131631.vod2.myqcloud.com/26f327f9vodgzp1253131631/f4bdff799031868222924043041/playlist.m3u8';
37 | String downloadRes =
38 | '/storage/emulated/0/tencentdownload/txdownload/2c58873a5b9916f9fef5103c74f0ce5e.m3u8.sqlite';
39 | String downloadRes2 =
40 | '/storage/emulated/0/tencentdownload/txdownload/cf3e281653e562303c8c2b14729ba7f5.m3u8.sqlite';
41 |
42 |
43 |
44 | @override
45 | void initState() {
46 | super.initState();
47 | addListener();
48 | initPlatformState();
49 |
50 | }
51 |
52 | addListener(){
53 | listener = () {
54 | if (!mounted) {
55 | return;
56 | }
57 | setState(() {});
58 | };
59 | downloadListener = () {
60 | if (!mounted) {
61 | return;
62 | }
63 | setState(() {});
64 | };
65 | }
66 |
67 | Future initPlatformState() async {
68 | //点播
69 | // _controller = TencentPlayerController.network(null, playerConfig: PlayerConfig(
70 | // auth: {"appId": , "fileId": ''}
71 | // ))
72 | _controller = TencentPlayerController.network(spe3,
73 | playerConfig: PlayerConfig(autoPlay: false))
74 |
75 | // _controller = TencentPlayerController.asset('static/tencent1.mp4')
76 | // _controller = TencentPlayerController.file('/storage/emulated/0/test.mp4')
77 | ..initialize().then((_) {
78 | setState(() {});
79 | });
80 |
81 | _controller.addListener(listener);
82 | _downloadController = DownloadController(
83 | '/storage/emulated/0/tencentdownload',
84 | appId: 1252463788);
85 | _downloadController.addListener(downloadListener);
86 | }
87 |
88 | @override
89 | void dispose() {
90 | super.dispose();
91 | _controller.removeListener(listener);
92 | _downloadController.removeListener(downloadListener);
93 | }
94 |
95 | @override
96 | Widget build(BuildContext context) {
97 | return MaterialApp(
98 | title: 'Video Demo',
99 | home: Scaffold(
100 | body: Container(
101 | child: Column(
102 | children: [
103 | Container(
104 | child: Stack(
105 | alignment: AlignmentDirectional.center,
106 | children: [
107 | _controller.value.initialized
108 | ? AspectRatio(
109 | aspectRatio: _controller.value.aspectRatio,
110 | child: TencentPlayer(_controller),
111 | )
112 | : Container(),
113 | Center(
114 | child: _controller.value.isLoading
115 | ? CircularProgressIndicator()
116 | : SizedBox(),
117 | ),
118 | ],
119 | ),
120 | ),
121 | Expanded(
122 | child: ListView(
123 | children: [
124 | Text(
125 | "播放网速:" + _controller.value.netSpeed.toString(),
126 | style: TextStyle(color: Colors.pink),
127 | ),
128 | Text(
129 | "错误:" + _controller.value.errorDescription.toString(),
130 | style: TextStyle(color: Colors.pink),
131 | ),
132 | Text(
133 | "播放进度:" + _controller.value.position.toString(),
134 | style: TextStyle(color: Colors.pink),
135 | ),
136 | Text(
137 | "缓冲进度:" + _controller.value.playable.toString(),
138 | style: TextStyle(color: Colors.pink),
139 | ),
140 | Text(
141 | "总时长:" + _controller.value.duration.toString(),
142 | style: TextStyle(color: Colors.pink),
143 | ),
144 | FlatButton(
145 | onPressed: () {
146 | _controller.seekTo(Duration(seconds: 5));
147 | },
148 | child: Text(
149 | 'seekTo 00:00:05',
150 | style: TextStyle(color: Colors.blue),
151 | )),
152 | Row(
153 | children: [
154 | FlatButton(
155 | onPressed: () {
156 | _controller.setRate(1.0);
157 | },
158 | child: Text(
159 | 'setRate 1.0',
160 | style: TextStyle(
161 | color: _controller.value.rate == 1.0
162 | ? Colors.red
163 | : Colors.blue),
164 | )),
165 | FlatButton(
166 | onPressed: () {
167 | _controller.setRate(1.5);
168 | },
169 | child: Text(
170 | 'setRate 1.5',
171 | style: TextStyle(
172 | color: _controller.value.rate == 1.5
173 | ? Colors.red
174 | : Colors.blue),
175 | )),
176 | FlatButton(
177 | onPressed: () {
178 | _controller.setRate(2.0);
179 | },
180 | child: Text(
181 | 'setRate 2.0',
182 | style: TextStyle(
183 | color: _controller.value.rate == 2.0
184 | ? Colors.red
185 | : Colors.blue),
186 | )),
187 | ],
188 | ),
189 | Row(
190 | children: [
191 | FlatButton(
192 | onPressed: () {
193 | _controller = TencentPlayerController.network(mu);
194 | _controller.initialize().then((_) {
195 | setState(() {});
196 | });
197 | _controller.addListener(listener);
198 | },
199 | child: Text(
200 | 'm3u8点播',
201 | style: TextStyle(
202 | color: _controller.dataSource == videoUrlAAA
203 | ? Colors.red
204 | : Colors.blue),
205 | )),
206 | FlatButton(
207 | onPressed: () {
208 | _controller = TencentPlayerController.network(spe1);
209 | _controller.initialize().then((_) {
210 | setState(() {});
211 | });
212 | _controller.addListener(listener);
213 | },
214 | child: Text(
215 | '普通点播',
216 | style: TextStyle(
217 | color: _controller.dataSource == videoUrlBBB
218 | ? Colors.red
219 | : Colors.blue),
220 | ),
221 | ),
222 | ],
223 | ),
224 | Row(
225 | children: [
226 | Container(
227 | margin: EdgeInsets.only(left: 15),
228 | child: Text(
229 | 'm3u8点播 : ',
230 | style: TextStyle(color: Colors.orange),
231 | ),
232 | ),
233 | FlatButton(
234 | child: Text(
235 | '标',
236 | style: TextStyle(
237 | color: _controller.value.bitrateIndex == 0
238 | ? Colors.yellow
239 | : Colors.green),
240 | ),
241 | onPressed: () {
242 | _controller.setBitrateIndex(0);
243 | },
244 | ),
245 | FlatButton(
246 | child: Text(
247 | '高',
248 | style: TextStyle(
249 | color: _controller.value.bitrateIndex == 1
250 | ? Colors.yellow
251 | : Colors.green),
252 | ),
253 | onPressed: () {
254 | _controller.setBitrateIndex(1);
255 | },
256 | ),
257 | FlatButton(
258 | child: Text(
259 | '超',
260 | style: TextStyle(
261 | color: _controller.value.bitrateIndex == 2
262 | ? Colors.yellow
263 | : Colors.green),
264 | ),
265 | onPressed: () {
266 | _controller.setBitrateIndex(2);
267 | },
268 | ),
269 | ],
270 | ),
271 | Row(
272 | children: [
273 | Container(
274 | margin: EdgeInsets.only(left: 15),
275 | child: Text(
276 | '普通点播 : ',
277 | style: TextStyle(color: Colors.orange),
278 | ),
279 | ),
280 | FlatButton(
281 | onPressed: () {
282 | _controller = TencentPlayerController.network(
283 | spe1,
284 | playerConfig: PlayerConfig(
285 | startTime: _controller
286 | .value.position.inSeconds));
287 | _controller.initialize().then((_) {
288 | setState(() {});
289 | });
290 | _controller.addListener(listener);
291 | },
292 | child: Text(
293 | '标',
294 | style: TextStyle(
295 | color: _controller.dataSource == videoUrlB
296 | ? Colors.red
297 | : Colors.blue),
298 | )),
299 | FlatButton(
300 | onPressed: () {
301 | _controller = TencentPlayerController.network(
302 | spe2,
303 | playerConfig: PlayerConfig(
304 | startTime: _controller
305 | .value.position.inSeconds));
306 | _controller.initialize().then((_) {
307 | setState(() {});
308 | });
309 | _controller.addListener(listener);
310 | },
311 | child: Text(
312 | '高',
313 | style: TextStyle(
314 | color: _controller.dataSource == videoUrlG
315 | ? Colors.red
316 | : Colors.blue),
317 | )),
318 | FlatButton(
319 | onPressed: () {
320 | _controller = TencentPlayerController.network(spe3,
321 | playerConfig: PlayerConfig(
322 | startTime:
323 | _controller.value.position.inSeconds));
324 | _controller.initialize().then((_) {
325 | setState(() {});
326 | });
327 | _controller.addListener(listener);
328 | },
329 | child: Text(
330 | '超',
331 | style: TextStyle(
332 | color: _controller.dataSource == videoUrl
333 | ? Colors.red
334 | : Colors.blue),
335 | ),
336 | ),
337 | ],
338 | ),
339 | Row(
340 | children: [
341 | FlatButton(
342 | onPressed: () {
343 | _downloadController.dowload("4564972819220421305",
344 | quanlity: 2);
345 | },
346 | child: Text(
347 | 'download1',
348 | style: TextStyle(color: Colors.blue),
349 | ),
350 | ),
351 | FlatButton(
352 | onPressed: () {
353 | _downloadController
354 | .pauseDownload("4564972819220421305");
355 | },
356 | child: Text(
357 | 'download1 - stop',
358 | style: TextStyle(color: Colors.blue),
359 | ),
360 | ),
361 | ],
362 | ),
363 | Row(
364 | children: [
365 | FlatButton(
366 | onPressed: () {
367 | _downloadController.dowload(testDownload);
368 | },
369 | child: Text(
370 | 'download2',
371 | style: TextStyle(color: Colors.blue),
372 | ),
373 | ),
374 | FlatButton(
375 | onPressed: () {
376 | _downloadController.pauseDownload(testDownload);
377 | },
378 | child: Text(
379 | 'download2 - stop',
380 | style: TextStyle(color: Colors.blue),
381 | ),
382 | ),
383 | ],
384 | ),
385 | Row(
386 | children: [
387 | FlatButton(
388 | onPressed: () {
389 | Navigator.push(
390 | context,
391 | MaterialPageRoute(
392 | builder: (context) => VideoPlayPage2()));
393 | },
394 | child: Text(
395 | '进入下一个界面播放',
396 | style: TextStyle(color: Colors.blue),
397 | ),
398 | ),
399 | ],
400 | ),
401 | Text(_downloadController.value != null
402 | ? _downloadController.value.toString()
403 | : '')
404 | ],
405 | ),
406 | )
407 | ],
408 | ),
409 | ),
410 | floatingActionButton: FloatingActionButton(
411 | onPressed: () {
412 | setState(() {
413 | _controller.value.isPlaying
414 | ? _controller.pause()
415 | : _controller.play();
416 | });
417 | },
418 | child: Icon(
419 | _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
420 | ),
421 | ),
422 | ),
423 | );
424 | }
425 | }
426 |
--------------------------------------------------------------------------------
/example/lib/video_play_page2.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'dart:async';
3 | import 'package:flutter_tencentplayer_plus/flutter_tencentplayer_plus.dart';
4 |
5 | class VideoPlayPage2 extends StatefulWidget {
6 | @override
7 | _VideoPlayPage2State createState() => _VideoPlayPage2State();
8 | }
9 |
10 | class _VideoPlayPage2State extends State {
11 | TencentPlayerController _controller;
12 | VoidCallback listener;
13 |
14 | DownloadController _downloadController;
15 | VoidCallback downloadListener;
16 |
17 | String videoUrl =
18 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4.flv';
19 | String videoUrlB =
20 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4_550.flv';
21 | String videoUrlG =
22 | 'http://5815.liveplay.myqcloud.com/live/5815_89aad37e06ff11e892905cb9018cf0d4_900.flv';
23 | String videoUrlAAA = 'http://file.jinxianyun.com/2018-06-12_16_58_22.mp4';
24 | String videoUrlBBB = 'http://file.jinxianyun.com/testhaha.mp4';
25 | String mu =
26 | 'http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8';
27 | String spe1 =
28 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f10.mp4';
29 | String spe2 =
30 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f20.mp4';
31 | String spe3 =
32 | 'http://1252463788.vod2.myqcloud.com/95576ef5vodtransgzp1252463788/e1ab85305285890781763144364/v.f30.mp4';
33 |
34 | String testDownload =
35 | 'http://1253131631.vod2.myqcloud.com/26f327f9vodgzp1253131631/f4bdff799031868222924043041/playlist.m3u8';
36 | String downloadRes =
37 | '/storage/emulated/0/tencentdownload/txdownload/2c58873a5b9916f9fef5103c74f0ce5e.m3u8.sqlite';
38 | String downloadRes2 =
39 | '/storage/emulated/0/tencentdownload/txdownload/cf3e281653e562303c8c2b14729ba7f5.m3u8.sqlite';
40 |
41 | @override
42 | void initState() {
43 | super.initState();
44 | addListener();
45 | initPlatformState();
46 |
47 | }
48 |
49 |
50 | addListener() {
51 | listener = () {
52 | if (!mounted) {
53 | return;
54 | }
55 | setState(() {});
56 | };
57 | downloadListener = () {
58 | if (!mounted) {
59 | return;
60 | }
61 | setState(() {});
62 | };
63 | }
64 | Future initPlatformState() async {
65 | //点播
66 | // _controller = TencentPlayerController.network(null, playerConfig: PlayerConfig(
67 | // auth: {"appId": , "fileId": ''}
68 | // ))
69 | _controller = TencentPlayerController.network(spe3,
70 | playerConfig: PlayerConfig(autoPlay: true))
71 |
72 | // _controller = TencentPlayerController.asset('static/tencent1.mp4')
73 | // _controller = TencentPlayerController.file('/storage/emulated/0/test.mp4')
74 | ..initialize().then((_) {
75 | setState(() {});
76 | });
77 |
78 | _controller.addListener(listener);
79 | _downloadController = DownloadController(
80 | '/storage/emulated/0/tencentdownload',
81 | appId: 1252463788);
82 | _downloadController.addListener(downloadListener);
83 | }
84 |
85 | @override
86 | void dispose() {
87 | super.dispose();
88 | _controller.removeListener(listener);
89 | _downloadController.removeListener(downloadListener);
90 | }
91 |
92 | @override
93 | Widget build(BuildContext context) {
94 | return MaterialApp(
95 | title: 'Video Demo',
96 | home: Scaffold(
97 | body: Container(
98 | child: Column(
99 | children: [
100 | Container(
101 | child: Stack(
102 | alignment: AlignmentDirectional.center,
103 | children: [
104 | _controller.value.initialized
105 | ? AspectRatio(
106 | aspectRatio: _controller.value.aspectRatio,
107 | child: TencentPlayer(_controller),
108 | )
109 | : Container(),
110 | Center(
111 | child: _controller.value.isLoading
112 | ? CircularProgressIndicator()
113 | : SizedBox(),
114 | ),
115 | ],
116 | ),
117 | ),
118 | Expanded(
119 | child: ListView(
120 | children: [
121 | Text(
122 | "播放网速:" + _controller.value.netSpeed.toString(),
123 | style: TextStyle(color: Colors.pink),
124 | ),
125 | Text(
126 | "错误:" + _controller.value.errorDescription.toString(),
127 | style: TextStyle(color: Colors.pink),
128 | ),
129 | Text(
130 | "播放进度:" + _controller.value.position.toString(),
131 | style: TextStyle(color: Colors.pink),
132 | ),
133 | Text(
134 | "缓冲进度:" + _controller.value.playable.toString(),
135 | style: TextStyle(color: Colors.pink),
136 | ),
137 | Text(
138 | "总时长:" + _controller.value.duration.toString(),
139 | style: TextStyle(color: Colors.pink),
140 | ),
141 | FlatButton(
142 | onPressed: () {
143 | _controller.seekTo(Duration(seconds: 5));
144 | },
145 | child: Text(
146 | 'seekTo 00:00:05',
147 | style: TextStyle(color: Colors.blue),
148 | )),
149 | Row(
150 | children: [
151 | FlatButton(
152 | onPressed: () {
153 | _controller.setRate(1.0);
154 | },
155 | child: Text(
156 | 'setRate 1.0',
157 | style: TextStyle(
158 | color: _controller.value.rate == 1.0
159 | ? Colors.red
160 | : Colors.blue),
161 | )),
162 | FlatButton(
163 | onPressed: () {
164 | _controller.setRate(1.5);
165 | },
166 | child: Text(
167 | 'setRate 1.5',
168 | style: TextStyle(
169 | color: _controller.value.rate == 1.5
170 | ? Colors.red
171 | : Colors.blue),
172 | )),
173 | FlatButton(
174 | onPressed: () {
175 | _controller.setRate(2.0);
176 | },
177 | child: Text(
178 | 'setRate 2.0',
179 | style: TextStyle(
180 | color: _controller.value.rate == 2.0
181 | ? Colors.red
182 | : Colors.blue),
183 | )),
184 | ],
185 | ),
186 | Row(
187 | children: [
188 | FlatButton(
189 | onPressed: () {
190 | _controller = TencentPlayerController.network(mu);
191 | _controller.initialize().then((_) {
192 | setState(() {});
193 | });
194 | _controller.addListener(listener);
195 | },
196 | child: Text(
197 | 'm3u8点播',
198 | style: TextStyle(
199 | color: _controller.dataSource == videoUrlAAA
200 | ? Colors.red
201 | : Colors.blue),
202 | )),
203 | FlatButton(
204 | onPressed: () {
205 | _controller = TencentPlayerController.network(spe1);
206 | _controller.initialize().then((_) {
207 | setState(() {});
208 | });
209 | _controller.addListener(listener);
210 | },
211 | child: Text(
212 | '普通点播',
213 | style: TextStyle(
214 | color: _controller.dataSource == videoUrlBBB
215 | ? Colors.red
216 | : Colors.blue),
217 | ),
218 | ),
219 | ],
220 | ),
221 | Row(
222 | children: [
223 | Container(
224 | margin: EdgeInsets.only(left: 15),
225 | child: Text(
226 | 'm3u8点播 : ',
227 | style: TextStyle(color: Colors.orange),
228 | ),
229 | ),
230 | FlatButton(
231 | child: Text(
232 | '标',
233 | style: TextStyle(
234 | color: _controller.value.bitrateIndex == 0
235 | ? Colors.yellow
236 | : Colors.green),
237 | ),
238 | onPressed: () {
239 | _controller.setBitrateIndex(0);
240 | },
241 | ),
242 | FlatButton(
243 | child: Text(
244 | '高',
245 | style: TextStyle(
246 | color: _controller.value.bitrateIndex == 1
247 | ? Colors.yellow
248 | : Colors.green),
249 | ),
250 | onPressed: () {
251 | _controller.setBitrateIndex(1);
252 | },
253 | ),
254 | FlatButton(
255 | child: Text(
256 | '超',
257 | style: TextStyle(
258 | color: _controller.value.bitrateIndex == 2
259 | ? Colors.yellow
260 | : Colors.green),
261 | ),
262 | onPressed: () {
263 | _controller.setBitrateIndex(2);
264 | },
265 | ),
266 | ],
267 | ),
268 | Row(
269 | children: [
270 | Container(
271 | margin: EdgeInsets.only(left: 15),
272 | child: Text(
273 | '普通点播 : ',
274 | style: TextStyle(color: Colors.orange),
275 | ),
276 | ),
277 | FlatButton(
278 | onPressed: () {
279 | _controller = TencentPlayerController.network(
280 | spe1,
281 | playerConfig: PlayerConfig(
282 | startTime: _controller
283 | .value.position.inSeconds));
284 | _controller.initialize().then((_) {
285 | setState(() {});
286 | });
287 | _controller.addListener(listener);
288 | },
289 | child: Text(
290 | '标',
291 | style: TextStyle(
292 | color: _controller.dataSource == videoUrlB
293 | ? Colors.red
294 | : Colors.blue),
295 | )),
296 | FlatButton(
297 | onPressed: () {
298 | _controller = TencentPlayerController.network(
299 | spe2,
300 | playerConfig: PlayerConfig(
301 | startTime: _controller
302 | .value.position.inSeconds));
303 | _controller.initialize().then((_) {
304 | setState(() {});
305 | });
306 | _controller.addListener(listener);
307 | },
308 | child: Text(
309 | '高',
310 | style: TextStyle(
311 | color: _controller.dataSource == videoUrlG
312 | ? Colors.red
313 | : Colors.blue),
314 | )),
315 | FlatButton(
316 | onPressed: () {
317 | _controller = TencentPlayerController.network(spe3,
318 | playerConfig: PlayerConfig(
319 | startTime:
320 | _controller.value.position.inSeconds));
321 | _controller.initialize().then((_) {
322 | setState(() {});
323 | });
324 | _controller.addListener(listener);
325 | },
326 | child: Text(
327 | '超',
328 | style: TextStyle(
329 | color: _controller.dataSource == videoUrl
330 | ? Colors.red
331 | : Colors.blue),
332 | ),
333 | ),
334 | ],
335 | ),
336 | Row(
337 | children: [
338 | FlatButton(
339 | onPressed: () {
340 | _downloadController.dowload("4564972819220421305",
341 | quanlity: 2);
342 | },
343 | child: Text(
344 | 'download1',
345 | style: TextStyle(color: Colors.blue),
346 | ),
347 | ),
348 | FlatButton(
349 | onPressed: () {
350 | _downloadController
351 | .pauseDownload("4564972819220421305");
352 | },
353 | child: Text(
354 | 'download1 - stop',
355 | style: TextStyle(color: Colors.blue),
356 | ),
357 | ),
358 | ],
359 | ),
360 | Row(
361 | children: [
362 | FlatButton(
363 | onPressed: () {
364 | _downloadController.dowload(testDownload);
365 | },
366 | child: Text(
367 | 'download2',
368 | style: TextStyle(color: Colors.blue),
369 | ),
370 | ),
371 | FlatButton(
372 | onPressed: () {
373 | _downloadController.pauseDownload(testDownload);
374 | },
375 | child: Text(
376 | 'download2 - stop',
377 | style: TextStyle(color: Colors.blue),
378 | ),
379 | ),
380 | ],
381 | ),
382 | Text(_downloadController.value != null
383 | ? _downloadController.value.toString()
384 | : '')
385 | ],
386 | ),
387 | )
388 | ],
389 | ),
390 | ),
391 | floatingActionButton: FloatingActionButton(
392 | onPressed: () {
393 | setState(() {
394 | _controller.value.isPlaying
395 | ? _controller.pause()
396 | : _controller.play();
397 | });
398 | },
399 | child: Icon(
400 | _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
401 | ),
402 | ),
403 | ),
404 | );
405 | }
406 | }
407 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.flutter-io.cn"
9 | source: hosted
10 | version: "2.3.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.flutter-io.cn"
16 | source: hosted
17 | version: "1.0.5"
18 | charcode:
19 | dependency: transitive
20 | description:
21 | name: charcode
22 | url: "https://pub.flutter-io.cn"
23 | source: hosted
24 | version: "1.1.2"
25 | collection:
26 | dependency: transitive
27 | description:
28 | name: collection
29 | url: "https://pub.flutter-io.cn"
30 | source: hosted
31 | version: "1.14.11"
32 | cupertino_icons:
33 | dependency: "direct main"
34 | description:
35 | name: cupertino_icons
36 | url: "https://pub.flutter-io.cn"
37 | source: hosted
38 | version: "0.1.3"
39 | flutter:
40 | dependency: "direct main"
41 | description: flutter
42 | source: sdk
43 | version: "0.0.0"
44 | flutter_tencentplayer_plus:
45 | dependency: "direct dev"
46 | description:
47 | path: ".."
48 | relative: true
49 | source: path
50 | version: "0.0.1"
51 | flutter_test:
52 | dependency: "direct dev"
53 | description: flutter
54 | source: sdk
55 | version: "0.0.0"
56 | matcher:
57 | dependency: transitive
58 | description:
59 | name: matcher
60 | url: "https://pub.flutter-io.cn"
61 | source: hosted
62 | version: "0.12.5"
63 | meta:
64 | dependency: transitive
65 | description:
66 | name: meta
67 | url: "https://pub.flutter-io.cn"
68 | source: hosted
69 | version: "1.1.7"
70 | path:
71 | dependency: transitive
72 | description:
73 | name: path
74 | url: "https://pub.flutter-io.cn"
75 | source: hosted
76 | version: "1.6.4"
77 | pedantic:
78 | dependency: transitive
79 | description:
80 | name: pedantic
81 | url: "https://pub.flutter-io.cn"
82 | source: hosted
83 | version: "1.8.0+1"
84 | quiver:
85 | dependency: transitive
86 | description:
87 | name: quiver
88 | url: "https://pub.flutter-io.cn"
89 | source: hosted
90 | version: "2.0.5"
91 | sky_engine:
92 | dependency: transitive
93 | description: flutter
94 | source: sdk
95 | version: "0.0.99"
96 | source_span:
97 | dependency: transitive
98 | description:
99 | name: source_span
100 | url: "https://pub.flutter-io.cn"
101 | source: hosted
102 | version: "1.5.5"
103 | stack_trace:
104 | dependency: transitive
105 | description:
106 | name: stack_trace
107 | url: "https://pub.flutter-io.cn"
108 | source: hosted
109 | version: "1.9.3"
110 | stream_channel:
111 | dependency: transitive
112 | description:
113 | name: stream_channel
114 | url: "https://pub.flutter-io.cn"
115 | source: hosted
116 | version: "2.0.0"
117 | string_scanner:
118 | dependency: transitive
119 | description:
120 | name: string_scanner
121 | url: "https://pub.flutter-io.cn"
122 | source: hosted
123 | version: "1.0.5"
124 | term_glyph:
125 | dependency: transitive
126 | description:
127 | name: term_glyph
128 | url: "https://pub.flutter-io.cn"
129 | source: hosted
130 | version: "1.1.0"
131 | test_api:
132 | dependency: transitive
133 | description:
134 | name: test_api
135 | url: "https://pub.flutter-io.cn"
136 | source: hosted
137 | version: "0.2.5"
138 | typed_data:
139 | dependency: transitive
140 | description:
141 | name: typed_data
142 | url: "https://pub.flutter-io.cn"
143 | source: hosted
144 | version: "1.1.6"
145 | vector_math:
146 | dependency: transitive
147 | description:
148 | name: vector_math
149 | url: "https://pub.flutter-io.cn"
150 | source: hosted
151 | version: "2.0.8"
152 | sdks:
153 | dart: ">=2.2.2 <3.0.0"
154 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_tencentplayer_plus_example
2 | description: Demonstrates how to use the flutter_tencentplayer_plus plugin.
3 | publish_to: 'none'
4 |
5 | environment:
6 | sdk: ">=2.1.0 <3.0.0"
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 |
12 | # The following adds the Cupertino Icons font to your application.
13 | # Use with the CupertinoIcons class for iOS style icons.
14 | cupertino_icons: ^0.1.2
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 |
20 | flutter_tencentplayer_plus:
21 | path: ../
22 |
23 | # For information on the generic Dart part of this file, see the
24 | # following page: https://dart.dev/tools/pub/pubspec
25 |
26 | # The following section is specific to Flutter.
27 | flutter:
28 |
29 | # The following line ensures that the Material Icons font is
30 | # included with your application, so that you can use the icons in
31 | # the material Icons class.
32 | uses-material-design: true
33 |
34 | # To add assets to your application, add an assets section, like this:
35 | # assets:
36 | # - images/a_dot_burr.jpeg
37 | # - images/a_dot_ham.jpeg
38 |
39 | # An image asset can refer to one or more resolution-specific "variants", see
40 | # https://flutter.dev/assets-and-images/#resolution-aware.
41 |
42 | # For details regarding adding assets from package dependencies, see
43 | # https://flutter.dev/assets-and-images/#from-packages
44 |
45 | # To add custom fonts to your application, add a fonts section here,
46 | # in this "flutter" section. Each entry in this list should have a
47 | # "family" key with the font family name, and a "fonts" key with a
48 | # list giving the asset and other descriptors for the font. For
49 | # example:
50 | # fonts:
51 | # - family: Schyler
52 | # fonts:
53 | # - asset: fonts/Schyler-Regular.ttf
54 | # - asset: fonts/Schyler-Italic.ttf
55 | # style: italic
56 | # - family: Trajan Pro
57 | # fonts:
58 | # - asset: fonts/TrajanPro.ttf
59 | # - asset: fonts/TrajanPro_Bold.ttf
60 | # weight: 700
61 | #
62 | # For details regarding fonts from package dependencies,
63 | # see https://flutter.dev/custom-fonts/#from-packages
64 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:flutter_tencentplayer_plus_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) => widget is Text &&
22 | widget.data.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/flutter_tencentplayer_plus.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 | /Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/FLTDownLoadManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // FLTDownLoadManager.h
3 | // flutter_tencentplayer
4 | //
5 | // Created by wilson on 2019/8/16.
6 | //
7 |
8 | #import
9 | #import
10 | #import "TXLiteAVSDK.h"
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface FLTDownLoadManager : NSObject
14 |
15 |
16 | @property(nonatomic) FlutterEventSink eventSink;
17 | @property(nonatomic) FlutterEventChannel* eventChannel;
18 | @property(nonatomic) FlutterResult result;
19 | @property(nonatomic) FlutterMethodCall* call;
20 | @property(nonatomic) NSString* path;
21 | @property(nonatomic) NSString* urlOrFileId;
22 | @property(nonatomic) TXVodDownloadManager* tXVodDownloadManager;
23 | @property(nonatomic) TXVodDownloadMediaInfo* tempMedia;
24 |
25 | - (instancetype)initWithMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
26 |
27 | //下载的方法
28 | - (void)downLoad;
29 | //停止下载的方法
30 | - (void)stopDownLoad;
31 |
32 | @end
33 |
34 | NS_ASSUME_NONNULL_END
35 |
--------------------------------------------------------------------------------
/ios/Classes/FLTDownLoadManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // FLTDownLoadManager.m
3 | // flutter_tencentplayer
4 | //
5 | // Created by wilson on 2019/8/16.
6 | //
7 |
8 | #import "FLTDownLoadManager.h"
9 |
10 | @implementation FLTDownLoadManager
11 |
12 |
13 | - (instancetype)initWithMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result{
14 | _call = call;
15 | _result = result;
16 |
17 | // [_eventChannel setStreamHandler:self];
18 | NSDictionary* argsMap = _call.arguments;
19 | _path = argsMap[@"savePath"];
20 |
21 | NSLog(@"下载地址 %@", _path);
22 | _urlOrFileId = argsMap[@"urlOrFileId"];
23 | if (_tXVodDownloadManager == nil) {
24 | _tXVodDownloadManager = [TXVodDownloadManager shareInstance];
25 | // NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
26 | // NSString *docPath = [paths lastObject];
27 | // NSString *downloadPath = [docPath stringByAppendingString:@"/downloader" ];
28 | //// NSLog(downloadPath);
29 | [_tXVodDownloadManager setDownloadPath:_path];
30 | }
31 | _tXVodDownloadManager.delegate = self;
32 | return self;
33 | }
34 |
35 | //开始下载
36 | - (void)downLoad{
37 | //设置下载对象
38 | NSLog(@"开始下载");
39 | if([_urlOrFileId hasPrefix: @"http"]){
40 | _tempMedia = [_tXVodDownloadManager startDownloadUrl:_urlOrFileId];
41 | }else{
42 | NSDictionary* argsMap = _call.arguments;
43 | int appId = [argsMap[@"appId"] intValue];
44 | int quanlity = [argsMap[@"quanlity"] intValue];
45 | _urlOrFileId = argsMap[@"urlOrFileId"];
46 | TXPlayerAuthParams *auth = [TXPlayerAuthParams new];
47 | auth.appId =appId;
48 | auth.fileId = _urlOrFileId;
49 | TXVodDownloadDataSource *dataSource = [TXVodDownloadDataSource new];
50 | dataSource.auth = auth;
51 | dataSource.templateName = @"HLS-标清-SD";
52 | if (quanlity == 2) {
53 | dataSource.templateName = @"HLS-标清-SD";
54 | } else if (quanlity == 3) {
55 | dataSource.templateName = @"HLS-高清-HD";
56 | } else if (quanlity == 4) {
57 | dataSource.templateName = @"HLS-全高清-FHD";
58 | }
59 | _tempMedia = [_tXVodDownloadManager startDownload:dataSource];
60 | }
61 |
62 | }
63 |
64 |
65 | //停止下载
66 | - (void)stopDownLoad{
67 | NSLog(@"停止下载");
68 | [_tXVodDownloadManager stopDownload:_tempMedia];
69 | }
70 |
71 | // ---------------通信相关
72 | - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
73 | _eventSink = nil;
74 |
75 | NSLog(@"FLTDownLoadManager停止通信");
76 | return nil;
77 | }
78 |
79 | - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
80 | _eventSink = events;
81 | NSLog(@"FLTDownLoadManager设置全局通信");
82 | return nil;
83 | }
84 |
85 |
86 |
87 | //----------------下载回调相关
88 |
89 | - (void)onDownloadStart:(TXVodDownloadMediaInfo *)mediaInfo {
90 | [self dealCallToFlutterData:@"start" mediaInfo:mediaInfo ];
91 | }
92 |
93 | - (void)onDownloadProgress:(TXVodDownloadMediaInfo *)mediaInfo {
94 |
95 | [self dealCallToFlutterData:@"progress" mediaInfo:mediaInfo ];
96 | }
97 |
98 | - (void)onDownloadStop:(TXVodDownloadMediaInfo *)mediaInfo {
99 |
100 | [self dealCallToFlutterData:@"stop" mediaInfo:mediaInfo ];
101 |
102 | }
103 | - (void)onDownloadFinish:(TXVodDownloadMediaInfo *)mediaInfo {
104 |
105 | [self dealCallToFlutterData:@"complete" mediaInfo:mediaInfo ];
106 | }
107 |
108 | - (void)onDownloadError:(TXVodDownloadMediaInfo *)mediaInfo errorCode:(TXDownloadError)code errorMsg:(NSString *)msg {
109 |
110 | NSLog(@"onDownloadError");
111 |
112 | NSString *quality = [NSString stringWithFormat:@"%ld",(long)mediaInfo.dataSource.quality];
113 | NSString *duration = [NSString stringWithFormat:@"%d",mediaInfo.duration];
114 | NSString *size = [NSString stringWithFormat:@"%d",mediaInfo.size];
115 | NSString *downloadSize = [NSString stringWithFormat:@"%d",mediaInfo.downloadSize];
116 | NSString *progress = [NSString stringWithFormat:@"%f",mediaInfo.progress];
117 | if (mediaInfo.dataSource!=nil) {
118 | if(self->_eventSink!=nil){
119 | NSMutableDictionary* paramDic = [NSMutableDictionary dictionary];
120 | [paramDic setValue:@"error" forKey:@"downloadStatus"];
121 | [paramDic setValue:quality forKey:@"quanlity"];
122 | [paramDic setValue:duration forKey:@"duration"];
123 | [paramDic setValue:size forKey:@"size"];
124 | [paramDic setValue:downloadSize forKey:@"downloadSize"];
125 | [paramDic setValue:progress forKey:@"progress"];
126 | [paramDic setValue:mediaInfo.playPath forKey:@"playPath"];
127 | [paramDic setValue:@(true) forKey:@"isStop"];
128 | [paramDic setValue:mediaInfo.url forKey:@"url"];
129 | [paramDic setValue:mediaInfo.dataSource.auth.fileId forKey:@"fileId"];
130 | [paramDic setValue:msg forKey:@"error"];
131 |
132 | self->_eventSink(paramDic);
133 | }
134 | }
135 | }
136 |
137 | - (int)hlsKeyVerify:(TXVodDownloadMediaInfo *)mediaInfo url:(NSString *)url data:(NSData *)data {
138 | NSLog(@"停止下载");
139 | return 0;
140 | }
141 |
142 | - (void)dealCallToFlutterData:(NSString*)type mediaInfo:(TXVodDownloadMediaInfo *)mediaInfo {
143 | NSLog(@"下载类型");
144 |
145 | NSString *quality = [NSString stringWithFormat:@"%ld",(long)mediaInfo.dataSource.quality];
146 | NSString *duration = [NSString stringWithFormat:@"%d",mediaInfo.duration];
147 | NSString *size = [NSString stringWithFormat:@"%d",mediaInfo.size];
148 | NSString *downloadSize = [NSString stringWithFormat:@"%d",mediaInfo.downloadSize];
149 | NSString *progress = [NSString stringWithFormat:@"%f",mediaInfo.progress];
150 |
151 | if (mediaInfo.dataSource!=nil) {
152 | // [mediaInfo.dataSource auth];
153 | if(self->_eventSink!=nil){
154 | self->_eventSink(@{
155 | @"downloadStatus":type,
156 | @"quanlity":quality ,
157 | @"duration":duration ,
158 | @"size":size ,
159 | @"downloadSize":downloadSize ,
160 | @"progress":progress ,
161 | @"playPath":mediaInfo.playPath ,
162 | @"isStop":@(true) ,
163 | @"url":mediaInfo.url ,
164 | @"fileId":mediaInfo.dataSource.auth.fileId,
165 | @"error":@"error" ,
166 |
167 | });
168 | }
169 | }
170 |
171 | }
172 |
173 |
174 |
175 |
176 |
177 | @end
178 |
--------------------------------------------------------------------------------
/ios/Classes/FLTFrameUpdater.h:
--------------------------------------------------------------------------------
1 | //
2 | // FLTFrameUpdater.h
3 | // flutter_plugin_demo3
4 | //
5 | // Created by Wei on 2019/5/15.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface FLTFrameUpdater : NSObject
14 | @property(nonatomic) int64_t textureId;
15 | @property(nonatomic, readonly) NSObject* registry;
16 |
17 | -(void)refreshDisplay;
18 | - (FLTFrameUpdater*)initWithRegistry:(NSObject*)registry;
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/ios/Classes/FLTFrameUpdater.m:
--------------------------------------------------------------------------------
1 | //
2 | // FLTFrameUpdater.m
3 | // flutter_plugin_demo3
4 | //
5 | // Created by Wei on 2019/5/15.
6 | //
7 |
8 | #import "FLTFrameUpdater.h"
9 |
10 | @implementation FLTFrameUpdater
11 | - (FLTFrameUpdater*)initWithRegistry:(NSObject*)registry {
12 | NSAssert(self, @"super init cannot be nil");
13 | if (self == nil) return nil;
14 | _registry = registry;
15 | return self;
16 | }
17 |
18 | -(void)refreshDisplay{
19 | [_registry textureFrameAvailable:self.textureId];
20 | }
21 | @end
22 |
--------------------------------------------------------------------------------
/ios/Classes/FLTVideoPlayer.h:
--------------------------------------------------------------------------------
1 | //
2 | // FLTVideoPlayer.h
3 | // flutter_plugin_demo3
4 | //
5 | // Created by Wei on 2019/5/15.
6 | //
7 |
8 | #import
9 | #import
10 | #import
11 | #import "FLTFrameUpdater.h"
12 | #import "TXLiteAVSDK.h"
13 | #import
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface FLTVideoPlayer : NSObject
17 | @property(readonly,nonatomic) TXVodPlayer* txPlayer;
18 | @property(nonatomic) FlutterEventChannel* eventChannel;
19 |
20 | //ios主动和flutter通信
21 | @property(nonatomic) FlutterEventSink eventSink;
22 | @property(nonatomic, readonly) bool disposed;
23 | @property(nonatomic, readonly) int64_t textureId;
24 |
25 | /**
26 | * 是否循环播放
27 | */
28 | @property (nonatomic, assign) BOOL loop;
29 | @property(nonatomic)FLTFrameUpdater* frameUpdater;
30 |
31 | - (instancetype)initWithCall:(FlutterMethodCall*)call
32 | frameUpdater:(FLTFrameUpdater*)frameUpdater
33 | registry:(NSObject*)registry
34 | messenger:(NSObject*)messenger;
35 | - (void)dispose;
36 | -(void)resume;
37 | -(void)pause;
38 | -(int64_t)position;
39 | -(int64_t)duration;
40 | -(void)seekTo:(int)position;
41 | /**
42 | * 设置播放开始时间
43 | * 在startPlay前设置,修改开始播放的起始位置
44 | */
45 | - (void)setStartTime:(CGFloat)startTime;
46 |
47 | /**
48 | * 停止播放音视频流
49 | * @return 0 = OK
50 | */
51 | - (int)stopPlay;
52 | /**
53 | * 可播放时长
54 | */
55 | - (float)playableDuration;
56 | /**
57 | * 视频宽度
58 | */
59 | - (int)width;
60 |
61 | /**
62 | * 视频高度
63 | */
64 | - (int)height;
65 | /**
66 | * 设置画面的方向
67 | * @param rotation 方向
68 | * @see TX_Enum_Type_HomeOrientation
69 | */
70 | - (void)setRenderRotation:(TX_Enum_Type_HomeOrientation)rotation;
71 | /**
72 | * 设置画面的裁剪模式
73 | * @param renderMode 裁剪
74 | * @see TX_Enum_Type_RenderMode
75 | */
76 | - (void)setRenderMode:(TX_Enum_Type_RenderMode)renderMode;
77 | /**
78 | * 设置静音
79 | */
80 | - (void)setMute:(BOOL)bEnable;
81 |
82 | /*
83 | * 截屏
84 | * @param snapshotCompletionBlock 通过回调返回当前图像
85 | */
86 | - (void)snapshot:(void (^)(UIImage *))snapshotCompletionBlock;
87 | /**
88 | * 设置播放速率
89 | * @param rate 正常速度为1.0;小于为慢速;大于为快速。最大建议不超过2.0
90 | */
91 | - (void)setRate:(float)rate;
92 | // 设置播放清晰度
93 | - (void)setBitrateIndex:(int)index;
94 | /**
95 | * 设置画面镜像
96 | */
97 | - (void)setMirror:(BOOL)isMirror;
98 |
99 | @end
100 |
101 | NS_ASSUME_NONNULL_END
102 |
--------------------------------------------------------------------------------
/ios/Classes/FLTVideoPlayer.m:
--------------------------------------------------------------------------------
1 | //
2 | // FLTVideoPlayer.m
3 | // flutter_plugin_demo3
4 | //
5 | // Created by Wei on 2019/5/15.
6 | //
7 |
8 | #import "FLTVideoPlayer.h"
9 | #import
10 |
11 |
12 |
13 | @implementation FLTVideoPlayer{
14 | // CVPixelBufferRef finalPiexelBuffer;
15 | // CVPixelBufferRef pixelBufferNowRef;
16 | CVPixelBufferRef volatile _latestPixelBuffer;
17 | CVPixelBufferRef _lastBuffer;
18 | }
19 |
20 | - (instancetype)initWithCall:(FlutterMethodCall *)call frameUpdater:(FLTFrameUpdater *)frameUpdater registry:(NSObject *)registry messenger:(NSObject*)messenger{
21 | self = [super init];
22 | _latestPixelBuffer = nil;
23 | _lastBuffer = nil;
24 | // NSLog(@"FLTVideo 初始化播放器");
25 | _textureId = [registry registerTexture:self];
26 | // NSLog(@"FLTVideo _textureId %lld",_textureId);
27 |
28 | FlutterEventChannel* eventChannel = [FlutterEventChannel
29 | eventChannelWithName:[NSString stringWithFormat:@"flutter_tencentplayer/videoEvents%lld",_textureId]
30 | binaryMessenger:messenger];
31 |
32 |
33 |
34 | _eventChannel = eventChannel;
35 | [_eventChannel setStreamHandler:self];
36 | NSDictionary* argsMap = call.arguments;
37 | TXVodPlayConfig* playConfig = [[TXVodPlayConfig alloc]init];
38 | playConfig.connectRetryCount= 3 ;
39 | playConfig.connectRetryInterval = 3;
40 | playConfig.timeout = 10 ;
41 |
42 |
43 |
44 | id headers = argsMap[@"headers"];
45 | if (headers!=nil&&headers!=NULL&&![@"" isEqualToString:headers]&&headers!=[NSNull null]) {
46 | NSDictionary* headers = argsMap[@"headers"];
47 | playConfig.headers = headers;
48 | }
49 |
50 | id cacheFolderPath = argsMap[@"cachePath"];
51 | if (cacheFolderPath!=nil&&cacheFolderPath!=NULL&&![@"" isEqualToString:cacheFolderPath]&&cacheFolderPath!=[NSNull null]) {
52 | playConfig.cacheFolderPath = cacheFolderPath;
53 | playConfig.maxCacheItems = 20;
54 | }else{
55 | // 设置缓存路径
56 | playConfig.cacheFolderPath =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
57 | playConfig.maxCacheItems = 10;
58 | }
59 |
60 | playConfig.progressInterval = 1;
61 | playConfig.maxBufferSize=4;
62 | //[argsMap[@"progressInterval"] intValue] ;
63 | BOOL autoPlayArg = [argsMap[@"autoPlay"] boolValue];
64 | float startPosition=0;
65 |
66 | id startTime = argsMap[@"startTime"];
67 | if(startTime!=nil&&startTime!=NULL&&![@"" isEqualToString:startTime]&&startTime!=[NSNull null]){
68 | startPosition =[argsMap[@"startTime"] floatValue];
69 | }
70 |
71 | frameUpdater.textureId = _textureId;
72 | _frameUpdater = frameUpdater;
73 |
74 | _txPlayer = [[TXVodPlayer alloc]init];
75 | [playConfig setPlayerPixelFormatType:kCVPixelFormatType_32BGRA];
76 | [_txPlayer setConfig:playConfig];
77 | [_txPlayer setIsAutoPlay:autoPlayArg];
78 | _txPlayer.enableHWAcceleration = YES;
79 | [_txPlayer setVodDelegate:self];
80 | [_txPlayer setVideoProcessDelegate:self];
81 | [_txPlayer setStartTime:startPosition];
82 |
83 | BOOL loop = [argsMap[@"loop"] boolValue];
84 | [_txPlayer setLoop: loop];
85 |
86 | id pathArg = argsMap[@"uri"];
87 | if(pathArg!=nil&&pathArg!=NULL&&![@"" isEqualToString:pathArg]&&pathArg!=[NSNull null]){
88 | NSLog(@"播放器启动方式1 play");
89 | [_txPlayer startPlay:pathArg];
90 | }else{
91 | NSLog(@"播放器启动方式2 fileid");
92 | id auth = argsMap[@"auth"];
93 | if(auth!=nil&&auth!=NULL&&![@"" isEqualToString:auth]&&auth!=[NSNull null]){
94 | NSDictionary* authMap = argsMap[@"auth"];
95 | int appId= [authMap[@"appId"] intValue];
96 | NSString *fileId= authMap[@"fileId"];
97 | TXPlayerAuthParams *p = [TXPlayerAuthParams new];
98 | p.appId = appId;
99 | p.fileId = fileId;
100 | [_txPlayer startPlayWithParams:p];
101 | }
102 | }
103 | NSLog(@"播放器初始化结束");
104 |
105 |
106 | return self;
107 |
108 | }
109 |
110 |
111 | #pragma FlutterTexture
112 | - (CVPixelBufferRef)copyPixelBuffer {
113 | CVPixelBufferRef pixelBuffer = _latestPixelBuffer;
114 | while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, nil,
115 | (void **)&_latestPixelBuffer)) {
116 | pixelBuffer = _latestPixelBuffer;
117 | }
118 | return pixelBuffer;
119 | }
120 |
121 | #pragma 腾讯播放器代理回调方法
122 | - (BOOL)onPlayerPixelBuffer:(CVPixelBufferRef)pixelBuffer{
123 |
124 | if (_lastBuffer == nil) {
125 | _lastBuffer = CVPixelBufferRetain(pixelBuffer);
126 | CFRetain(pixelBuffer);
127 | } else if (_lastBuffer != pixelBuffer) {
128 | CVPixelBufferRelease(_lastBuffer);
129 | _lastBuffer = CVPixelBufferRetain(pixelBuffer);
130 | CFRetain(pixelBuffer);
131 | }
132 |
133 | CVPixelBufferRef newBuffer = pixelBuffer;
134 |
135 | CVPixelBufferRef old = _latestPixelBuffer;
136 | while (!OSAtomicCompareAndSwapPtrBarrier(old, newBuffer,
137 | (void **)&_latestPixelBuffer)) {
138 | old = _latestPixelBuffer;
139 | }
140 |
141 | if (old && old != pixelBuffer) {
142 | CFRelease(old);
143 | }
144 | [self.frameUpdater refreshDisplay];
145 | return NO;
146 | }
147 |
148 | /**
149 | * 点播事件通知
150 | *
151 | * @param player 点播对象
152 | * @param EvtID 参见TXLiveSDKEventDef.h
153 | * @param param 参见TXLiveSDKTypeDef.h
154 | * @see TXVodPlayer
155 | */
156 | -(void)onPlayEvent:(TXVodPlayer *)player event:(int)EvtID withParam:(NSDictionary *)param{
157 |
158 | dispatch_async(dispatch_get_main_queue(), ^{
159 |
160 | if(EvtID==PLAY_EVT_VOD_PLAY_PREPARED){
161 | int64_t duration = [player duration];
162 | NSString *durationStr = [NSString stringWithFormat: @"%ld", (long)duration];
163 | NSInteger durationInt = [durationStr intValue];
164 | if(self->_eventSink!=nil){
165 | self->_eventSink(@{
166 | @"event":@"initialized",
167 | @"duration":@(durationInt),
168 | @"width":@([player width]),
169 | @"height":@([player height])
170 | });
171 | }
172 |
173 |
174 | }else if(EvtID==PLAY_EVT_PLAY_PROGRESS){
175 | int64_t progress = [player currentPlaybackTime];
176 | int64_t duration = [player duration];
177 | int64_t playableDuration = [player playableDuration];
178 | // NSString *progressStr = [NSString stringWithFormat: @"%ld", (long)progress];
179 | // NSString *durationStr = [NSString stringWithFormat: @"%ld", (long)duration];
180 | // NSString *playableDurationStr = [NSString stringWithFormat: @"%ld", (long)playableDuration];
181 | // long progressInt = [progress intValue]*1000;
182 | // long durationint = [duration intValue]*1000;
183 | // long playableDurationInt = [playableDuration intValue]*1000;
184 | // NSLog(@"单精度浮点数: %d",progressInt);
185 | // NSLog(@"单精度浮点数: %d",durationint);
186 | if(self->_eventSink!=nil){
187 | self->_eventSink(@{
188 | @"event":@"progress",
189 | @"progress":@(progress*1000),
190 | @"duration":@(duration*1000),
191 | @"playable":@(playableDuration*1000)
192 | });
193 | }
194 |
195 | }else if(EvtID==PLAY_EVT_PLAY_LOADING){
196 | if(self->_eventSink!=nil){
197 | self->_eventSink(@{
198 | @"event":@"loading",
199 | });
200 | }
201 |
202 | }else if(EvtID==PLAY_EVT_VOD_LOADING_END){
203 | if(self->_eventSink!=nil){
204 | self->_eventSink(@{
205 | @"event":@"loadingend",
206 | });
207 | }
208 |
209 | }else if(EvtID==PLAY_EVT_PLAY_END){
210 | if(self->_eventSink!=nil){
211 | self->_eventSink(@{
212 | @"event":@"playend",
213 | });
214 | }
215 |
216 | }else if(EvtID==PLAY_ERR_NET_DISCONNECT){
217 | if(self->_eventSink!=nil){
218 | self->_eventSink(@{
219 | @"event":@"error",
220 | @"errorInfo":param[@"EVT_MSG"],
221 | });
222 |
223 | self->_eventSink(@{
224 | @"event":@"disconnect",
225 | });
226 |
227 | }
228 |
229 | }else if(EvtID==ERR_PLAY_LIVE_STREAM_NET_DISCONNECT){
230 | if(self->_eventSink!=nil){
231 | self->_eventSink(@{
232 | @"event":@"error",
233 | @"errorInfo":param[@"EVT_MSG"],
234 | });
235 | }
236 | }else if(EvtID==WARNING_LIVE_STREAM_SERVER_RECONNECT){
237 | if(self->_eventSink!=nil){
238 | self->_eventSink(@{
239 | @"event":@"error",
240 | @"errorInfo":param[@"EVT_MSG"],
241 | });
242 | }
243 | }else {
244 | if(EvtID<0){
245 | if(self->_eventSink!=nil){
246 | self->_eventSink(@{
247 | @"event":@"error",
248 | @"errorInfo":param[@"EVT_MSG"],
249 | });
250 | }
251 | }
252 | }
253 |
254 | });
255 | }
256 |
257 | - (void)onNetStatus:(TXVodPlayer *)player withParam:(NSDictionary *)param {
258 | if(self->_eventSink!=nil){
259 | self->_eventSink(@{
260 | @"event":@"netStatus",
261 | @"netSpeed": param[NET_STATUS_NET_SPEED],
262 | @"cacheSize": param[NET_STATUS_V_SUM_CACHE_SIZE],
263 | });
264 | }
265 | }
266 |
267 | #pragma FlutterStreamHandler
268 | - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments {
269 | _eventSink = nil;
270 | NSLog(@"FLTVideo 停止通信");
271 | return nil;
272 | }
273 |
274 | - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
275 | eventSink:(nonnull FlutterEventSink)events {
276 | _eventSink = events;
277 |
278 | NSLog(@"FLTVideo 开启通信");
279 | //[self sendInitialized];
280 | return nil;
281 | }
282 |
283 | - (void)dispose {
284 | _disposed = true;
285 | [self stopPlay];
286 | _txPlayer = nil;
287 | _frameUpdater = nil;
288 | NSLog(@"FLTVideo dispose");
289 | CVPixelBufferRef old = _latestPixelBuffer;
290 | while (!OSAtomicCompareAndSwapPtrBarrier(old, nil,
291 | (void **)&_latestPixelBuffer)) {
292 | old = _latestPixelBuffer;
293 | }
294 | if (old) {
295 | CFRelease(old);
296 | }
297 |
298 | if (_lastBuffer) {
299 | CVPixelBufferRelease(_lastBuffer);
300 | _lastBuffer = nil;
301 | }
302 |
303 | // if(_eventChannel){
304 | // [_eventChannel setStreamHandler:nil];
305 | // _eventChannel =nil;
306 | // }
307 |
308 | }
309 |
310 | -(void)setLoop:(BOOL)loop{
311 | [_txPlayer setLoop:loop];
312 | _loop = loop;
313 | }
314 |
315 | - (void)resume{
316 | [_txPlayer resume];
317 | }
318 | -(void)pause{
319 | [_txPlayer pause];
320 | }
321 | - (int64_t)position{
322 | return [_txPlayer currentPlaybackTime];
323 | }
324 |
325 | - (int64_t)duration{
326 | return [_txPlayer duration];
327 | }
328 |
329 | - (void)seekTo:(int)position{
330 | [_txPlayer seek:position];
331 | }
332 |
333 | - (void)setStartTime:(CGFloat)startTime{
334 | [_txPlayer setStartTime:startTime];
335 | }
336 |
337 | - (int)stopPlay{
338 | return [_txPlayer stopPlay];
339 | }
340 |
341 | - (float)playableDuration{
342 | return [_txPlayer playableDuration];
343 | }
344 |
345 | - (int)width{
346 | return [_txPlayer width];
347 | }
348 |
349 | - (int)height{
350 | return [_txPlayer height];
351 | }
352 |
353 | - (void)setRenderMode:(TX_Enum_Type_RenderMode)renderMode{
354 | [_txPlayer setRenderMode:renderMode];
355 | }
356 |
357 | - (void)setRenderRotation:(TX_Enum_Type_HomeOrientation)rotation{
358 |
359 | [_txPlayer setRenderRotation:rotation];
360 | }
361 |
362 | - (void)setMute:(BOOL)bEnable{
363 | [_txPlayer setMute:bEnable];
364 | }
365 |
366 |
367 |
368 |
369 | - (void)setRate:(float)rate{
370 | [_txPlayer setRate:rate];
371 | }
372 |
373 | - (void)setBitrateIndex:(int)index{
374 | [_txPlayer setBitrateIndex:index];
375 | }
376 |
377 | - (void)setMirror:(BOOL)isMirror{
378 | [_txPlayer setMirror:isMirror];
379 | }
380 |
381 | -(void)snapshot:(void (^)(UIImage * _Nonnull))snapshotCompletionBlock{
382 |
383 | }
384 |
385 | @end
386 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterTencentplayerPlusPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FlutterTencentplayerPlusPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterTencentplayerPlusPlugin.m:
--------------------------------------------------------------------------------
1 | #import "FlutterTencentplayerPlusPlugin.h"
2 |
3 | #import "FLTVideoPlayer.h"
4 | #import "FLTFrameUpdater.h"
5 | #import "FLTDownLoadManager.h"
6 |
7 | @interface FlutterTencentplayerPlusPlugin ()
8 |
9 | @property(readonly, nonatomic) NSObject* registry;
10 | @property(readonly, nonatomic) NSObject* messenger;
11 | @property(readonly, nonatomic) NSMutableDictionary* players;
12 | @property(readonly, nonatomic) NSMutableDictionary* downLoads;
13 | @property(readonly, nonatomic) NSObject* registrar;
14 |
15 |
16 |
17 |
18 | @end
19 |
20 |
21 | @implementation FlutterTencentplayerPlusPlugin
22 |
23 | NSObject* mRegistrar;
24 | //FLTVideoPlayer* player ;
25 |
26 |
27 | //第一次进来先执行的方法 注册插件
28 | - (instancetype)initWithRegistrar:(NSObject*)registrar {
29 | self = [super init];
30 | NSAssert(self, @"super init cannot be nil");
31 | _registry = [registrar textures];
32 | _messenger = [registrar messenger];
33 | _registrar = registrar;
34 | _players = [NSMutableDictionary dictionary];
35 | _downLoads = [NSMutableDictionary dictionaryWithCapacity:1];
36 | NSLog(@"FLTVideo initWithRegistrar");
37 | return self;
38 | }
39 |
40 | + (void)registerWithRegistrar:(NSObject*)registrar {
41 | FlutterMethodChannel* channel = [FlutterMethodChannel
42 | methodChannelWithName:@"flutter_tencentplayer"
43 | binaryMessenger:[registrar messenger]];
44 | // FlutterTencentplayerPlugin* instance = [[FlutterTencentplayerPlugin alloc] init];
45 | FlutterTencentplayerPlusPlugin* instance = [[FlutterTencentplayerPlusPlugin alloc] initWithRegistrar:registrar];
46 | NSLog(@"FLTVideo registerWithRegistrar");
47 | [registrar addMethodCallDelegate:instance channel:channel];
48 |
49 |
50 | }
51 |
52 | - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
53 | NSLog(@"FLTVideo call name %@",call.method);
54 | if ([@"init" isEqualToString:call.method]) {
55 | [self disposeAllPlayers];
56 | result(nil);
57 | }else if([@"create" isEqualToString:call.method]){
58 | // [self disposeAllPlayers];
59 | FLTFrameUpdater* frameUpdater = [[FLTFrameUpdater alloc] initWithRegistry:_registry];
60 | FLTVideoPlayer* player= [[FLTVideoPlayer alloc] initWithCall:call frameUpdater:frameUpdater registry:_registry messenger:_messenger];
61 |
62 | if (player) {
63 | [self onPlayerSetup:player frameUpdater:frameUpdater result:result];
64 | // NSString *textureIdStr = [NSString stringWithFormat: @"%lld",[player textureId]];
65 | //[_players setObject:player forKey:textureIdStr];
66 | }
67 | result(nil);
68 | }else if([@"download" isEqualToString:call.method]){
69 |
70 | NSDictionary* argsMap = call.arguments;
71 | NSString* urlOrFileId = argsMap[@"urlOrFileId"];
72 | NSLog(@"下载相关 startdownload %@", urlOrFileId);
73 |
74 | NSString* channelUrl =[NSString stringWithFormat:@"flutter_tencentplayer/downloadEvents%@",urlOrFileId];
75 | NSLog(@"%@", channelUrl);
76 | FlutterEventChannel* eventChannel = [FlutterEventChannel
77 | eventChannelWithName:channelUrl
78 | binaryMessenger:_messenger];
79 | FLTDownLoadManager* downLoadManager = [[FLTDownLoadManager alloc] initWithMethodCall:call result:result];
80 | [eventChannel setStreamHandler:downLoadManager];
81 | downLoadManager.eventChannel =eventChannel;
82 | [downLoadManager downLoad];
83 |
84 | _downLoads[urlOrFileId] = downLoadManager;
85 | NSLog(@"下载相关 start 数组大小 %lu", (unsigned long)_downLoads.count);
86 |
87 |
88 | result(nil);
89 | }else if([@"stopDownload" isEqualToString:call.method]){
90 | NSDictionary* argsMap = call.arguments;
91 | NSString* urlOrFileId = argsMap[@"urlOrFileId"];
92 | NSLog(@"下载相关 stopDownload %@", urlOrFileId);
93 | FLTDownLoadManager* downLoadManager = _downLoads[urlOrFileId];
94 | if(downLoadManager!=nil){
95 | [downLoadManager stopDownLoad];
96 | }else{
97 | NSLog(@"下载相关 对象为空 %lu", (unsigned long)_downLoads.count);
98 | }
99 |
100 |
101 |
102 | result(nil);
103 | }else {
104 | [self onMethodCall:call result:result];
105 | }
106 | }
107 |
108 | -(void) onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
109 |
110 | NSDictionary* argsMap = call.arguments;
111 | int64_t textureId = ((NSNumber*)argsMap[@"textureId"]).unsignedIntegerValue;
112 | if([NSNull null]==argsMap[@"textureId"]) {
113 | return;
114 | }
115 | // int64_t textureId = ((NSNumber*)argsMap[@"textureId"]).unsignedIntegerValue;
116 | //NSString *textureIdStr = [NSString stringWithFormat: @"%lld",textureId];
117 | //FLTVideoPlayer* player = _players[textureIdStr];
118 | FLTVideoPlayer* player = _players[@(textureId)];
119 |
120 | if([@"play" isEqualToString:call.method]){
121 | [player resume];
122 | result(nil);
123 | }else if([@"pause" isEqualToString:call.method]){
124 | [player pause];
125 | result(nil);
126 | }else if([@"seekTo" isEqualToString:call.method]){
127 | NSLog(@"跳转到指定位置----------");
128 | [player seekTo:[[argsMap objectForKey:@"location"] intValue]];
129 | result(nil);
130 | }else if([@"setRate" isEqualToString:call.method]){ //播放速率
131 | NSLog(@"修改播放速率----------");
132 | float rate = [[argsMap objectForKey:@"rate"] floatValue];
133 | if (rate<0||rate>2) {
134 | result(nil);
135 | return;
136 | }
137 | [player setRate:rate];
138 | result(nil);
139 |
140 | }else if([@"setBitrateIndex" isEqualToString:call.method]){
141 | NSLog(@"修改播放清晰度----------");
142 | int index = [[argsMap objectForKey:@"index"] intValue];
143 | [player setBitrateIndex:index];
144 | result(nil);
145 | }else if([@"dispose" isEqualToString:call.method]){
146 | [_registry unregisterTexture:textureId];
147 | [player dispose];
148 | player= nil;
149 | [_players removeObjectForKey:@(textureId)];
150 | result(nil);
151 | }else{
152 | result(FlutterMethodNotImplemented);
153 | }
154 |
155 | }
156 |
157 | - (void)onPlayerSetup:(FLTVideoPlayer*)player
158 | frameUpdater:(FLTFrameUpdater*)frameUpdater
159 | result:(FlutterResult)result {
160 | _players[@(player.textureId)] = player;
161 | result(@{@"textureId" : @(player.textureId)});
162 |
163 | }
164 | //
165 | //-(void) disposeAllPlayers{
166 | // NSLog(@"FLTVideo 初始化播放器状态----------");
167 | // // Allow audio playback when the Ring/Silent switch is set to silent
168 | // [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
169 | // if(player){
170 | // [player dispose];
171 | // player = nil;
172 | // }
173 | //}
174 |
175 |
176 | -(void) disposeAllPlayers{
177 | NSLog(@"初始化状态----------");
178 | // Allow audio playback when the Ring/Silent switch is set to silent
179 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
180 |
181 | for (NSNumber* textureId in _players) {
182 | [_registry unregisterTexture:[textureId unsignedIntegerValue]];
183 | [[_players objectForKey:textureId] dispose];
184 | }
185 | [_players removeAllObjects];
186 | }
187 |
188 | @end
189 |
190 |
--------------------------------------------------------------------------------
/ios/flutter_tencentplayer_plus.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
3 | #
4 | Pod::Spec.new do |s|
5 | s.name = 'flutter_tencentplayer_plus'
6 | s.version = '0.0.1'
7 | s.summary = 'flutter_plugin_flutter_tencentplayer_plus'
8 | s.description = <<-DESC
9 | flutter_plugin_flutter_tencentplayer_plus
10 | DESC
11 | s.homepage = 'http://example.com'
12 | s.license = { :file => '../LICENSE' }
13 | s.author = { 'Your Company' => 'email@example.com' }
14 | s.source = { :path => '.' }
15 | s.source_files = 'Classes/**/*'
16 | s.public_header_files = 'Classes/**/*.h'
17 | s.dependency 'Flutter'
18 | s.dependency 'TXLiteAVSDK_Player', '= 6.8.7969'
19 | s.user_target_xcconfig = { 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES' }
20 | s.ios.deployment_target = '8.0'
21 | end
22 |
23 |
--------------------------------------------------------------------------------
/lib/controller/download_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/services.dart';
4 | import 'package:flutter_tencentplayer_plus/flutter_tencentplayer_plus.dart';
5 |
6 | class DownloadController extends ValueNotifier> {
7 | final String savePath;
8 | final int appId;
9 | StreamSubscription _eventSubscription;
10 | MethodChannel channel = TencentPlayer.channel;
11 | bool _isDisposed = false;
12 |
13 | DownloadController(this.savePath, {this.appId})
14 | : super(Map());
15 |
16 | void dowload(String urlOrFileId, {int quanlity}) async {
17 | Map downloadInfoMap = {
18 | "savePath": savePath,
19 | "urlOrFileId": urlOrFileId,
20 | "appId": appId,
21 | "quanlity": quanlity,
22 | };
23 |
24 | await channel.invokeMethod(
25 | 'download',
26 | downloadInfoMap,
27 | );
28 | _eventSubscription = _eventChannelFor(urlOrFileId)
29 | .receiveBroadcastStream()
30 | .listen(eventListener);
31 | }
32 |
33 | EventChannel _eventChannelFor(String urlOrFileId) {
34 | return EventChannel('flutter_tencentplayer/downloadEvents$urlOrFileId');
35 | }
36 |
37 | void eventListener(dynamic event) {
38 | if (_isDisposed) {
39 | return;
40 | }
41 | final Map map = event;
42 | debugPrint("native to flutter");
43 | debugPrint(map.toString());
44 | DownloadValue downloadValue = DownloadValue.fromJson(map);
45 | if (downloadValue.fileId != null) {
46 | value[downloadValue.fileId] = downloadValue;
47 | } else {
48 | value[downloadValue.url] = downloadValue;
49 | }
50 | notifyListeners();
51 | }
52 |
53 | @override
54 | Future dispose() async {
55 | _isDisposed = true;
56 | _eventSubscription.cancel();
57 | super.dispose();
58 | }
59 |
60 | Future pauseDownload(String urlOrFileId) async {
61 | if (_isDisposed) {
62 | return;
63 | }
64 | await channel.invokeMethod(
65 | 'stopDownload',
66 | {
67 | "urlOrFileId": urlOrFileId,
68 | },
69 | );
70 | }
71 |
72 | Future cancelDownload(String urlOrFileId) async {
73 | if (_isDisposed) {
74 | return;
75 | }
76 | await channel.invokeMethod(
77 | 'stopDownload',
78 | {
79 | "urlOrFileId": urlOrFileId,
80 | },
81 | );
82 |
83 | if (value.containsKey(urlOrFileId)) {
84 | Future.delayed(Duration(milliseconds: 2500), () {
85 | value.remove(urlOrFileId);
86 | });
87 | }
88 |
89 | notifyListeners();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/lib/controller/tencent_player_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/services.dart';
4 | import 'package:flutter_tencentplayer_plus/flutter_tencentplayer_plus.dart';
5 |
6 | class TencentPlayerController extends ValueNotifier {
7 | int _textureId;
8 | final String dataSource;
9 | final DataSourceType dataSourceType;
10 | final PlayerConfig playerConfig;
11 | MethodChannel channel = TencentPlayer.channel;
12 |
13 | TencentPlayerController.asset(this.dataSource,
14 | {this.playerConfig = const PlayerConfig()})
15 | : dataSourceType = DataSourceType.asset,
16 | super(TencentPlayerValue());
17 |
18 | TencentPlayerController.network(this.dataSource,
19 | {this.playerConfig = const PlayerConfig()})
20 | : dataSourceType = DataSourceType.network,
21 | super(TencentPlayerValue());
22 |
23 | TencentPlayerController.file(String filePath,
24 | {this.playerConfig = const PlayerConfig()})
25 | : dataSource = filePath,
26 | dataSourceType = DataSourceType.file,
27 | super(TencentPlayerValue());
28 |
29 | bool _isDisposed = false;
30 | StreamSubscription _eventSubscription;
31 | _VideoAppLifeCycleObserver _lifeCycleObserver;
32 |
33 | // @visibleForTesting
34 | int get textureId => _textureId;
35 |
36 | ///初始化播放器的方法
37 | Future initialize() async {
38 | _lifeCycleObserver = _VideoAppLifeCycleObserver(this);
39 | _lifeCycleObserver.initialize();
40 | Map dataSourceDescription;
41 | switch (dataSourceType) {
42 | case DataSourceType.asset:
43 | dataSourceDescription = {'asset': dataSource};
44 | break;
45 | case DataSourceType.network:
46 | case DataSourceType.file:
47 | dataSourceDescription = {'uri': dataSource};
48 | break;
49 | }
50 |
51 | value = value.copyWith(isPlaying: playerConfig.autoPlay);
52 | dataSourceDescription.addAll(playerConfig.toJson());
53 |
54 | final Map response =
55 | await channel.invokeMapMethod(
56 | 'create',
57 | dataSourceDescription,
58 | );
59 |
60 | _textureId = response['textureId'];
61 |
62 | ///设置监听naive 返回的的数据
63 | _eventSubscription = _eventChannelFor(_textureId)
64 | .receiveBroadcastStream()
65 | .listen(eventListener);
66 | }
67 |
68 | ///注册监听native的方法
69 | EventChannel _eventChannelFor(int textureId) {
70 | return EventChannel('flutter_tencentplayer/videoEvents$textureId');
71 | }
72 |
73 | ///native 传递到flutter 进行数据处理
74 | void eventListener(dynamic event) {
75 | if (_isDisposed) {
76 | return;
77 | }
78 | final Map map = event;
79 | switch (map['event']) {
80 | case 'initialized':
81 | value = value.copyWith(
82 | duration: Duration(milliseconds: map['duration']),
83 | size: Size(map['width']?.toDouble() ?? 0.0,
84 | map['height']?.toDouble() ?? 0.0),
85 | );
86 | break;
87 | case 'progress':
88 | value = value.copyWith(
89 | position: Duration(milliseconds: map['progress']),
90 | duration: Duration(milliseconds: map['duration']),
91 | playable: Duration(milliseconds: map['playable']),
92 | );
93 | break;
94 | case 'loading':
95 | value = value.copyWith(isLoading: true);
96 | break;
97 | case 'loadingend':
98 | value = value.copyWith(isLoading: false);
99 | break;
100 | case 'playend':
101 | value = value.copyWith(isPlaying: false, position: value.duration);
102 | break;
103 | case 'netStatus':
104 | value = value.copyWith(netSpeed: map['netSpeed']);
105 | break;
106 | case 'error':
107 | value = value.copyWith(errorDescription: map['errorInfo']);
108 | break;
109 | }
110 | }
111 |
112 | @override
113 | Future dispose() async {
114 | if (!_isDisposed) {
115 | _isDisposed = true;
116 | await _eventSubscription?.cancel();
117 | await channel.invokeListMethod(
118 | 'dispose', {'textureId': _textureId});
119 | _lifeCycleObserver.dispose();
120 | }
121 | super.dispose();
122 | }
123 |
124 | Future play() async {
125 | value = value.copyWith(isPlaying: true);
126 | await _applyPlayPause();
127 | }
128 |
129 | Future pause() async {
130 | value = value.copyWith(isPlaying: false);
131 | await _applyPlayPause();
132 | }
133 |
134 | Future _applyPlayPause() async {
135 | if (!value.initialized || _isDisposed) {
136 | return;
137 | }
138 | if (value.isPlaying) {
139 | await channel
140 | .invokeMethod('play', {'textureId': _textureId});
141 | } else {
142 | await channel
143 | .invokeMethod('pause', {'textureId': _textureId});
144 | }
145 | }
146 |
147 | Future seekTo(Duration moment) async {
148 | if (_isDisposed) {
149 | return;
150 | }
151 | if (moment == null) {
152 | return;
153 | }
154 | if (moment > value.duration) {
155 | moment = value.duration;
156 | } else if (moment < const Duration()) {
157 | moment = const Duration();
158 | }
159 | await channel.invokeMethod('seekTo', {
160 | 'textureId': _textureId,
161 | 'location': moment.inSeconds,
162 | });
163 | value = value.copyWith(position: moment);
164 | }
165 |
166 | ///点播为m3u8子流,会自动无缝seek
167 | Future setBitrateIndex(int index) async {
168 | if (_isDisposed) {
169 | return;
170 | }
171 | await channel.invokeMethod('setBitrateIndex', {
172 | 'textureId': _textureId,
173 | 'index': index,
174 | });
175 | value = value.copyWith(bitrateIndex: index);
176 | }
177 |
178 | Future setRate(double rate) async {
179 | if (_isDisposed) {
180 | return;
181 | }
182 | if (rate > 2.0) {
183 | rate = 2.0;
184 | } else if (rate < 1.0) {
185 | rate = 1.0;
186 | }
187 | await channel.invokeMethod('setRate', {
188 | 'textureId': _textureId,
189 | 'rate': rate,
190 | });
191 | value = value.copyWith(rate: rate);
192 | }
193 | }
194 |
195 | ///视频组件生命周期监听
196 | class _VideoAppLifeCycleObserver with WidgetsBindingObserver {
197 | bool _wasPlayingBeforePause = false;
198 | final TencentPlayerController _controller;
199 |
200 | _VideoAppLifeCycleObserver(this._controller);
201 |
202 | void initialize() {
203 | WidgetsBinding.instance.addObserver(this);
204 | }
205 |
206 | @override
207 | void didChangeAppLifecycleState(AppLifecycleState state) {
208 | switch (state) {
209 |
210 | ///组件进入暂停状态
211 | case AppLifecycleState.paused:
212 | _wasPlayingBeforePause = _controller.value.isPlaying;
213 | _controller.pause();
214 | break;
215 |
216 | ///组件进入活跃状态
217 | case AppLifecycleState.resumed:
218 | if (_wasPlayingBeforePause) {
219 | _controller.play();
220 | }
221 | break;
222 | default:
223 | }
224 | }
225 |
226 | void dispose() {
227 | WidgetsBinding.instance.removeObserver(this);
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/lib/flutter_tencentplayer_plus.dart:
--------------------------------------------------------------------------------
1 | export 'model/download_value.dart';
2 | export 'model/player_config.dart';
3 | export 'model/tentcent_player_value.dart';
4 |
5 | export 'controller/download_controller.dart';
6 | export 'controller/tencent_player_controller.dart';
7 |
8 | export 'package:flutter_tencentplayer_plus/view/tencent_player.dart';
9 |
--------------------------------------------------------------------------------
/lib/model/download_value.dart:
--------------------------------------------------------------------------------
1 | class DownloadValue {
2 | final String downloadStatus;
3 | final int quanlity;
4 | final int duration;
5 | final int size;
6 | final int downloadSize;
7 | final double progress;
8 | final String playPath;
9 | final bool isStop;
10 | final String url;
11 | final String fileId;
12 | final String error;
13 |
14 | DownloadValue({
15 | this.downloadStatus,
16 | this.quanlity,
17 | this.duration,
18 | this.size,
19 | this.downloadSize,
20 | this.progress,
21 | this.playPath,
22 | this.isStop,
23 | this.url,
24 | this.fileId,
25 | this.error,
26 | });
27 |
28 | DownloadValue copyWith({
29 | String downloadStatus,
30 | int quanlity,
31 | int duration,
32 | int size,
33 | int downloadSize,
34 | double progress,
35 | String playPath,
36 | bool isStop,
37 | String url,
38 | String fileId,
39 | String error,
40 | }) {
41 | return DownloadValue(
42 | downloadStatus: downloadStatus ?? this.downloadStatus,
43 | quanlity: quanlity ?? this.quanlity,
44 | duration: duration ?? this.duration,
45 | size: size ?? this.size,
46 | downloadSize: downloadSize ?? this.downloadSize,
47 | progress: progress ?? this.progress,
48 | playPath: playPath ?? this.playPath,
49 | isStop: isStop ?? this.isStop,
50 | url: url ?? this.url,
51 | fileId: fileId ?? this.fileId,
52 | error: error ?? this.error,
53 | );
54 | }
55 |
56 | @override
57 | String toString() {
58 | return toJson().toString();
59 | }
60 |
61 | Map toJson() => {
62 | 'downloadStatus': this.downloadStatus,
63 | 'quanlity': this.quanlity,
64 | 'duration': this.duration,
65 | 'size': this.size,
66 | 'downloadSize': this.downloadSize,
67 | 'progress': this.progress,
68 | 'playPath': this.playPath,
69 | 'isStop': this.isStop,
70 | 'url': this.url,
71 | 'fileId': this.fileId,
72 | 'error': this.error,
73 | };
74 |
75 | factory DownloadValue.fromJson(Map json) {
76 | return DownloadValue(
77 | downloadStatus: json['downloadStatus'] as String,
78 | quanlity: int.parse(json['quanlity'].toString()),
79 | duration: int.parse(json['duration']),
80 | size: int.parse(json['size']),
81 | downloadSize: int.parse(json['downloadSize']),
82 | progress: double.parse(json['progress']),
83 | playPath: json['playPath'] as String,
84 | isStop: json['isStop'] == "true",
85 | url: json['url'] as String,
86 | fileId: json['fileId'] as String,
87 | error: json['error'] as String,
88 | );
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/model/player_config.dart:
--------------------------------------------------------------------------------
1 | class PlayerConfig {
2 | final bool autoPlay;
3 | final bool loop;
4 | final Map headers;
5 | final String cachePath;
6 | final int progressInterval;
7 |
8 | // 单位:秒
9 | final int startTime;
10 | final Map auth;
11 |
12 | const PlayerConfig({
13 | this.autoPlay = true,
14 | this.loop = false,
15 | this.headers,
16 | this.cachePath,
17 | this.progressInterval = 200,
18 | this.startTime,
19 | this.auth,
20 | });
21 |
22 | Map toJson() => {
23 | 'autoPlay': this.autoPlay,
24 | 'loop': this.loop,
25 | 'headers': this.headers,
26 | 'cachePath': this.cachePath,
27 | 'progressInterval': this.progressInterval,
28 | 'startTime': this.startTime,
29 | 'auth': this.auth,
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/lib/model/tentcent_player_value.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class TencentPlayerValue {
4 | final Duration duration;
5 | final Duration position;
6 | final Duration playable;
7 | final bool isPlaying;
8 | final String errorDescription;
9 | final Size size;
10 | final bool isLoading;
11 | final int netSpeed;
12 | final double rate;
13 | final int bitrateIndex;
14 |
15 | bool get initialized => duration.inMilliseconds != 0;
16 |
17 | bool get hasError => errorDescription != null;
18 |
19 | double get aspectRatio => size != null
20 | ? size.width / size.height > 0.0 ? size.width / size.height : 1.0
21 | : 1.0;
22 |
23 | TencentPlayerValue({
24 | this.duration = const Duration(),
25 | this.position = const Duration(),
26 | this.playable = const Duration(),
27 | this.isPlaying = false,
28 | this.errorDescription,
29 | this.size,
30 | this.isLoading = false,
31 | this.netSpeed,
32 | this.rate = 1.0,
33 | this.bitrateIndex = 0, //TODO 默认清晰度
34 | });
35 |
36 | TencentPlayerValue copyWith({
37 | Duration duration,
38 | Duration position,
39 | Duration playable,
40 | bool isPlaying,
41 | String errorDescription,
42 | Size size,
43 | bool isLoading,
44 | int netSpeed,
45 | double rate,
46 | int bitrateIndex,
47 | }) {
48 | return TencentPlayerValue(
49 | duration: duration ?? this.duration,
50 | position: position ?? this.position,
51 | playable: playable ?? this.playable,
52 | isPlaying: isPlaying ?? this.isPlaying,
53 | errorDescription: errorDescription ?? this.errorDescription,
54 | size: size ?? this.size,
55 | isLoading: isLoading ?? this.isLoading,
56 | netSpeed: netSpeed ?? this.netSpeed,
57 | rate: rate ?? this.rate,
58 | bitrateIndex: bitrateIndex ?? this.bitrateIndex,
59 | );
60 | }
61 |
62 | @override
63 | String toString() {
64 | return '$runtimeType('
65 | 'duration: $duration, '
66 | 'position: $position, '
67 | 'playable: $playable, '
68 | 'isPlaying: $isPlaying, '
69 | 'errorDescription: $errorDescription),'
70 | 'isLoading: $isLoading),'
71 | 'netSpeed: $netSpeed),'
72 | 'rate: $rate),'
73 | 'bitrateIndex: $bitrateIndex),'
74 | 'size: $size)';
75 | }
76 | }
77 |
78 | enum DataSourceType { asset, network, file }
79 |
--------------------------------------------------------------------------------
/lib/view/tencent_player.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:flutter_tencentplayer_plus/flutter_tencentplayer_plus.dart';
4 |
5 | class TencentPlayer extends StatefulWidget {
6 | static MethodChannel channel = const MethodChannel('flutter_tencentplayer')
7 |
8 | /// init方法是TencentPlayer 第一次加载的时候执行 并且只是执行一次
9 | ..invokeMethod('init');
10 |
11 | final TencentPlayerController controller;
12 |
13 | TencentPlayer(this.controller);
14 |
15 | @override
16 | _TencentPlayerState createState() => new _TencentPlayerState();
17 | }
18 |
19 | class _TencentPlayerState extends State {
20 | // VoidCallback _listener;
21 | // int _textureId;
22 | //
23 | // _TencentPlayerState() {
24 | // _listener = () {
25 | // final int newTextureId = widget.controller.textureId;
26 | // if (newTextureId != _textureId) {
27 | // setState(() {
28 | // _textureId = newTextureId;
29 | // });
30 | // }
31 | // };
32 | // }
33 |
34 | @override
35 | void initState() {
36 | super.initState();
37 | // _textureId = widget.controller.textureId;
38 | // widget.controller.addListener(_listener);
39 |
40 | print("TencentPlayer initState");
41 | }
42 |
43 | @override
44 | void didUpdateWidget(TencentPlayer oldWidget) {
45 | //print("TencentPlayer didUpdateWidget");
46 | super.didUpdateWidget(oldWidget);
47 | // oldWidget.controller.removeListener(_listener);
48 | // _textureId = widget.controller.textureId;
49 | // widget.controller.addListener(_listener);
50 | }
51 |
52 | @override
53 | void deactivate() {
54 | print("TencentPlayer deactivate");
55 | super.deactivate();
56 | // widget.controller.removeListener(_listener);
57 | }
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | return ValueListenableBuilder(
62 | valueListenable: widget.controller,
63 | builder: (BuildContext context, TencentPlayerValue value, Widget child) {
64 | // return Texture(textureId: widget.controller.textureId);
65 | var _textureId = widget.controller.textureId;
66 | return _textureId == null
67 | ? Container()
68 | : Texture(textureId: _textureId);
69 | },
70 | );
71 | // ChangeNotifierProvider
72 | //
73 | // return _textureId == null ? Container() : Texture(textureId: _textureId);
74 | }
75 |
76 | @override
77 | void dispose() {
78 | print("TencentPlayer dispose");
79 | widget.controller.dispose();
80 | super.dispose();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.flutter-io.cn"
9 | source: hosted
10 | version: "2.3.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.flutter-io.cn"
16 | source: hosted
17 | version: "1.0.5"
18 | charcode:
19 | dependency: transitive
20 | description:
21 | name: charcode
22 | url: "https://pub.flutter-io.cn"
23 | source: hosted
24 | version: "1.1.2"
25 | collection:
26 | dependency: transitive
27 | description:
28 | name: collection
29 | url: "https://pub.flutter-io.cn"
30 | source: hosted
31 | version: "1.14.11"
32 | flutter:
33 | dependency: "direct main"
34 | description: flutter
35 | source: sdk
36 | version: "0.0.0"
37 | flutter_test:
38 | dependency: "direct dev"
39 | description: flutter
40 | source: sdk
41 | version: "0.0.0"
42 | matcher:
43 | dependency: transitive
44 | description:
45 | name: matcher
46 | url: "https://pub.flutter-io.cn"
47 | source: hosted
48 | version: "0.12.5"
49 | meta:
50 | dependency: transitive
51 | description:
52 | name: meta
53 | url: "https://pub.flutter-io.cn"
54 | source: hosted
55 | version: "1.1.7"
56 | path:
57 | dependency: transitive
58 | description:
59 | name: path
60 | url: "https://pub.flutter-io.cn"
61 | source: hosted
62 | version: "1.6.4"
63 | pedantic:
64 | dependency: transitive
65 | description:
66 | name: pedantic
67 | url: "https://pub.flutter-io.cn"
68 | source: hosted
69 | version: "1.8.0+1"
70 | quiver:
71 | dependency: transitive
72 | description:
73 | name: quiver
74 | url: "https://pub.flutter-io.cn"
75 | source: hosted
76 | version: "2.0.5"
77 | sky_engine:
78 | dependency: transitive
79 | description: flutter
80 | source: sdk
81 | version: "0.0.99"
82 | source_span:
83 | dependency: transitive
84 | description:
85 | name: source_span
86 | url: "https://pub.flutter-io.cn"
87 | source: hosted
88 | version: "1.5.5"
89 | stack_trace:
90 | dependency: transitive
91 | description:
92 | name: stack_trace
93 | url: "https://pub.flutter-io.cn"
94 | source: hosted
95 | version: "1.9.3"
96 | stream_channel:
97 | dependency: transitive
98 | description:
99 | name: stream_channel
100 | url: "https://pub.flutter-io.cn"
101 | source: hosted
102 | version: "2.0.0"
103 | string_scanner:
104 | dependency: transitive
105 | description:
106 | name: string_scanner
107 | url: "https://pub.flutter-io.cn"
108 | source: hosted
109 | version: "1.0.5"
110 | term_glyph:
111 | dependency: transitive
112 | description:
113 | name: term_glyph
114 | url: "https://pub.flutter-io.cn"
115 | source: hosted
116 | version: "1.1.0"
117 | test_api:
118 | dependency: transitive
119 | description:
120 | name: test_api
121 | url: "https://pub.flutter-io.cn"
122 | source: hosted
123 | version: "0.2.5"
124 | typed_data:
125 | dependency: transitive
126 | description:
127 | name: typed_data
128 | url: "https://pub.flutter-io.cn"
129 | source: hosted
130 | version: "1.1.6"
131 | vector_math:
132 | dependency: transitive
133 | description:
134 | name: vector_math
135 | url: "https://pub.flutter-io.cn"
136 | source: hosted
137 | version: "2.0.8"
138 | sdks:
139 | dart: ">=2.2.2 <3.0.0"
140 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_tencentplayer_plus
2 | description: The flutter plugin supports live local video playback and supports both android and ios
3 | version: 0.1.0
4 | homepage: https://github.com/yxwandroid/flutter_tencentplayer_plus.git
5 |
6 | environment:
7 | sdk: ">=2.1.0 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 |
13 | dev_dependencies:
14 | flutter_test:
15 | sdk: flutter
16 |
17 | # For information on the generic Dart part of this file, see the
18 | # following page: https://dart.dev/tools/pub/pubspec
19 |
20 | # The following section is specific to Flutter.
21 | flutter:
22 | # This section identifies this Flutter project as a plugin project.
23 | # The androidPackage and pluginClass identifiers should not ordinarily
24 | # be modified. They are used by the tooling to maintain consistency when
25 | # adding or updating assets for this project.
26 | plugin:
27 | androidPackage: plus.tencentplayer.wilson.flutter.flutter_tencentplayer_plus
28 | pluginClass: FlutterTencentplayerPlusPlugin
29 |
30 | # To add assets to your plugin package, add an assets section, like this:
31 | # assets:
32 | # - images/a_dot_burr.jpeg
33 | # - images/a_dot_ham.jpeg
34 | #
35 | # For details regarding assets in packages, see
36 | # https://flutter.dev/assets-and-images/#from-packages
37 | #
38 | # An image asset can refer to one or more resolution-specific "variants", see
39 | # https://flutter.dev/assets-and-images/#resolution-aware.
40 |
41 | # To add custom fonts to your plugin package, add a fonts section here,
42 | # in this "flutter" section. Each entry in this list should have a
43 | # "family" key with the font family name, and a "fonts" key with a
44 | # list giving the asset and other descriptors for the font. For
45 | # example:
46 | # fonts:
47 | # - family: Schyler
48 | # fonts:
49 | # - asset: fonts/Schyler-Regular.ttf
50 | # - asset: fonts/Schyler-Italic.ttf
51 | # style: italic
52 | # - family: Trajan Pro
53 | # fonts:
54 | # - asset: fonts/TrajanPro.ttf
55 | # - asset: fonts/TrajanPro_Bold.ttf
56 | # weight: 700
57 | #
58 | # For details regarding fonts in packages, see
59 | # https://flutter.dev/custom-fonts/#from-packages
60 |
--------------------------------------------------------------------------------
/readme/android.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yxwandroid/flutter_tencentplayer_plus/35e729c9910abbea17555099b3fdf322dc3d0b12/readme/android.gif
--------------------------------------------------------------------------------
/test/flutter_tencentplayer_plus_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 |
4 | void main() {
5 | const MethodChannel channel = MethodChannel('flutter_tencentplayer_plus');
6 |
7 | setUp(() {
8 | channel.setMockMethodCallHandler((MethodCall methodCall) async {
9 | return '42';
10 | });
11 | });
12 |
13 | tearDown(() {
14 | channel.setMockMethodCallHandler(null);
15 | });
16 |
17 | // test('getPlatformVersion', () async {
18 | // expect(await FlutterTencentplayerPlus.platformVersion, '42');
19 | // });
20 | }
21 |
--------------------------------------------------------------------------------