Explorar el Código

1. 图片转视频改用 AVFoundation 来生成
2. 优化内存

zhongbaojian hace 5 años
padre
commit
b3a4b731c0

+ 1 - 1
SuperShow/OJAGPUImageDecoder/OJADecoder.swift

@@ -241,7 +241,7 @@ extension OJADecoder {
                          这个问题只有在图片后面接了其他filter之后会出现
                          因为接了filter之后,filter的outputFrame就不是图片的outputFrame了
                          */
-                        for input in lastInputArray {
+                        for input in self.lastInputArray {
                             autoreleasepool {
                                 if let mMovieInput = input as? OJAMovieFrameInput {
                                     if let inputBuffer = mMovieInput.readVideoSampleBuffer() {

+ 39 - 24
SuperShow/Tool/WSSMediaOperationTool.swift

@@ -45,38 +45,49 @@ class WSSMediaOperationTool: NSObject {
                           outputPath: String,
                           progressHandle: ((Float) -> Void)? = nil,
                           finishHandle: ((OJAImageVideoDecoder.OJAImageVideoDecoderState, String?) -> Void)? = nil) {
-        //
-        imageVideoDecoder?.endProgressing()
-        imageVideoDecoder?.progressChangeCallBack = nil
-        imageVideoDecoder?.finishCombineCallBack = nil
-        imageVideoDecoder = nil
-        // 如果已经存在文件,先删除
-        try? FileManager.default.removeItem(at: URL(fileURLWithPath: outputPath))
+        // 使用AVFoundation生成视频
+        if let model = images.first, let image = model.sourceImage {
+            let duration = model.endSeconds - model.startSeconds
 
-        imageVideoDecoder = OJAImageVideoDecoder(imageDataArray: images, totalSeconds: duration)
-        imageVideoDecoder?.outputPath = outputPath
-        imageVideoDecoder?.progressChangeCallBack = { pg in
-            progressHandle?(pg)
-        }
-        imageVideoDecoder?.finishCombineCallBack = { state, outputPath in
-            finishHandle?(state, outputPath)
-        }
+            WSSMediaOperationTool.makeVideo(withImage: image, duration: duration, outputPath: outputPath) { path in
+                finishHandle?(.finish, path)
+            }
 
-        DispatchQueue.main.async { [weak self] in
-            self?.imageVideoDecoder?.startProgressing()
+            progressHandle?(1.0)
         }
+
+        // 使用GPUImage生成视频
+//        imageVideoDecoder?.endProgressing()
+//        imageVideoDecoder?.progressChangeCallBack = nil
+//        imageVideoDecoder?.finishCombineCallBack = nil
+//        imageVideoDecoder = nil
+//        // 如果已经存在文件,先删除
+//        try? FileManager.default.removeItem(at: URL(fileURLWithPath: outputPath))
+//
+//        imageVideoDecoder = OJAImageVideoDecoder(imageDataArray: images, totalSeconds: duration)
+//        imageVideoDecoder?.outputPath = outputPath
+//        imageVideoDecoder?.progressChangeCallBack = { pg in
+//            progressHandle?(pg)
+//        }
+//        imageVideoDecoder?.finishCombineCallBack = { state, outputPath in
+//            finishHandle?(state, outputPath)
+//        }
+//
+//        DispatchQueue.main.async { [weak self] in
+//            self?.imageVideoDecoder?.startProgressing()
+//        }
     }
 
     func endImage2Video() {
-        imageVideoDecoder?.endProgressing()
+//        imageVideoDecoder?.endProgressing()
     }
 
     func releaseObjs() {
         //
-        if imageVideoDecoder != nil {
-            imageVideoDecoder?.endProgressing()
-            imageVideoDecoder = nil
-        }
+//        if imageVideoDecoder != nil {
+//            imageVideoDecoder?.endProgressing()
+//            imageVideoDecoder = nil
+//        }
     }
 }
 
@@ -953,8 +964,12 @@ extension WSSMediaOperationTool {
     ///   - duration: 生成的视频时长,单位秒
     ///   - finish: 生成视频结果回调
     class func makeVideo(withImage image: UIImage, duration: TimeInterval, outputPath: String, finish: ((String?) -> Void)?) {
-        WSSUIImageTool.createVideo(with: image, videoDuration: duration, outputPath: outputPath) { path in
-            finish?(path)
+        DispatchQueue.global(qos: .userInteractive).async {
+            WSSUIImageTool.createVideo(with: image, videoDuration: duration, outputPath: outputPath) { path in
+                DispatchQueue.main.async {
+                    finish?(path)
+                }
+            }
         }
     }
 

+ 8 - 7
SuperShow/Tool/WSSUIImageTool.m

@@ -7,6 +7,7 @@
 //
 
 #import "WSSUIImageTool.h"
+#import "UIImage+OJAKit.h"
 
 @implementation WSSUIImageTool
 
@@ -18,8 +19,8 @@
     //设置mov路径
     NSString *moviePath = outputPath;
     //定义视频的大小,需要是 320x480 倍数
-    CGSize size = CGSizeMake(640, 960);
-    UIImage *resizeImage = [self imageWithImage:image scaledToSize:size];
+    CGSize size = CGSizeMake(960, 1440);
+    UIImage *resizeImage = [image oja_imageByResizeToSize:size contentMode:UIViewContentModeScaleAspectFit];
     // 转成UTF-8编码
     unlink([moviePath UTF8String]);
 
@@ -57,21 +58,21 @@
     dispatch_queue_t dispatchQueue = dispatch_queue_create("wss-write-video", NULL);
     CVPixelBufferRef pixelBuffer = [self pixelBufferFromUIImage:resizeImage size:size];
     __weak typeof(writerInput) weakInput = writerInput;
-    __weak typeof(videoWriter) weakWriter = videoWriter;
+//    __weak typeof(videoWriter) weakWriter = videoWriter;
     [writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{
         __strong typeof(weakInput) strongInput = weakInput;
-        __strong typeof(weakWriter) strongWriter = weakWriter;
+//        __strong typeof(weakWriter) strongWriter = weakWriter;
 
         if (pixelBuffer) {
             if ([adaptor appendPixelBuffer:pixelBuffer withPresentationTime:kCMTimeZero]) {
                 DLog(@"###: 第一帧添加成功");
 
                 if ([adaptor appendPixelBuffer:pixelBuffer withPresentationTime:CMTimeMakeWithSeconds(duration, 600)]) {
-                    DLog(@"###: 第二帧添加成功");
+                    DLog(@"###: 第二帧添加成功 >>> %@ + %@", strongInput, videoWriter);
 
                     [strongInput markAsFinished];
-                    [strongWriter endSessionAtSourceTime:CMTimeMakeWithSeconds(duration, 600)];
-                    [strongWriter finishWritingWithCompletionHandler:^{
+                    [videoWriter endSessionAtSourceTime:CMTimeMakeWithSeconds(duration, 600)];
+                    [videoWriter finishWritingWithCompletionHandler:^{
                         DLog(@"###: 完成视频生成");
                         finishHandler(moviePath);
                     }];

+ 11 - 1
SuperShow/UI/ResourcePicker/WSSResourcePickerController.swift

@@ -42,6 +42,8 @@ class WSSResourcePickerController: MTViewController {
         return operationQueue
     }()
 
+    fileprivate var isStopImage2Video: Bool = false
+
     fileprivate var confirmSelectedModels = [WSSResourceSelectedModel]()
 
     // MARK: ====== UI ======
@@ -180,7 +182,8 @@ class WSSResourcePickerController: MTViewController {
         refreshState()
 
         NotificationCenter.default.reactive.notifications(forName: Notification.Name.kSVProgressHUDCacnel).take(duringLifetimeOf: self).observeValues { [weak self] _ in
-            self?.mediaTool.endImage2Video()
+//            self?.mediaTool.endImage2Video()
+            self?.isStopImage2Video = true
         }
     }
 
@@ -333,6 +336,7 @@ class WSSResourcePickerController: MTViewController {
             dataArray.append(model)
         }
 
+        isStopImage2Video = false
         composePhotoVideo(imageModels: dataArray, maxImageCount: dataArray.count)
     }
 
@@ -355,6 +359,12 @@ class WSSResourcePickerController: MTViewController {
     }
 
     func composePhotoVideo(imageModels: [OJASourceImageModel], maxImageCount: Int) {
+        if isStopImage2Video {
+            /// 手动停止
+            showHud(withOnlyText: "取消合成")
+            return
+        }
+
         var imageDatas = imageModels
 
         let outputPath = kOJSUserCacheDirectory + "/ImageVieo_\(imageDatas.count)_" + String(format: "%.f", Date().timeIntervalSince1970) + ".mp4"

+ 28 - 1
SuperShow/UI/Template/WSSCreativeTemplateEditViewController.swift

@@ -51,6 +51,9 @@ class WSSCreativeTemplateEditViewController: WSSMultiVideoEditViewController {
 
             hideHud()
         }
+
+        // ---
+        GPUImageContext.sharedFramebufferCache()?.purgeAllUnassignedFramebuffers()
     }
 
     override func viewWillDisappear(_ animated: Bool) {
@@ -59,6 +62,17 @@ class WSSCreativeTemplateEditViewController: WSSMultiVideoEditViewController {
         videoPlayer?.pause()
     }
 
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        if videoPlayer == nil {
+            // 恢复视频
+            if let firstModel = self.creativeVideoInfoArray.safeObject(atIndex: self.selectedIndexPath.row) {
+                changePlayer(withVideoInfo: firstModel, isPlay: false)
+            }
+        }
+    }
+
     override func setupSubViews() {
         super.setupSubViews()
 
@@ -118,6 +132,17 @@ class WSSCreativeTemplateEditViewController: WSSMultiVideoEditViewController {
         fatalError("init(coder:) has not been implemented")
     }
 
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+
+        if isCurrentPageShowing == false {
+            videoPlayer?.reset()
+            videoPlayer = nil
+
+            WSSSimpleLog("@@@: 释放视频播放器")
+        }
+    }
+
     override func releaseObjects() {
         super.releaseObjects()
 
@@ -212,7 +237,7 @@ private extension WSSCreativeTemplateEditViewController {
                                             videoSize: videoSize,
                                             videoDegress: WSSMediaOperationTool.degressFromVideo(asset: videoModel.vaildVideoAsset),
                                             mediaModel: mediaModel)
-            
+
             // 创建临时视图做初始化
             let cropView = WSSVideoCropView(frame: videoCropView?.frame ?? CGRect.zero, videoCropModel: cropModel)
             cropView.changeCropModel(cropModel, isInit: true)
@@ -383,6 +408,8 @@ fileprivate extension WSSCreativeTemplateEditViewController {
             }
         }
 
+        WSSSimpleLog("🥗🥗🥗: \(templateSize)")
+
         try? FileManager.default.removeItem(atPath: outputURLString)
         let movieWriter = OJAImageMovieWriter(movieURL: outputURL, size: templateSize)
         movieWriter?.shouldPassthroughAudio = true

+ 22 - 5
SuperShow/UI/Template/WSSTemplateDetailViewController.swift

@@ -78,10 +78,10 @@ class WSSTemplateDetailViewController: MTViewController {
             .observeValues { [weak self] _ in
                 self?.refreshTemplates()
             }
-        
+
         // 监听前后台切换
         NotificationCenter.default.reactive.notifications(forName: UIApplication.didBecomeActiveNotification).take(duringLifetimeOf: self).observeValues { [weak self] _ in
-            guard let viewIsAppear = self?.viewIsAppear, viewIsAppear == true else {return}
+            guard let viewIsAppear = self?.viewIsAppear, viewIsAppear == true else { return }
             if let state = self?.videoPlayer?.playerStatus, state != .playing {
                 self?.videoResumePlay()
             }
@@ -97,7 +97,6 @@ class WSSTemplateDetailViewController: MTViewController {
         NotificationCenter.default.reactive.notifications(forName: Notification.Name.kEditVideoFinish).take(duringLifetimeOf: self).observeValues { [weak self] _ in
             self?.releaseObjects()
         }
-        
 
         // ---
         videoContainer.appendTemplateModels(templateList)
@@ -117,8 +116,15 @@ class WSSTemplateDetailViewController: MTViewController {
     override func viewDidAppear(_ animated: Bool) {
         super.viewDidAppear(animated)
 
-        if videoPlayer?.playerStatus != .playing {
-            videoResumePlay()
+        if videoPlayer == nil {
+            // 恢复视频播放
+            if let showingItem = self.videoContainer.currentShowingItem(), let model = showingItem.templateModel {
+                playVideo(withTemplate: model)
+            }
+        } else {
+            if videoPlayer?.playerStatus != .playing {
+                videoResumePlay()
+            }
         }
     }
 
@@ -148,6 +154,17 @@ class WSSTemplateDetailViewController: MTViewController {
         super.exitPage()
     }
 
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+
+        videoContainer.recycleMemory()
+
+        videoPlayer?.reset()
+        videoPlayer = nil
+        
+        WSSSimpleLog("@@@: 释放播放器")
+    }
+
     private func releaseVideo() {
         videoPlayer?.removeFromSuperview()
         videoPlayer?.reset()