iOS中将多张图片合成为可导出可播放的视频文件(Swift 3)

34.UIImage — 把多张图片合成一张图 UIImage — 把多张图片合成一张图 阅读详情

最近在做一个东西的时候,需要把一张或者多张图片合成为一个视频文件,并加入到视频轨道中进行播放或者导出,而不单单是把图片当做水印加到现有的视频上,做的时候首先考虑到的当然是AVAssetWriter,但是在做的时候还是遇到了很多问题,首先我用的是swift 3,翻遍了StackOverFlow也没有找到合适的,当然也有点偷懒的意思,想直接搜来完事儿。

先上个链接,思路和方法姿势全都有了,http://stackoverflow.com/questions/3741323/how-do-i-export-uiimage-array-as-a-movie?noredirect=1&lq=1,具体的步骤及涉及到点还请点看来看下,当然如果是为了省事儿,下面我也会直接给出demo,还合适你拿去就好。

第一次做的时候,因为里面的很多接口及操作都是异步的方式,但是当在测试的时候,发现我现有流程的代码中用需要和其他流程的代码同步,所以又改成了同步的方式。下面就直接贴代码了:

异步的方式:

//
//  DOVImagesToVideoAsync.swift
//  ImagesToVideo
//
//  Created by Wulei on 2016/12/24.
//  Copyright © 2016年 wulei. All rights reserved.
//

import Foundation
import AVFoundation
import UIKit

typealias DOVMovieMakerCompletion = (URL) -> Void
fileprivate typealias DOVMovieMakerUIImageExtractor = (AnyObject) -> UIImage?

class DOVImageToVideoAsync: NSObject{
    
    //MARK: Private Properties
    
    private var assetWriter:AVAssetWriter!
    private var writeInput:AVAssetWriterInput!
    private var bufferAdapter:AVAssetWriterInputPixelBufferAdaptor!
    private var videoSettings:[String : Any]!
    private var frameTime:CMTime!
    private var fileURL:URL!
    private var completionBlock: DOVMovieMakerCompletion?
    
    //MARK: Class Method
    
    class func videoSettings(codec:String, width:Int, height:Int) -> [String: Any]{
        if(Int(width) % 16 != 0){
            print("warning: video settings width must be divisible by 16")
        }
        
        let videoSettings:[String: Any] = [AVVideoCodecKey: AVVideoCodecH264,
                                           AVVideoWidthKey: width,
                                           AVVideoHeightKey: height]
        
        return videoSettings
    }
    
    //MARK: Public methods
    
