jun7572 %!s(int64=5) %!d(string=hai) anos
pai
achega
a9f789c92e

+ 9 - 0
example/ios/File.swift

@@ -0,0 +1,9 @@
+//
+//  File.swift
+//  Runner
+//
+//  Created by jiajunzhou on 2020/4/3.
+//  Copyright © 2020 The Chromium Authors. All rights reserved.
+//
+
+import Foundation

+ 4 - 0
example/ios/Runner-Bridging-Header.h

@@ -0,0 +1,4 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+

+ 12 - 0
example/ios/Runner/Info.plist

@@ -2,6 +2,8 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>io.flutter.embedded_views_preview</key>
+	<string>YES</string>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
 	<key>CFBundleExecutable</key>
@@ -22,6 +24,16 @@
 	<string>$(FLUTTER_BUILD_NUMBER)</string>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
+	<key>NSAppleMusicUsageDescription</key>
+	<string></string>
+	<key>NSCameraUsageDescription</key>
+	<string>需要使用摄像头以拍摄配音视频</string>
+	<key>NSMicrophoneUsageDescription</key>
+	<string>需要使用录音功能以配音</string>
+	<key>NSPhotoLibraryAddUsageDescription</key>
+	<string></string>
+	<key>NSPhotoLibraryUsageDescription</key>
+	<string></string>
 	<key>UILaunchStoryboardName</key>
 	<string>LaunchScreen</string>
 	<key>UIMainStoryboardFile</key>

+ 317 - 0
ios/Classes/CameraViewFactory.swift

