Swift iOS: сохранение видео в библиотекуIOS

Программируем под IOS
Ответить
Гость
 Swift iOS: сохранение видео в библиотеку

Сообщение Гость »


Я пытаюсь сделать видео из изображений, а затем переместить его в библиотеку фотографий. Я всегда получаю сообщение «Ошибка сохранения видео: Необязательно (Error Domain=PHPhotosErrorDomain Code=-1 «(null)»)»
Большая часть кода была найдена в других сообщениях и исправлена, поскольку Swift, похоже, изменил много. Поиск информации — настоящий кошмар, и я был бы очень благодарен за помощь. Я приложил весь проект, если кто-то заинтересуется кодом или поможет.
Загрузка проекта:
https://www.dropbox.com/s/nx910dy9arssngy /SwiftMP4ios.zip?dl=0

Код: Выделить всё

//  ContentView.swift
//  SwiftMP4ios
//
//  Created by Marc Breuer on 21.08.20.
//  Copyright © 2020 Marc Breuer.  All rights reserved.
//

import SwiftUI
import Foundation
import AVFoundation
import UIKit
import Photos

struct ContentView: View {
var body: some View {
Text("Hello, World!")
.onTapGesture {
createMovie()
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

var appdocUrl: URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}

private func createMovie(){
let outputPath = "playground.mov"
let image1 = UIImage(named: "1")
let image2 = UIImage(named: "2")
let image3 = UIImage(named: "3")
let image4 = UIImage(named: "4")

let width = Int(image1!.size.width);
let height = Int(image1!.size.height);
let size = CGSize(width: width, height: height)
//load(fileName:"test.jpg")
//if(image != nil)
writeImagesAsMovie(allImages:[image1!, image2!, image3!, image4!],
videoPath:outputPath,videoSize:size,videoFPS:30)
}

private func load(fileName: String) -> UIImage? {
let fileURL = appdocUrl.appendingPathComponent(fileName)
do {
let imageData = try Data(contentsOf: fileURL)
return UIImage(data: imageData)
} catch {
print("Error loading image : \(error)")
}
return nil
}

private func save(fileName: String, image: UIImage) -> String? {
let fileURL = appdocUrl.appendingPathComponent(fileName)
if let imageData = image.jpegData(compressionQuality: 1.0) {
try? imageData.write(to: fileURL, options: .atomic)
return fileName // ----> Save fileName
}
print("Error saving image")
return nil
}

func writeImagesAsMovie(allImages: [UIImage], videoPath: String, videoSize: CGSize, videoFPS: Int32) {
// Create AVAssetWriter to write video
guard let assetWriter = createAssetWriter(path:videoPath, size: videoSize) else {
print("Error converting images to video: AVAssetWriter not created")
return
}

// If here, AVAssetWriter exists so create AVAssetWriterInputPixelBufferAdaptor
let writerInput = assetWriter.inputs.filter{ $0.mediaType == AVMediaType.video }.first!
let sourceBufferAttributes : [String : Any] = [
kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32ARGB),
kCVPixelBufferWidthKey as String : videoSize.width,
kCVPixelBufferHeightKey as String : videoSize.height,
]
let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput, sourcePixelBufferAttributes: sourceBufferAttributes)

// Start writing session
assetWriter.startWriting()
print("ASSET WRITER STATUS",assetWriter.status.rawValue);

assetWriter.startSession(atSourceTime:CMTime.zero)
if (pixelBufferAdaptor.pixelBufferPool == nil) {
print("Error converting images to video: pixelBufferPool nil after starting session")
return
}

// -- Create queue for 
let mediaQueue = DispatchQueue.init(label:"mediaInputQueue")

// -- Set video parameters
let frameDuration = CMTimeMake(value:1, timescale:videoFPS)
var frameCount = 0

// -- Add images to video
let numImages = allImages.count
writerInput.requestMediaDataWhenReady(on: mediaQueue, using: { () -> Void in
// Append unadded images to video but only while input ready
while (writerInput.isReadyForMoreMediaData && frameCount <  numImages) {
let lastFrameTime = CMTimeMake(value: Int64(frameCount), timescale: videoFPS)
let presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)

if !appendPixelBufferForImageAtURL(image: allImages[frameCount], pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime) {
print("Error converting images to video: AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer")
return
}

frameCount += 1
}

// No more images to add? End video.
if (frameCount >= numImages) {
writerInput.markAsFinished()
assetWriter.finishWriting {
if (assetWriter.error != nil) {
print("Error converting images to video: \(String(describing: assetWriter.error))")
} else {
saveVideoToLibrary(videoURL: NSURL(fileURLWithPath: videoPath))
print("Converted images to movie @ \(videoPath)")

//UISaveVideoAtPathToSavedPhotosAlbum(videoPath)
}
}
}
})
}

func createAssetWriter(path: String, size: CGSize) -> AVAssetWriter? {
// Convert  to NSURL object
//let pathURL = NSURL(fileURLWithPath: path)
let outputURL = appdocUrl.appendingPathComponent(path)

print("Output URL ",outputURL)
//make sure there is not file here
do{
try FileManager.default.removeItem(at: outputURL)
}catch{
}

// Return new asset writer or nil
do {
// Create asset writer
let newWriter = try AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileType.mp4)

// Define settings for video input
let videoSettings: [String : Any] = [
AVVideoCodecKey  : AVVideoCodecType.h264,
AVVideoWidthKey  : size.width,
AVVideoHeightKey : size.height,
]

// Add video input to writer
let assetWriterVideoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
newWriter.add(assetWriterVideoInput)

// Return writer
print("Created asset writer for \(size.width)x\(size.height) video", newWriter.status.rawValue)
return newWriter
} catch {
print("Error creating asset writer: \(error)")
return nil
}
}

func appendPixelBufferForImageAtURL(image: UIImage, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) ->  Bool {
var appendSucceeded = false

autoreleasepool {
if  let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool {
//let pixelBufferPointer = CVPixelBufferPointer
var pixelBuffer : CVPixelBuffer? = nil
let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(
kCFAllocatorDefault,
pixelBufferPool,
&pixelBuffer
)

if status == 0 {
fillPixelBufferFromImage(image: image, pixelBuffer: pixelBuffer!)
appendSucceeded = pixelBufferAdaptor.append(pixelBuffer!, withPresentationTime: presentationTime)
} else {
NSLog("Error: Failed to allocate pixel buffer from pool")
}
}
}

return appendSucceeded
}

func fillPixelBufferFromImage(image: UIImage, pixelBuffer: CVPixelBuffer) {
CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()

let width = Int(image.size.width);
let height = Int(image.size.height);

// Create CGBitmapContext
let context = CGContext(
data: pixelData,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer),
space: rgbColorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue
)

// Draw image into context
context!.draw(image.cgImage!, in: CGRect(origin: .zero, size: image.size))
//draw(context, in: CGRect(0, 0, image.size.width, image.size.height), false)
//CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.cgImage)

CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
}

func saveVideoToLibrary(videoURL: NSURL) {
PHPhotoLibrary.requestAuthorization { status in
// Return if unauthorized
guard status == .authorized else {
print("Error saving video: unauthorized access")
return
}

PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL as URL)
}) { success, error in
if !success {
print("Error saving video: \(String(describing: error))")
}
}
}
}


Источник: https://stackoverflow.com/questions/635 ... to-library
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»