    init(videoSettings: [String: Any]) {
        super.init()
        
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        var tempPath:String
        repeat{
            let random = arc4random()
            tempPath = paths[0] + "/\(random).mp4"
        }while(FileManager.default.fileExists(atPath: tempPath))
        //        let tempPath = paths[0] + "/exprotvideo.mp4"
        //        if(FileManager.default.fileExists(atPath: tempPath)){
        //            guard (try? FileManager.default.removeItem(atPath: tempPath)) != nil else {
        //                print("remove path failed")
        //                return
        //            }
        //        }
        
        self.fileURL = URL(fileURLWithPath: tempPath)
        self.assetWriter = try! AVAssetWriter(url: self.fileURL, fileType: AVFileTypeQuickTimeMovie)
        
        self.videoSettings = videoSettings
        self.writeInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
        assert(self.assetWriter.canAdd(self.writeInput), "add failed")
        
        self.assetWriter.add(self.writeInput)
        let bufferAttributes:[String: Any] = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB)]
        self.bufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: self.writeInput, sourcePixelBufferAttributes: bufferAttributes)
        self.frameTime = CMTimeMake(1, 1)
    }
    
    func createMovieFrom(urls: [URL], withCompletion: @escaping DOVMovieMakerCompletion){
        self.createMovieFromSource(images: urls as [AnyObject], extractor:{(inputObject:AnyObject) ->UIImage? in
            return UIImage(data: try! Data(contentsOf: inputObject as! URL))}, withCompletion: withCompletion)
    }
    
    func createMovieFrom(images: [UIImage], withCompletion: @escaping DOVMovieMakerCompletion){
        self.createMovieFromSource(images: images, extractor: {(inputObject:AnyObject) -> UIImage? in
            return inputObject as? UIImage}, withCompletion: withCompletion)
    }
    
    //MARK: Private methods
    
    private func createMovieFromSource(images: [AnyObject], extractor: @escaping DOVMovieMakerUIImageExtractor, withCompletion: @escaping CXEMovieMakerCompletion){
        self.completionBlock = withCompletion
        
        self.assetWriter.startWriting()
        self.assetWriter.startSession(atSourceTime: kCMTimeZero)
        
        let mediaInputQueue = DispatchQueue(label: "mediaInputQueue")
        var i = 0
        let frameNumber = images.count
        self.writeInput.requestMediaDataWhenReady(on: mediaInputQueue){
            while(true){
                if(i >= frameNumber){
                    break
                }
                
                if (self.writeInput.isReadyForMoreMediaData){
                    var sampleBuffer:CVPixelBuffer?
                    autoreleasepool{
                        let img = extractor(images[i])
                        if img == nil{
                            i += 1
                            print("Warning: counld not extract one of the frames")
                            //                            continue
                        }
                        sampleBuffer = self.newPixelBufferFrom(cgImage: img!.cgImage!)
                    }
                    if (sampleBuffer != nil){
                        if(i == 0){
                            self.bufferAdapter.append(sampleBuffer!, withPresentationTime: kCMTimeZero)
                        }else{
                            let value = i - 1
                            let lastTime = CMTimeMake(Int64(value), self.frameTime.timescale)
                            let presentTime = CMTimeAdd(lastTime, self.frameTime)
                            self.bufferAdapter.append(sampleBuffer!, withPresentationTime: presentTime)
                        }
                        i = i + 1
                    }
                }
            }
            self.writeInput.markAsFinished()
            self.assetWriter.finishWriting {
                DispatchQueue.main.sync {
                    self.completionBlock!(self.fileURL)
                }
            }
        }
    }
    
    private func newPixelBufferFrom(cgImage:CGImage) -> CVPixelBuffer?{
        let options:[String: Any] = [kCVPixelBufferCGImageCompatibilityKey as String: true, kCVPixelBufferCGBitmapContextCompatibilityKey as String: true]
        var pxbuffer:CVPixelBuffer?
        let frameWidth = self.videoSettings[AVVideoWidthKey] as! Int
        let frameHeight = self.videoSettings[AVVideoHeightKey] as! Int
        
        let status = CVPixelBufferCreate(kCFAllocatorDefault, frameWidth, frameHeight, kCVPixelFormatType_32ARGB, options as CFDictionary?, &pxbuffer)
        assert(status == kCVReturnSuccess && pxbuffer != nil, "newPixelBuffer failed")
        
        CVPixelBufferLockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
        let pxdata = CVPixelBufferGetBaseAddress(pxbuffer!)
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        let context = CGContext(data: pxdata, width: frameWidth, height: frameHeight, bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pxbuffer!), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)
        assert(context != nil, "context is nil")
        
        context!.concatenate(CGAffineTransform.identity)
        context!.draw(cgImage, in: CGRect(x: 0, y: 0, width: cgImage.width, height: cgImage.height))
        CVPixelBufferUnlockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
        return pxbuffer
    }
}

同步的方式:

//
//  DOVImagesToVideoSync.swift
//  ImagesToVideo
//
//  Created by Wulei on 2016/12/24.
//  Copyright © 2016年 wulei. All rights reserved.
//

import Foundation
import AVFoundation
import UIKit

fileprivate typealias DOVMovieMakerUIImageExtractor = (AnyObject) -> UIImage?


class DOVImageToVideoSync: NSObject{
    
    //MARK: Private Properties
    
    private var assetWriter:AVAssetWriter!
    private var writeInput:AVAssetWriterInput!
    private var bufferAdapter:AVAssetWriterInputPixelBufferAdaptor!
    private var videoSettings:[String : Any]!
    private var frameTime:CMTime!
    private var fileURL:URL!
    
    //MARK: Class Method
    
    class func videoSettings(codec:String, width:Int, height:Int) -> [String: Any]{
        if(Int(width) % 16 != 0){
            print("warning: video settings width must be divisible by 16")
        }
        
        let videoSettings:[String: Any] = [AVVideoCodecKey: AVVideoCodecH264,
                                           AVVideoWidthKey: width,
                                           AVVideoHeightKey: height]
        
        return videoSettings
    }
    
    //MARK: Public methods
    
    init(videoSettings: [String: Any]) {
        super.init()
        
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        var tempPath:String
        repeat{
            let random = arc4random()
            tempPath = paths[0] + "/\(random).mp4"
        }while(FileManager.default.fileExists(atPath: tempPath))
        
        self.fileURL = URL(fileURLWithPath: tempPath)
        self.assetWriter = try! AVAssetWriter(url: self.fileURL, fileType: AVFileTypeQuickTimeMovie)
        
        self.videoSettings = videoSettings
        self.writeInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
        assert(self.assetWriter.canAdd(self.writeInput), "add failed")
        
        self.assetWriter.add(self.writeInput)
        let bufferAttributes:[String: Any] = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB)]
        self.bufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: self.writeInput, sourcePixelBufferAttributes: bufferAttributes)
        self.frameTime = CMTimeMake(600, 600)
    }
    
    func createMovieFrom(url: URL, duration:Int) -> URL{
        var urls = [URL]()
        var index = duration
        while(index > 0){
            urls.append(url)
            index -= 1
        }
        return self.createMovieFromSource(images: urls as [AnyObject], extractor:{(inputObject:AnyObject) ->UIImage? in
            return UIImage(data: try! Data(contentsOf: inputObject as! URL))})
    }
    
    func createMovieFrom(image: UIImage, duration:Int) -> URL{
        var images = [UIImage]()
        var index = duration
        while(index > 0){
            images.append(image)
            index -= 1
        }
        return self.createMovieFromSource(images: images, extractor: {(inputObject:AnyObject) -> UIImage? in
            return inputObject as? UIImage})
    }
    
    //MARK: Private methods
    
    private func createMovieFromSource(images: [AnyObject], extractor: @escaping DOVMovieMakerUIImageExtractor) -> URL{
        
        self.assetWriter.startWriting()
        //        self.assetWriter.startSession(atSourceTime: kCMTimeZero)
        let zeroTime = CMTimeMake(Int64(0),self.frameTime.timescale)
        self.assetWriter.startSession(atSourceTime: zeroTime)
        
        var i = 0
        let frameNumber = images.count
        
        while !self.writeInput.isReadyForMoreMediaData {}
        
        while(true){
            if(i >= frameNumber){
                break
            }
            
            if (self.writeInput.isReadyForMoreMediaData){
                var sampleBuffer:CVPixelBuffer?
                autoreleasepool{
                    let img = extractor(images[i])
                    if img == nil{
                        i += 1
                        print("Warning: counld not extract one of the frames")
                        //                            continue
                    }
                    sampleBuffer = self.newPixelBufferFrom(cgImage: img!.cgImage!)
                }
                if (sampleBuffer != nil){
                    if(i == 0){
                        self.bufferAdapter.append(sampleBuffer!, withPresentationTime: kCMTimeZero)
                    }else{
                        let value = i - 1
                        let lastTime = CMTimeMake(Int64(value), self.frameTime.timescale)
                        let presentTime = CMTimeAdd(lastTime, self.frameTime)
                        self.bufferAdapter.append(sampleBuffer!, withPresentationTime: presentTime)
                    }
                    i = i + 1
                }
            }
        }
        self.writeInput.markAsFinished()
        self.assetWriter.finishWriting {}
        
        var isSuccess:Bool = false
        while(!isSuccess){
            switch self.assetWriter.status {
            case .completed:
                isSuccess = true
                print("completed")
            case .writing:
                sleep(1)
                print("writing")
            case .failed:
                isSuccess = true
                print("failed")
            case .cancelled:
                isSuccess = true
                print("cancelled")
            default:
                isSuccess = true
                print("unknown")
            }
        }
        return self.fileURL
    }
    
    private func newPixelBufferFrom(cgImage:CGImage) -> CVPixelBuffer?{
        let options:[String: Any] = [kCVPixelBufferCGImageCompatibilityKey as String: true, kCVPixelBufferCGBitmapContextCompatibilityKey as String: true]
        var pxbuffer:CVPixelBuffer?
        let frameWidth = self.videoSettings[AVVideoWidthKey] as! Int
        let frameHeight = self.videoSettings[AVVideoHeightKey] as! Int
        
        let status = CVPixelBufferCreate(kCFAllocatorDefault, frameWidth, frameHeight, kCVPixelFormatType_32ARGB, options as CFDictionary?, &pxbuffer)
        assert(status == kCVReturnSuccess && pxbuffer != nil, "newPixelBuffer failed")
        
        CVPixelBufferLockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
        let pxdata = CVPixelBufferGetBaseAddress(pxbuffer!)
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        let context = CGContext(data: pxdata, width: frameWidth, height: frameHeight, bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pxbuffer!), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)
        assert(context != nil, "context is nil")
        
        context!.concatenate(CGAffineTransform.identity)
        context!.draw(cgImage, in: CGRect(x: 0, y: 0, width: cgImage.width, height: cgImage.height))
        CVPixelBufferUnlockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
        return pxbuffer
    }
}