@@ -0,0 +1,317 @@
+import Foundation
+import Flutter
+import UIKit
+import AliyunVideoSDKPro
+public class CameraViewFactory: NSObject, FlutterPlatformViewFactory {
+    var messenger: FlutterBinaryMessenger!
+    public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
+        return CameraView(withFrame: frame, viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)
+    }
+    @objc public init(messenger: (NSObject & FlutterBinaryMessenger)?) {
+        super.init()
+        self.messenger = messenger
+    }
+    
+    public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
+        return FlutterStandardMessageCodec.sharedInstance()
+    }
+    
+}
+public class CameraView: NSObject, FlutterPlatformView, AliyunIRecorderDelegate, AliyunIPlayerCallback, AliyunIExporterCallback, AliyunIRenderCallback {
+    public func playerDidEnd() {
+        
+    }
+    
+    public func playProgress(_ playSec: Double, streamProgress streamSec: Double) {
+        
+    }
+    
+    public func playError(_ errorCode: Int32) {
+        
+    }
+    
+    public func seekDidEnd() {
+        
+    }
+    
+    public func playerDidStart() {
+        
+    }
+    
+    
+    fileprivate var viewId: Int64!
+    fileprivate var cameraView: UIView!
+    fileprivate var channel: FlutterMethodChannel!
+    fileprivate var recordPath: String?
+    fileprivate var taskPath: String?
+    fileprivate var recorder: AliyunIRecorder!
+    fileprivate var composeResult: FlutterResult?
+    public init(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger: FlutterBinaryMessenger) {
+        super.init()
+        
+        self.viewId = viewId
+       
+        self.cameraView = UIView()
+        self.cameraView.frame = UIScreen.main.bounds
+        self.channel = FlutterMethodChannel(name: "flutter_ali_camera_\(viewId)", binaryMessenger: binaryMessenger)
+        self.channel.setMethodCallHandler({
+            [weak self]
+            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
+            if let this = self {
+                this.onMethodCall(call: call, result: result)
+            }
+        })
+    }
+    
+    
+    public func view() -> UIView {
+        return self.cameraView
+    }
+    func onMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) {
+        let args = call.arguments as? [String: Any]
+        let method = call.method
+       if call.method == "initializeSdk" {
+//            initSdk(result)
+            result(true)
+        } else if call.method == "cleanAudioData" {
+            let videoId = args!["videoId"] as! String
+            let pathAudio = args!["pathAudio"] as! String
+            deleteFileWithPrefix(folderPath: pathAudio, prefix: "\(videoId)_")
+            result(true)
+        } else if call.method == "getFilterParentPath" {
+            print("path \(Bundle.main.path(forResource: "icon", ofType: "png"))")
+            let url = Bundle.main.bundlePath
+            result("\(url)/filter/")
+        } else if method == "startPreview" {
+            initCameraView()
+            recorder?.clipManager?.deleteAllPart()
+            recorder?.startPreview(withPositon: AliyunIRecorderCameraPosition.front)
+            result(true)
+        } else 
+        if method == "startPreview" {
+            initCameraView()
+            recorder?.clipManager?.deleteAllPart()
+            recorder?.startPreview(withPositon: AliyunIRecorderCameraPosition.front)
+            result(true)
+        } else if method == "setRecordPath" {
+            self.recordPath = args!["path"] as? String
+            self.taskPath = args!["taskPath"] as? String
+            result(true)
+        } else if method == "onStop" {
+            onStop()
+            result(true)
+        } else if method == "onResume" {
+            recorder?.startPreview(withPositon: AliyunIRecorderCameraPosition.front)
+            result(true)
+        } else if method == "onDestory" {
+            onStop()
+            onDestory()
+            result(true)
+        } else if method == "switchCamera" {
+            recorder.switchCameraPosition()
+            result(true)
+        } else if method == "setBeautyLevel" {
+            let level =  args!["level"] as! Int
+            recorder.beautifyStatus = (level == 0)
+            recorder.beautifyValue =  Int32(level)
+            result(true)
+        } else if method == "startRecord" {
+            let maxDuration = args!["maxDuration"] as! Int
+            let videoId = args!["videoId"] as! String
+            let index = args!["index"] as! Int
+            let fileName = args!["fileName"] as! String
+            if recorder?.clipManager?.videoAbsolutePaths != nil && recorder?.clipManager?.videoAbsolutePaths.count != 0 {
+                recorder?.clipManager?.deletePart()
+            }
+            clearCacheVideo(videoId: videoId, index: index)
+            recorder?.outputPath = self.recordPath! + "\(fileName).mp4"
+            recorder?.clipManager?.maxDuration = CGFloat(Float(maxDuration) / 1000.0)
+            recorder?.startRecording()
+            result(true)
+        } else if method == "stopRecord" {
+            recorder?.stopRecording()
+            result(true)
+        } else if method == "finishRecord" {
+            recorder?.finishRecording()
+            result(true)
+        } else if method == "setFilter" {
+            let path: String? = args?["path"] as? String
+            if path == nil || path?.count == 0 {
+                // delete filter
+                recorder?.deleteFilter()
+                return
+            }
+            recorder?.apply(AliyunEffectFilter.init(file: path))
+        } else if method == "startCompose" {
+            let outputPath = args!["outputPath"] as! String
+            let bgmPath = args!["bgmPath"] as! String
+            let paths = args!["paths"] as! [String]
+            let durations = args!["durations"] as! [Int]
+            composeResult = result
+            startCompose(outputPath: outputPath, bgmPath: bgmPath, paths: paths, durations: durations)
+        } else if method == "create"{
+            result(FlutterMethodNotImplemented)
+        }
+    }
+    private func initCameraView() {
+        recorder = AliyunIRecorder.init(delegate: self, videoSize: CGSize(width: 720, height: 1280))
+        recorder?.preview = self.cameraView
+        recorder?.taskPath = self.taskPath
+        recorder?.clipManager?.maxDuration = 0
+    }
+    
+    private func onStop() {
+        recorder?.stopRecording()
+        recorder?.stopPreview()
+    }
+    
+    private func onDestory() {
+        recorder?.destroy()
+        recorder = nil
+        cameraView = nil
+    }
+    
+    var editor: AliyunEditor?
+    private func startCompose(outputPath: String, bgmPath: String, paths: [String], durations: [Int]) {
+        AliyunVideoSDKInfo.print()
+        if durations.count != paths.count {
+            return
+        }
+//        AliyunVideoSDKInfo.setLogLevel(AlivcLogLevel.debug)
+        
+        let importer = AliyunImporter.init(path: self.taskPath, outputSize: CGSize(width: 480, height: 720))
+        let param = AliyunVideoParam.init()
+        param.fps = 30
+        param.gop = 30
+        param.videoQuality = AliyunVideoQuality.medium
+        param.scaleMode = AliyunScaleMode.fill
+        // 编码模式
+        param.codecType = AliyunVideoCodecType.hardware
+        importer?.setVideoParam(param)
+        for index in 0..<paths.count {
+            print("paths: \(paths[index])")
+            // 取出有效的document地址
+            let documentRance: Range = paths[index].range(of: "/Documents/")!
+            let distance: Int = paths[index].distance(from: paths[index].startIndex, to: documentRance.upperBound)
+            let truePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] + paths[index].subString(from: distance - 1)
+            print("true path: \(truePath)")
+            
+            importer?.addMediaClip(AliyunClip.init(videoPath: truePath, animDuration: 0))
+        }
+        importer?.generateProjectConfigure()
+        editor = AliyunEditor.init(path: self.taskPath, preview: nil)
+        editor?.apply(AliyunEffectMusic.init(file: bgmPath))
+        editor?.delegate = self
+        editor?.startEdit()
+        
+        let exporter = editor?.getExporter()
+        exporter?.startExport(outputPath)
+    }
+    
+    private func clearCacheVideo(videoId: String, index: Int) {
+        deleteFileWithPrefix(folderPath: self.recordPath!, prefix: "\(videoId)_\(index)_")
+    }
+    
+    private func deleteFileWithPrefix(folderPath: String, prefix: String) {
+        let manager = FileManager.default
+        let files:[String] = try! manager.contentsOfDirectory(atPath: folderPath)
+        
+        for file in files{
+            do {
+                if file.hasPrefix(prefix) {
+                    try manager.removeItem(atPath: folderPath + "/\(file)")
+                }
+            } catch {
+                print("faild to delete")
+            }
+        }
+    }
+    
+    public func recorderDeviceAuthorization(_ status: AliyunIRecorderDeviceAuthor) {
+        if status == AliyunIRecorderDeviceAuthor.enabled {
+            print("sdk enabled")
+        } else {
+            print("sdk disabled")
+        }
+    }
+    
+    public func recorderVideoDuration(_ duration: CGFloat) {
+        print("recorderVideoDuration:\(duration)")
+        channel.invokeMethod("recordProgress", arguments: ["progress": Int(duration * 1000)])
+    }
+    
+    public func recoderError(_ error: Error!) {
+        print("recoderError")
+        recorder?.finishRecording()
+    }
+    
+    public func recorderDidStopWithMaxDuration() {
+        print("recorderDidStopWithMaxDuration")
+        recorder?.finishRecording()
+    }
+    
+    public func recorderDidFinishRecording() {
+        channel.invokeMethod("recordProgress", arguments: ["progress":  Int((recorder?.clipManager?.maxDuration ?? 0) * 1000), "path": recorder?.outputPath])
+    }
+    public func exporterDidCancel() {
+        
+    }
+    
+    public func exportProgress(_ progress: Float) {
+        print("exportProgress")
+    }
+    
+    public func exportError(_ errorCode: Int32) {
+        print("export error : \(errorCode)")
+        composeResult?(false)
+    }
+    
+    public func exporterDidStart() {
+        print("exporterDidStart")
+    }
+       
+    public func exporterDidEnd(_ outputPath: String!) {
+        composeResult?(true)
+    }
+}
+extension String {
+  func toInt() -> Int? {
+      return Int(self)
+  }
+  func toFloat() -> Float? {
+      return Float(self)
+  }
+  func toDouble() -> Double? {
+      return Double(self)
+  }
+  //MARK:- 去除字符串两端的空白字符
+  func trim() -> String {
+      return self.trimmingCharacters(in: CharacterSet.whitespaces)
+  }
+  func subString(to: Int) -> String {
+    var to = to
+    if to > self.count {
+        to = self.count
+    }
+    return String(self.prefix(to))
+}
+  func subString(from: Int) -> String {
+    if from >= self.count {
+        return ""
+    }
+    let startIndex = self.index(self.startIndex, offsetBy: from)
+    let endIndex = self.endIndex
+    return String(self[startIndex..<endIndex])
+}
+ func subString(start: Int, end: Int) -> String {
+    if start < end {
+        let startIndex = self.index(self.startIndex, offsetBy: start)
+        let endIndex = self.index(self.startIndex, offsetBy: end)
+        
+        return String(self[startIndex..<endIndex])
+    }
+    return ""
+ }
+    
+    
+}

