Pixel SDK
Pixel SDK 是一个用 Swift 编写的照片和视频编辑框架。
功能
要求
- iPhone或iPad
- iOS 11+
- Xcode 11.4+
开始使用
注意:请查阅Xcode 示例项目以获取大量的示例代码。
CocoaPods
CocoaPods是iOS项目的依赖管理器。要使用CocoaPods将PixelSDK集成到您的Xcode项目中,首先请确认您已安装至少Xcode 11.4或更高版本。
然后,确保您安装了最新版本的CocoaPods。运行以下命令:
$ sudo gem install cocoapods
在您的Podfile
中指定PixelSDK
pod 'PixelSDK'
从您的项目目录中运行以下命令
$ pod install
Swift包管理器
Swift包管理器是Xcode内置的依赖管理器。要使用Swift包管理器将PixelSDK集成到您的Xcode项目中,首先请确认您已安装最新版本的Xcode。
在Xcode菜单栏中选择文件 > 添加包,并在搜索栏中输入以下存储库URL
https://github.com/GottaYotta/PixelSDK.git
选择依赖规则为 分支
和 master
。然后按“添加包”按钮。
注意:我们不推荐将依赖规则设置为一个大版本,因为这将阻止您在将来接收关键错误修复。
设置
将以下行包含在您的应用程序的Info.plist
中
<key>NSPhotoLibraryUsageDescription</key>
<string>Photo access is needed so you can select photos from your library.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is needed so you can record video with sound.</string>
<key>NSCameraUsageDescription</key>
<string>Camera access is needed so you can take photos.</string>
响应用户操作(例如,点击按钮)以呈现SDK。将使用默认的主要过滤器和调整过滤器。SDK将支持任何尺寸的图片和视频,并支持访问相机和图库。
import PixelSDK
let container = ContainerController()
container.editControllerDelegate = self
let nav = UINavigationController(rootViewController: container)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
同时实现它的委托方法。当在 EditController 中按下 Next 按钮时,将调用此委托方法。响应时,您应该取消导航控制器或在新控制器上推送。以下示例在新控制器上推送。然后使用提供的 session
参数在您方便的时候输出您的照片或视频。
extension ViewController: EditControllerDelegate {
func editController(_ editController: EditController, didFinishEditing session: Session) {
let controller = UIViewController()
editController.navigationController?.pushViewController(controller, animated: true)
}
}
生成 API 密钥 并将其指定在您的 App Delegate 的 application(_, didFinishLaunchingWithOptions:)
中。以下 定价选项 可用于您的高级密钥。没有 API 密钥,图片和视频导出将包含水印。请保密您的 API 密钥。
import PixelSDK
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
PixelSDK.setup("YOUR API KEY")
return true
}
可选地,指定最大视频时长。默认的最大视频时长为 80 秒。
// Set the maximum video duration to 3 minutes.
PixelSDK.shared.maxVideoDuration = 60*3
就这些!您现在已完全访问 SDK。
限制 SDK
仅图像
以下示例展示了仅支持图像的 SDK。用户将可以访问库和相机。
// Show only the library and photo camera modes in the tab bar
let container = ContainerController(modes: [.library, .photo])
container.editControllerDelegate = self
// Include only images from the users photo library
container.libraryController.fetchPredicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue)
// Include only images from the users drafts
container.libraryController.draftMediaTypes = [.image]
let nav = UINavigationController(rootViewController: container)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
仅视频
以下示例展示了仅支持视频的 SDK。用户将可以访问库和相机。
// Show only the library and video camera modes in the tab bar
let container = ContainerController(modes: [.library, .video])
container.editControllerDelegate = self
// Include only videos from the users photo library
container.libraryController.fetchPredicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.video.rawValue)
// Include only videos from the users drafts
container.libraryController.draftMediaTypes = [.video]
let nav = UINavigationController(rootViewController: container)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
仅方形内容
以下示例展示了仅支持创建方形照片和视频的 SDK。用户将可以访问库和相机。
let container = ContainerController()
container.editControllerDelegate = self
// Only allow square content from the library cropper
container.libraryController.previewCropController.aspectRatio = CGSize(width: 1, height: 1)
// Only allow square content from the camera controller
container.cameraController.aspectRatio = CGSize(width: 1, height: 1)
let nav = UINavigationController(rootViewController: container)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
仅肖像内容
以下示例展示了仅支持创建肖像照片和视频的 SDK。用户将可以访问库和相机。
let container = ContainerController()
container.editControllerDelegate = self
// Only allow portrait content from the library cropper
container.libraryController.previewCropController.aspectRatio = CGSize(width: 3, height: 4)
// Only allow portrait content from the camera controller
container.cameraController.aspectRatio = CGSize(width: 3, height: 4)
let nav = UINavigationController(rootViewController: container)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
图书馆专用
以下示例演示了仅支持图书馆的SDK。
let container = ContainerController(mode: .library)
container.editControllerDelegate = self
let nav = UINavigationController(rootViewController: container)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
相机专用
以下示例演示了仅支持视频相机的SDK。
let container = ContainerController(mode: .video)
container.editControllerDelegate = self
let nav = UINavigationController(rootViewController: container)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
显示编辑器
可以通过显示EditController,配合您自己的媒体内容来绕过相机和图书馆的使用。
图片
以下示例展示了具有名为 "test_image" 的图像的 EditController,并将初始主要过滤器设置为 Wilshire。
let image = UIImage(named: "test_image")!
let session = Session(image: image)
session.image!.primaryFilter = SessionFilterWilshire()
let editController = EditController(session: session)
editController.delegate = self
let nav = UINavigationController(rootViewController: editController)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
视频
以下示例展示了 EditController 与两个名为 "test.mov" 和 "test2.mp4" 的 AVAssets,并将初始主要过滤器设置为 Sepulveda。这两个资产将按顺序成为视频的片段。
您也可以手动将渲染大小传递给 Session 初始化器。有关更多信息,请参阅 Session 文档。
let asset1 = AVAsset(url: Bundle.main.url(forResource: "test", withExtension: "mov")!)
let asset2 = AVAsset(url: Bundle.main.url(forResource: "test2", withExtension: "mp4")!)
let _ = Session(assets: [asset1, asset2], sessionReady: { (session, error) in
guard let session = session,
let video = session.video else {
print("Unable to create session: \(error!)")
return
}
// Set the initial primary filter to Sepulveda
video.primaryFilter = SessionFilterSepulveda()
let editController = EditController(session: session)
editController.delegate = self
let nav = UINavigationController(rootViewController: editController)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true, completion: nil)
})
脚本编辑
图片和视频也可以通过编程方式编辑,而不是通过可视方式。有关更多信息,请参阅 Session 文档。
例如,将图像的主要过滤器设置为 Wilshire
session.image!.primaryFilter = SessionFilterWilshire()
对一个图像应用 亮度 过滤器
let brightnessFilter = SessionFilterBrightness()
brightnessFilter.normalizedIntensity = 0.2
session.image!.filters = [brightnessFilter]
对一个整个视频应用 饱和度 过滤器
let saturationFilter = SessionFilterSaturation()
saturationFilter.normalizedIntensity = 0.3
session.video!.filters = [saturationFilter]
对视频的第一个片段应用 对比度 过滤器
let segment = session.video!.videoSegments.first!
let contrastFilter = SessionFilterContrast()
contrastFilter.normalizedIntensity = 0.2
segment.filters = [contrastFilter]
修剪一个片段,使其从一秒开始,持续两秒
let segment = session.video!.videoSegments.first!
segment.trimStartTime = CMTime(seconds: 1, preferredTimescale: segment.duration.timescale)
segment.trimDuration = CMTime(seconds: 2, preferredTimescale: segment.duration.timescale)
使用 首选转换 旋转视频的第一个片段
let segment = session.video!.videoSegments.first!
segment.preferredTransform = .rotated180Degrees(segment.naturalSize)
segment.cropRect = segment.suggestedCropRect()
增加视频第一个片段的 速度
let segment = session.video!.videoSegments.first!
segment.speedMultiplier = 2 // 2x faster
您可以在进行程序性编辑后显示 EditController,它将反映您的更改。此外,PreviewController 将实时反映所有程序性编辑。
在对会话进行程序性编辑后,您应手动调用 session.save()
。
导出媒体
图像导出
以下示例演示了如何导出为 UIImage
if let image = session.image {
ImageExporter.shared.export(image: image, completion: { (error, uiImage) in
if let error = error {
print("Unable to export image: \(error)")
return
}
print("Finished image export with UIImage: \(uiImage!)")
})
}
除了以 UIImage 的形式导出,导出的图像还会保存到文件中,作为 JPEG 存储在 image.exportedImageURL
变量。导出完成后,您可以使用、移动或复制此文件。一旦您完成文件,您应该调用 session.destroy()
以从用户的草稿中删除图片并删除其相关文件。
视频导出
以下示例演示了将视频导出为 mp4 文件
if let video = session.video {
VideoExporter.shared.export(video: video, progress: { progress in
print("Export progress: \(progress)")
}, completion: { error in
if let error = error {
print("Unable to export video: \(error)")
return
}
print("Finished video export at URL: \(video.exportedVideoURL)")
})
}
导出完成后,您应使用、移动或复制在 video.exportedVideoURL
中找到的文件。一旦您完成文件,您应该调用 session.destroy()
以从用户的草稿中删除视频并删除其相关文件。
在导出视频之前,可以通过设置 video.frameDuration
变量来自定义帧率。
您还可以更改 video.renderSize
,但我们建议您设置 PreviewCropController 的 aspectRatio 和 CameraController 的 aspectRatio。请参阅 方形内容示例。这些属性允许您通过在视频处理逻辑的较晚阶段延迟任何上采样或下采样来保留视频质量。如果您计划在服务器上转换视频为 HLS,则编码器应处理任何上采样或下采样。
编解码设置
如果您没有自定义视频和音频编码设置,默认设置将生成一个使用H.264视频编码和立体声AAC音频编码的mp4文件
import PixelSDK
import AVFoundation
var acl = AudioChannelLayout()
memset(&acl, 0, MemoryLayout<AudioChannelLayout>.size)
acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo
let audioEncodingSettings: [String: Any] = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey: 2,
AVSampleRateKey: AVAudioSession.sharedInstance().sampleRate,
AVChannelLayoutKey: NSData(bytes:&acl, length:MemoryLayout<AudioChannelLayout>.size),
AVEncoderBitRateKey: 96000
]
let videoEncodingSettings: [String: Any] = [
AVVideoCompressionPropertiesKey: [
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC],
AVVideoCodecKey: AVVideoCodecType.h264
]
VideoExporter.shared.export(video: session.video!,
fileType: .mp4,
videoEncodingSettings: videoEncodingSettings,
audioEncodingSettings: audioEncodingSettings,
progress: nil,
completion: { error in
})
您可以提供许多编码设置组合。它们必须遵循AVFoundations的AVVideoSettings.h和AVAudioSettings.h头文件中规定的规范。同时还有一个可用的编解码器列表。请注意,每个编解码器可能对您提供的设置有不同的要求。
以下是一个HEVC视频编码设置的示例
import PixelSDK
import AVFoundation
import VideoToolbox
let videoEncodingSettings: [String: Any] = [
AVVideoCompressionPropertiesKey: [
AVVideoProfileLevelKey: kVTProfileLevel_HEVC_Main_AutoLevel],
AVVideoCodecKey: AVVideoCodecType.hevc
]
转码媒体
媒体文件也可以在不使用UI的情况下进行转码。
此示例将两个名为“test.mov”和“test2.mp4”的AVAssets拼接成一个60帧每秒的mp4文件,使用H.264视频编码和立体声AAC音频编码。
此外,对第一个片段(资产)应用了饱和度滤镜,对第二个片段应用了像素化滤镜。
import PixelSDK
import AVFoundation
let asset1 = AVAsset(url: Bundle.main.url(forResource: "test", withExtension: "mov")!)
let asset2 = AVAsset(url: Bundle.main.url(forResource: "test2", withExtension: "mp4")!)
let _ = Session(assets: [asset1, asset2], sessionReady: { (session, error) in
guard let session = session,
let video = session.video else {
print("Unable to create session: \(error!)")
return
}
// Mark the session as transient so it does not persist on disk/appear in the users drafts
session.isTransient = true
// Set the video frame rate to 60 fps
video.frameDuration = CMTime(value: 1, timescale: 60)
// Apply a Saturation filter to the first segment
let saturationFilter = SessionFilterSaturation()
saturationFilter.normalizedIntensity = 0.2
video.videoSegments[0].filters = [saturationFilter]
// Apply a Pixellate filter to the second segment
let pixellateFilter = SessionFilterPixellate()
video.videoSegments[1].filters = [pixellateFilter]
// Write to an MP4 file with H.264 video encoding and stereo AAC audio encoding
var acl = AudioChannelLayout()
memset(&acl, 0, MemoryLayout<AudioChannelLayout>.size)
acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo
let audioEncodingSettings: [String: Any] = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey: 2,
AVSampleRateKey: AVAudioSession.sharedInstance().sampleRate,
AVChannelLayoutKey: NSData(bytes:&acl, length:MemoryLayout<AudioChannelLayout>.size),
AVEncoderBitRateKey: 96000
]
let videoEncodingSettings: [String: Any] = [
AVVideoCompressionPropertiesKey: [
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC],
AVVideoCodecKey: AVVideoCodecType.h264
]
VideoExporter.shared.export(video: video,
fileType: .mp4,
videoEncodingSettings: videoEncodingSettings,
audioEncodingSettings: audioEncodingSettings,
progress: { (progress) in
print("Transcode progress: \(progress)")
}, completion: { (error) in
if let error = error {
print("Unable to transcode video: \(error)")
return
}
print("Finished video transcode at URL: \(video.exportedVideoURL)")
})
})
转码完成后,您可以打开、复制或删除在video.exportedVideoURL
中发现的文件。
您可以提供许多编码设置组合。它们必须遵循AVFoundations的AVVideoSettings.h和AVAudioSettings.h头文件中规定的规范。同时还有一个可用的编解码器列表。请注意,每个编解码器可能对您提供的设置有不同的要求。
编写自定义滤镜
可以通过Photoshop、Lightroom预设、3D LUT文件,或您喜欢的照片编辑应用程序轻松创建您自己的滤镜。使用RGB查找图像将照片和视频中的颜色进行重映射。这是常见社交媒体应用程序使用的方法。对于更复杂的滤镜,您可以通过链式操作GPUImage3,例如:LookupFilter –> 饱和度 –> 锐化。
步骤1:在您的照片编辑器中打开您选择的任何测试图像。
步骤2:对测试图像应用滤镜和更改,直到对最终结果满意。
步骤3:从仓库中下载原始的lookup.png图像,并在您的照片编辑器中将其打开。
步骤4:将您在测试图像上所做的更改应用到lookup.png图像,并将结果保存为新图像lookup_example.png。不要减小查找图像的大小,降低其质量或更改其格式。在某些照片编辑器中,您可以将测试图像的调整图层简单复制到查找图像。
注意:每个像素颜色必须独立于其他像素(例如,锐化不会工作)。如果您需要一个更复杂的滤镜,可以通过链式操作GPUImage3,例如:LookupFilter –> 锐化 –> 饱和度。链式操作的示例也包括在Xcode示例项目中。
步骤5:使用以下代码创建您的SessionFilter子类,利用LookupFilter操作和您新创建的lookup_example.png图像。请确保将lookup_example.png图像添加到您的Xcode项目中。
import Foundation
import PixelSDK
import GPUImage
class SessionFilterExample: SessionFilter {
public required init() {
super.init()
self.commonInit()
}
public required init(from decoder: Decoder) throws {
try super.init(from: decoder)
self.commonInit()
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
}
func commonInit() {
self.displayName = "Example Filter"
self.version = "1.0"
self.cameraThumbnailImage = UIImage(named: "thumbnail_example.png")
// self.normalizedIntensityDefault = 100 // This value gets computed
self.normalizedIntensityRange = (0, 100)
self.actualIntensityDefault = 1
self.actualIntensityRange = (0, 1)
}
override public func operation() -> ImageProcessingOperation {
let lookupFilter = LookupFilter()
do {
lookupFilter.lookupImage = try PictureInput(image: UIImage(named: "lookup_example.png")!)
}
catch {
print("ERROR: Unable to create PictureInput \(error)")
}
return lookupFilter
}
override public func operationUpdateNeeded(_ op: ImageProcessingOperation) {
let op = op as! LookupFilter
op.intensity = Float(self.actualIntensity)
}
}
步骤 6: 将您自定义的过滤器添加到可用的过滤器列表中。在您展示 ContainerController 或 EditController 之前完成此操作。
PixelSDK.shared.primaryFilters = [
SessionFilterExample(), // Your custom filter
SessionFilterWilshire(),
SessionFilterMontana(),
SessionFilterSanVicente(),
SessionFilterMelrose(),
SessionFilterSepulveda(),
SessionFilterLaCienega(),
SessionFilterAbbotKinney(),
SessionFilterMulholland(),
SessionFilterSunset()
]
完成了!享受您的新自定义过滤器。
自定义颜色
通过将颜色资产添加到您的资产库中来定制框架中的 UI 颜色
您添加的颜色资产可以使用以下名称中的任何一个
Xcode 示例项目 包含所有可用的颜色资产及其默认颜色。如果您不提供颜色资产,将使用默认颜色。
许可
在发布您的应用程序之前,请确保 生成一个 API 密钥。以下 定价选项 适用于您的 API 密钥。如果没有 API 密钥,图像和视频导出将包含水印。