运行结果截图:


Demo程序:https://github.com/Willib/ImagesToVideo

iOS -- 图片数组 生成视频 一键成片 可以直接拷贝到工具类中使用 import UIKit import AVFoundation enum AnimationType { case fromRightToLeft case fromLowerToUpper case zoomOut case zoomIn case rotate case fadeInFadeOut } class ImageAnimationTool { static let shared = I 阅读详情

相关推荐

Swift实战:5分钟搞定iOS动态壁纸制作(附完整Demo)

本文提供了一份详尽的Swift实战指南,教你如何在5分钟内制作iOS动态壁纸。文章深入解析了实况照片(Live Photo)作为动态壁纸核心载体的原理,并提供了从资源准备、元数据注入到相册保存的完整代码实现与Demo,帮助开发者快速掌握这一炫酷功能。

s3t4u的博客 476

swift-video-generator:图片加音频生成视频及多视频合并库及演示

阅读 80 收藏 9 2017-11-07 原文链接:github.com 腾讯云学生优惠套餐,985高校学习云计算的主力机型,2G2核,1M带宽系统盘(Linux 50G/Windows 50G)免费赠送50GB对象存储空间还有.cn域名一年使用权!不要犹豫了,立即来腾讯云抢购!https://cloud.tencent.com/act/campus    

cleven 2142

swift-swift-video-generator用图片加音频生成视频及多视频合并的库

该库提供了一种将图像和音频合并为视频或将多个视频合并为一个视频的简单方法

iOS UIImagePickerController录制视频转为mp4格式

http://stackoverflow.com/questions/16894399/avassetexportsession-closes-uiimagepickercontroller http://stackoverflow.com/questions/11751883/how-can-i-reduce-the-file-size-of-a-video-created-with-uiim

ALDRIDGE1的专栏 4709

Gromacs实战:从蛋白质-小分子对接到分子动力学模拟全流程解析

本文详细解析了使用Gromacs进行蛋白质-小分子对接和分子动力学模拟的全流程,包括文件准备、拓扑生成、复合物构建、溶剂化处理、能量最小化及平衡模拟等关键步骤。通过实战案例,帮助研究者验证药物分子与靶标蛋白的结合稳定性,提升分子动力学模拟的效率和准确性。

weixin_29216353的博客 380

我用 AI Agent 全自动跑 COMSOL 仿真:从建模到后处理,踩了 10+ 个深坑

本文探讨了利用AI通过COMSOL Java API实现AI芯片液冷冷板全流程自动化仿真的实践。针对700W高功耗AI芯片的散热需求,构建了包含4颗Chiplet和微通道冷板的三维液冷模型。通过严格设计的Prompt工程,AI Agent成功完成了从几何建模到结果验证的全过程,最终实现了芯片最高温度54°C的良好散热效果。文章重点分享了10个关键问题的解决经验,包括流体连通性验证、网格顺序控制、离散阶数选择、收敛判据优化等,并建立了包含流场/热场/网格独立性的三重验证体系。实践表明,AI可高效处理重复性工作

sjjsjjsjjsjjsjj的博客 970

iOS多张图片合成一个视频

IOS多张图片合成一个视频 - (void)viewDidLoad { [superviewDidLoad]; //Do any additional setup after loading the view. imageArr =[[NSMutableArrayalloc]initWithObjects: [UIImageimag

BearsG 2500

手把手教你用VCS+VERDI搭建数字IC验证环境(附Makefile避坑指南)

本文详细介绍了如何使用VCS编译器和VERDI调试工具搭建数字IC验证环境,涵盖从RTL设计到波形分析的全流程。重点解析了Makefile自动化构建、FSDB波形生成方法及常见问题解决方案,帮助工程师高效完成验证工作。特别提供了VCS编译参数配置和VERDI调试技巧的实用指南。

weixin_33738578的博客 447

ios开发 把视频文件和图片保存到相册

保存图片: UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); 保存图片成功或失败的回调方法: - (void) image: (UIImage*)image didFinishSavingWithError: (NSErr

kaven_lv的专栏 1万+

Windows本地部署Qwen3-14B实战指南:Ollama+Open WebUI零Docker方案

大语言模型本地部署是AI工程化落地的关键环节,其核心在于推理框架兼容性、硬件加速适配性与端到端稳定性。Ollama作为轻量级模型运行时,原生支持Windows DirectML与CUDA,规避了Docker/WSL2虚拟化开销和llama.cpp编译风险;Qwen3-14B则凭借128K上下文、Tool Calling原生支持及中文长文本结构化能力,成为政务、金融等场景首选。本文聚焦Windows平台真实约束——显存阈值、驱动版本匹配、路径编码兼容、离线知识库集成(RAG)等工程细节,提供经63台Windo

weixin_32136203的博客 413

【数据集】基于ubESTARFM法的100m 地温LST数据集(澳大利亚)

【数据集】基于ubESTARFM法的100m 地温LST数据集(澳大利亚)

WW、forever的博客 265

swift 从手机选照片_纯swift版本照片选择器

CLImagePickerTool 照片选择器github。要求iOS 8.0+swift 3.0+主要功能:图片多选,设置最大可选择的照片数量,对选择后的照片进行压缩处理点击图片进行查看,可缩放视频预览、视频播放、照片预览屏蔽视频文件屏蔽图片资源显示视频资源重置选中状态、预览、异步下载图片视频文件和图片文件不能同时选择图片编辑操作(马赛克,涂鸦,性能得到很大改善)下载iCloud中的照片或者视频...

weixin_39597262的博客 417

《谷歌数据分析方法》脑图笔记

目录壹、脑图笔记贰、《谷歌数据分析方法》脑图文稿[^1]一、 反思数据分析二、选择数据分析工具的最佳策略第一步:选择数据分析工具前要回答的3个关键问题第二步:选择数据分析工具前要问供应商的10个问题第三步:如何有效地试用数据分析工具第四步:确定合作前的协议,检查供应商的服务条款三、点击流分析的精彩世界:指标8个关键网站指标数据分析指标揭秘关键网站指标的战略性策略四、成功的关键:衡量绩效关注“少数关键指标”衡量非电子商务网站的成功衡量B2B网站五、调研和测试永不过时的3个最佳调研问题选择线上调研供应商的8个建

积跬步,慕至千里的博客 2927

iOS中系统的PHAsset图片、视频计算存储大小

//相簿列表项 struct HGImageAlbumItem { //相簿名称 var title:String? //相簿内的资源 var fetchResult:NSMutableArray? } 第一种:1. 计算系统的图片大小 // MARK: 统计系统图片的总大小 func statisticPictureAssetsAllSiz...

天道酬勤 4379

【华为OD机试】创建二叉树(python, java, c++, js)

请按下列描达构建一颗二叉树,并返回该树的根节点: 1、先创建值为-1的根结点,根节点在第0层; 2、然后根据operations依次添加节点: operations[i] = [height, index] 表示对第 height 层的第index 个节点node, 添加值为i 的子节点: 若node 无「左子节点」。则添加左子节点; 若node 有「左子节点」但无「右子节点」,则添加右子节点: 否则不作任何处理。 height、index 均从0开始计数;index 指所在层的创建顺序。

A_D_I_D_A_S的博客 188

置顶必读(2) |《滚雪球学数学建模》专栏目录 & 历年国赛/美赛真题库 & 真题解析(持续更新中)

本专栏《滚雪球学数学建模》采用“从零开始、逐步滚雪球”的学习路径,把数学建模的基础理论、方法工具与大量实战真题(尤其是国赛题,涵盖2025-1992所有真题)有机融合,形成一套系统化训练路线。非常适合零基础入门、希望提升模型与工程化能力的本科/研究生,以及备战全国大学生数学建模竞赛(国赛/省赛)的同学。加油,我们一起把建模能力打造成可迁移的核心竞争力!

**My Coding Family** 26万+

如何在iOS聊天应用中快速添加图片分享和文件传输功能

想要为你的iOS聊天应用添加强大的图片分享和文件传输功能吗?✨ 本文将为你详细介绍如何基于Messenger-iOS-chat-swift-firestore项目扩展多媒体消息功能,打造更丰富的实时聊天体验。这个基于Swift和Firebase Firestore的聊天应用已经具备了实时文本通信基础,通过简单的扩展就能实现完整的图片分享和文件传输功能。 ## 📱 项目概述与核心架构 Mess

gitblog_00132的博客 1087

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

iOS 视频文件和图片文件缓存路径

let name = currentTimeStamp() + ".mp4"let temporaryFile = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent(name) movieURL = NSURL(fileURLWithPath: temporaryFile)

Quinn's blog ! I'm glad to be here! 3628

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

如何快速集成HXPhotoPicker:iOS终极图片/视频选择与编辑神器

在iOS应用开发中,高效的图片/视频选择与编辑功能是提升用户体验的核心。**HXPhotoPicker**作为一款开源的图片/视频选择器,完美支持LivePhoto、GIF图片选择、3DTouch预览、iCloud资源在线下载及全功能编辑工具,让开发者轻松实现媲美微信的媒体处理体验。本文将带你探索这款工具的强大功能与快速集成指南。 ## ???? 为什么选择HXPhotoPicker?5大核心优势...

gitblog_00262的博客 1026

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

iOS使用Carrierwave进行图片和视频的上传(swift3.0)

废话不多说,直接上代码 首先你要去CRVNetworkManager.h这个类里去修改CRVDefaultPath上传路径的后缀,之后就可以开始上代码了 一.上传图片代码 二.上传拍摄的视频,因为iphone拍摄的视频格式是MOV的,但是Carrierwave只支持以下格式的视频文件上传:asf, avi, dvr-ms, m1v, m4v, mp2, mp2v, mp4, mpe,

我爱罗翠的博客 1176

HXPhotoPicker终极指南:如何在iOS应用中实现专业级图片选择器

HXPhotoPicker是一个功能强大的iOS图片/视频选择器,支持LivePhoto、GIF图片选择、3DTouch预览、在线下载iCloud资源以及图片/视频编辑功能。这个开源项目模仿了微信的图片选择器,为开发者提供了完整的图片选择解决方案。 ## 🎯 为什么选择HXPhotoPicker? HXPhotoPicker提供了**专业级**的图片选择体验,支持**多种媒体类型**和**丰

gitblog_00743的博客 584

iOS 选择图片过滤掉视频文件

UIImagePickerController *pickerImage = [[UIImagePickerController alloc] init]; pickerImage.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; pickerImage.mediaTypes = [[NSArray alloc] i

乌鸦的博客 1680

SwiftVideoGenerator 视频配乐指南:mergeVideoWithAudio 为视频替换背景音乐的完整方法

想把视频里枯燥的原始音轨换成动听的背景音乐?SwiftVideoGenerator 正是解决这个问题的开源神器。作为一款专为 iOS 打造的 Swift 视频处理库,SwiftVideoGenerator 提供了简单到极致的 `mergeVideoWithAudio` 方法,只需传入视频和音频两个 URL,就能一键完成视频背景音乐替换。本指南将带你从零开始,掌握用 mergeVideoWithAu

gitblog_00425的博客 257

iOS 保存图片视频到手机相册

//保存相册UIKIT_EXTERN void UIImageWriteToSavedPhotosAlbum(UIImage *image,__nullableid completionTarget,__nullableSEL completionSelector,void *__nullable contextInfo)__TVOS_PROHIBITED;//xcode中的方法 U...

代码解释生活 2237

SwiftVideoGenerator 视频裁剪教程:splitVideo 精准截取任意时间片段的 3 个步骤

做 iOS 视频编辑功能时,**视频裁剪**(截取任意时间片段)是最常见也最实用的需求。SwiftVideoGenerator 是一个开源的 Swift 视频处理库,内置了强大的 **splitVideo 视频裁剪方法**,一行代码就能从视频中精准截取任意时间范围,并输出为独立的新视频文件。本教程用 3 个步骤带你快速上手 SwiftVideoGenerator 视频裁剪,零基础也能轻松实现 iO

gitblog_00251的博客 957
上一篇: iOS内嵌播放视图到View实现单帧控制(Swift)
下一篇: iOS中自定义进度条设置半透明背景(Swift 3)
Willib
博客等级 码龄14年 83粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值