+ 169 - 0
ios/Classes/CameraViewFactory1.swift

@@ -0,0 +1,169 @@
+//
+//  CameraViewFactory1.swift
+//  Pods
+//
+//  Created by jiajunzhou on 2020/4/5.
+//
+import Foundation
+import Flutter
+import UIKit
+import AliyunVideoSDKPro
+public class CameraViewFactory1 :NSObject,FlutterPlatformViewFactory{
+    var messenger: FlutterBinaryMessenger!
+    public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
+       return CameraView1(withFrame: frame, viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)
+    }
+    @objc public init(messenger: (NSObject & FlutterBinaryMessenger)?) {
+        super.init()
+        self.messenger = messenger
+    }
+    
+}
+public class CameraView1: NSObject, FlutterPlatformView,AliyunIRecorderDelegate{
+    public func recorderDeviceAuthorization(_ status: AliyunIRecorderDeviceAuthor) {
+          if status == AliyunIRecorderDeviceAuthor.enabled {
+                        print("sdk enabled")
+                    } else {
+                        print("sdk disabled")
+                    }
+    }
+    public func recorderDidStopRecording(){
+        print("recorderDidStopRecording.....");
+    }
+        
+//   调用stopRecording停止录制后,SDK内部会执行保存视频相关操作,收到AliyunIRecorderDelegate的- (void)recorderDidStopRecording回调后才能继续执行其他操作。
+//    startRecording和stopRecording需要成对出现,可以调用一次或多次,对应SDK内部会生成一段或多段临时视频文件。
+   
+    
+    
+    public func view() -> UIView {
+        return self.cameraView
+    }
+    fileprivate var viewId: Int64!
+       fileprivate var cameraView: UIView!
+       fileprivate var channel: FlutterMethodChannel!
+       fileprivate var recordPath: String?
+       fileprivate var taskPath: String?
+       fileprivate var recorder: AliyunIRecorder!
+       fileprivate var composeResult: FlutterResult?
+    //初始设置参数
+                var videoWidth : Int!
+                 var videoHeight : Int!
+                 var fps : Int!
+                 var videoCodecs : Int!
+                 var crf : Int!
+                 var encoderFps : Int!
+                 var quality : Int!
+                 var videoBitrate : Int!
+                 var gop : Int!
+    var first :Bool = true;
+    public init(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger: FlutterBinaryMessenger) {
+         super.init()
+         
+         self.viewId = viewId
+        
+         self.cameraView = UIView()
+         self.cameraView.frame = UIScreen.main.bounds
+         self.channel = FlutterMethodChannel(name: "flutter_ali_camera", binaryMessenger: binaryMessenger)
+         self.channel.setMethodCallHandler({
+             [weak self]
+             (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
+             if let this = self {
+                 this.onMethodCall(call: call, result: result)
+             }
+         })
+     }
+    func onMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) {
+        if(call.method=="initializeSdk"){
+            result(true);
+        }
+        else if(call.method=="create"){
+            let args = call.arguments as? [String: Any]
+            var dics = args!["recordOption"] as?  Dictionary<String, Int>
+             self.videoHeight = dics!["videoHeight"]
+             self.videoWidth = dics!["videoWidth"]
+             self.fps = dics!["fps"]
+             self.videoCodecs = dics!["videoCodecs"]
+             self.crf = dics!["crf"]
+             self.encoderFps = dics!["encoderFps"]
+             self.quality = dics!["quality"]
+             self.videoBitrate = dics!["videoBitrate"]
+             self.gop = dics!["gop"]
+            
+//                 recorder = AliyunIRecorder.init(delegate: self, videoSize: CGSize(width: 720 , height: 1080))
+//                       recorder?.preview = self.cameraView
+//                       recorder?.taskPath = self.taskPath
+//                       recorder?.clipManager?.maxDuration = 0
+                 
+               result(true);
+        }else if(call.method=="startPreview"){
+            if(first){
+                first=false;
+                recorder = AliyunIRecorder.init(delegate: self, videoSize: CGSize(width: videoWidth , height: videoHeight))
+                    recorder?.preview = self.cameraView
+                    recorder?.taskPath = self.taskPath
+                    recorder?.clipManager?.maxDuration = 0
+                    recorder?.clipManager?.deleteAllPart()
+                    recorder.beautifyStatus = true
+            }
+        
+                recorder?.startPreview(withPositon: AliyunIRecorderCameraPosition.front)
+         
+        }
+        else if(call.method=="stopPreview"){
+            onStop();
+              result(true);
+        }
+        else if(call.method=="setBeauty"){
+            
+            let args = call.arguments as?  Dictionary<String, Int>
+            let  level =  args!["level"] as! Int
+            recorder.beautifyValue =  Int32(level)
+        }
+        else if(call.method=="switchCamera"){
+            recorder.switchCameraPosition();
+            result(true);
+        }
+        else if(call.method=="setFilter"){
+            //未验证
+            let args = call.arguments as?  Dictionary<String, String>
+            let path = args?["path"]
+            if path == nil || path?.count == 0 {
+              // delete filter
+            recorder?.deleteFilter()
+               return
+            }
+         recorder?.apply(AliyunEffectFilter.init(file: path))
+        }
+        else if(call.method=="startRecord"){
+            let args = call.arguments as?  Dictionary<String, Int>
+            let maxDuration = args!["max"] as! Int
+            var fileName = args!["recordPath"] as! String
+            print("fileNamefileNamefileName=="+fileName)
+            recorder?.outputPath = fileName
+         
+            recorder?.clipManager?.maxDuration = CGFloat(Float(maxDuration) / 1000.0)
+            recorder?.startRecording()
+        }
+        else if(call.method=="startCompose"){
+            
+        }
+        else if(call.method=="recordUpdate"){
+            
+        }
+        else if(call.method=="onDestroy"){
+            
+        }
+        else {
+            result(FlutterMethodNotImplemented)
+        }
+    }
+    
+    
+
+    private func onStop() {
+        recorder?.stopRecording()
+        recorder?.stopPreview()
+    }
+    
+}

+ 16 - 6
ios/Classes/FlutterAliCameraPlugin.m

@@ -1,12 +1,22 @@
 #import "FlutterAliCameraPlugin.h"
-
+#import <flutter_ali_camera/flutter_ali_camera-Swift.h>
+#if __has_include(<flutter_ali_camera/flutter_ali_camera-Swift.h>)
+#import <flutter_ali_camera/flutter_ali_camera-Swift.h>
+#else
+// Support project import fallback if the generated compatibility header
+// is not copied when this plugin is created as a library.
+// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
+#import "flutter_ali_camera-Swift.h"
+#endif
 @implementation FlutterAliCameraPlugin
 + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
-  FlutterMethodChannel* channel = [FlutterMethodChannel
-      methodChannelWithName:@"flutter_ali_camera"
-            binaryMessenger:[registrar messenger]];
-  FlutterAliCameraPlugin* instance = [[FlutterAliCameraPlugin alloc] init];
-  [registrar addMethodCallDelegate:instance channel:channel];
+//  FlutterMethodChannel* channel = [FlutterMethodChannel
+//      methodChannelWithName:@"flutter_ali_camera"
+//            binaryMessenger:[registrar messenger]];
+//  FlutterAliCameraPlugin* instance = [[FlutterAliCameraPlugin alloc] init];
+// [registrar addMethodCallDelegate:instance channel:channel];
+//    [registrar registerViewFactory:[[CameraViewFactory1 alloc]initWithMessenger:[registrar messenger]]  withId:@"com.i2edu.cameraLib"];
+     [SwiftFlutterAliCameraPlugin registerWithRegistrar:registrar];
 }
 
 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {

+ 20 - 4
ios/Classes/SwiftFlutterAliCameraPlugin.swift

@@ -1,14 +1,30 @@
 import Flutter
 import UIKit
+import AliyunVideoSDKPro
+
+
 
 public class SwiftFlutterAliCameraPlugin: NSObject, FlutterPlugin {
+    public func recorderDeviceAuthorization(_ status: AliyunIRecorderDeviceAuthor) {
+       
+    }
+    
+    var messenger: FlutterBinaryMessenger!
+
+  
   public static func register(with registrar: FlutterPluginRegistrar) {
-    let channel = FlutterMethodChannel(name: "flutter_ali_camera", binaryMessenger: registrar.messenger())
-    let instance = SwiftFlutterAliCameraPlugin()
-    registrar.addMethodCallDelegate(instance, channel: channel)
+//    let channel = FlutterMethodChannel(name: "flutter_ali_camera", binaryMessenger: registrar.messenger())
+//    let instance = SwiftFlutterAliCameraPlugin()
+//    registrar.addMethodCallDelegate(instance, channel: channel)
+    registrar.register(CameraViewFactory1(messenger: registrar.messenger() as! NSObject & FlutterBinaryMessenger), withId: "com.i2edu.cameraLib")
   }
 
+     
+    
   public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
-    result("iOS " + UIDevice.current.systemVersion)
+ 
   }
+    
+
+    
 }

+ 9 - 0
ios/flutter_ali_camera.podspec

@@ -17,6 +17,15 @@ A new Flutter plugin.
   s.public_header_files = 'Classes/**/*.h'
   s.dependency 'Flutter'
   s.platform = :ios, '8.0'
+  s.swift_version = '5.0'
+  s.frameworks = 'ImageIO', 'CoreMedia', 'CoreVideo', 'VideoToolBox', 'MediaPlayer', 'OpenAL', 'CoreLocation', 'QuartzCore', 'CoreGraphics'
+  s.libraries = 'z', 'c++', 'c', 'sqlite3', 'iconv', 'resolv'
+  s.dependency 'AliyunVideoSDKStd', ' ~> 3.15.0'
+  s.dependency 'QuCore-ThirdParty', ' ~> 3.15.0'
+  s.dependency 'AlivcConan', '~> 1.0.3'
+  s.dependency 'VODUpload'
+  s.dependency 'AliyunOSSiOS'
+  s.static_framework = true
 
   # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
   s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }