[英]Converting UnsafeMutableRawPointer to String(utf8) in swift
[英]Swift - converting video to data and String returns nil with UTF8 encoding - sending video via HTTP POST to AWS S3 bucket
问题的高级解释(在 Swift 5 中)
我正在使用AVAssetWriter
在 MOV 中录制视频
我正在使用exportSession.exportAsynchronously
对 MP4 中的视频进行编码(但是我可以跳过这一步,我仍然有同样的问题)
我通过 HTTP POST 将视频发送到 AWS S3 存储桶:
let fileData = try NSData(contentsOfFile:videoPathMP4.path, options:[]) as Data
let fileContent = String(data: fileData, encoding: .utf8)
fileContent
现在nil
,这意味着视频数据无法以 UTF8 进行解释。 如果我使用 UTF16,它可以工作(我得到一个字符串),但是当我在服务器端收到消息时,它不是一个可读的 MP4 文件(它已损坏?)。 我感觉这是因为它应该是 UTF8 中的字符串,但我无法将视频数据转换为 UTF8 字符串发送到服务器。
如何以 UTF8 格式发送此数据,或者如何仅以 NSData 格式发送视频数据? 我看错了吗?
以下是我的不同步骤的代码片段:
第 1 步 - 在 MOV 中录制视频:
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer).seconds
switch _captureState {
case .start:
print ("starting to record")
// Set up recorder
_filename = UUID().uuidString
let videoPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("\(_filename).mov")
let writer = try! AVAssetWriter(outputURL: videoPath, fileType: .mov)
let settings = _videoOutput!.recommendedVideoSettingsForAssetWriter(writingTo: .mov)
let input = AVAssetWriterInput(mediaType: .video, outputSettings: settings)
input.mediaTimeScale = CMTimeScale(bitPattern: 600)
input.expectsMediaDataInRealTime = true
input.transform = CGAffineTransform(rotationAngle: .pi/2)
let adapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: input, sourcePixelBufferAttributes: nil)
if writer.canAdd(input) {
writer.add(input)
}
writer.startWriting()
writer.startSession(atSourceTime: .zero)
_assetWriter = writer
_assetWriterInput = input
_adpater = adapter
_captureState = .capturing
_time = timestamp
case .capturing:
if _assetWriterInput?.isReadyForMoreMediaData == true {
let time = CMTime(seconds: timestamp - _time, preferredTimescale: CMTimeScale(600))
_adpater?.append(CMSampleBufferGetImageBuffer(sampleBuffer)!, withPresentationTime: time)
}
break
case .end:
guard _assetWriterInput?.isReadyForMoreMediaData == true, _assetWriter!.status != .failed else { break }
_assetWriterInput?.markAsFinished()
_assetWriter?.finishWriting { [weak self] in
self?._captureState = .idle
self?._assetWriter = nil
self?._assetWriterInput = nil
print ("Finished writing video file: \(self!._filename)")
}
default:
break
}
}
第 2 步 - 在 MP4 中编码视频(同步以避免发送数据的竞争条件):
func encodeVideo(at videoURL: URL, completionHandler: ((URL?, Error?) -> Void)?) {
let avAsset = AVURLAsset(url: videoURL, options: nil)
let startDate = Date()
//Create Export session
guard let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) else {
completionHandler?(nil, nil)
return
}
//Creating temp path to save the converted video
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath = documentsDirectory.appendingPathComponent("rendered-Video.mp4")
//Check if the file already exists then remove the previous file
if FileManager.default.fileExists(atPath: filePath.path) {
do {
try FileManager.default.removeItem(at: filePath)
} catch {
completionHandler?(nil, error)
}
}
exportSession.outputURL = filePath
exportSession.outputFileType = AVFileType.mp4
exportSession.shouldOptimizeForNetworkUse = true
let start = CMTimeMakeWithSeconds(0.0, preferredTimescale: 0)
let range = CMTimeRangeMake(start: start, duration: avAsset.duration)
exportSession.timeRange = range
let group = DispatchGroup()
group.enter()
exportSession.exportAsynchronously(completionHandler: {() -> Void in
switch exportSession.status {
case .failed:
print(exportSession.error ?? "NO ERROR")
completionHandler?(nil, exportSession.error)
case .cancelled:
print("Export canceled")
completionHandler?(nil, nil)
case .completed:
//Video conversion finished
let endDate = Date()
let time = endDate.timeIntervalSince(startDate)
print(time)
print("Successful!")
print(exportSession.outputURL ?? "NO OUTPUT URL")
completionHandler?(exportSession.outputURL, nil)
default: break
}
group.leave()
})
group.wait(timeout: .now() + 10.0) // blocks current queue
}
第 3 步 - 发送视频(获取一个临时的 URL 到 S3 并授权发送实际的视频文件,然后发送视频文件 - 所有呼叫都进行阻塞呼叫):
private func sendVideo(filename: String, recordID: String){
//Obtain temporary URL and credentials
let url = URL(string: "https://<OMITTED>")!
//JSON Payload for server
let data = [
"recordid" : recordID,
] as [String : Any]
let group = DispatchGroup()
group.enter()
Helper.sendVideoURLRequestToRestAPI(url: url, data: data)
{ response, json, error in
// will be called at either completion or at an error.
guard let statusCode = response?.statusCode else{
print ("[sendVideo] no status code returned - request failed")
return
}
print ("Status Code from server: \(statusCode)")
if statusCode != 200 {
DispatchQueue.main.async {
let alert2 = UIAlertController(title: "Error", message: "Error submitting data - please try again! - \(json["body"] as? String)", preferredStyle: .alert)
alert2.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert2, animated: true)
}
} else { //success
let videoURL = json["url"] as! String
let parameters = [
[
"key": "key",
"value": (json["fields"] as! [String : Any])["key"]!,
"type": "text"
],
[
"key": "AWSAccessKeyId",
"value": (json["fields"] as! [String : Any])["AWSAccessKeyId"]!,
"type": "text"
],
[
"key": "signature",
"value": (json["fields"] as! [String : Any])["signature"]!,
"type": "text"
],
[
"key": "policy",
"value": (json["fields"] as! [String : Any])["policy"]!,
"type": "text"
],
[
"key": "x-amz-security-token",
"value": (json["fields"] as! [String : Any])["x-amz-security-token"]!,
"type": "text"
],
[
"key": "file",
"src": filename,
"type": "file",
"contentType": "video/mp4"
]
] as [[String : Any]]
let boundary = "Boundary-\(UUID().uuidString)"
var body = ""
var error: Error? = nil
for param in parameters {
if param["disabled"] == nil {
let paramName = param["key"]!
body += "--\(boundary)\r\n"
body += "Content-Disposition:form-data; name=\"\(paramName)\""
if param["contentType"] != nil {
body += "\r\nContent-Type: \(param["contentType"] as! String)"
}
let paramType = param["type"] as! String
if paramType == "text" {
let paramValue = param["value"] as! String
body += "\r\n\r\n\(paramValue)\r\n"
} else {
let paramSrc = param["src"] as! String
print("paramSrc: \(paramSrc)")
let videoPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("\(paramSrc).mov")
self.encodeVideo(at: videoPath, completionHandler: { url, error in
guard error == nil else {
print ("error video mp4 conversion")
print(String(describing: error))
return
}
})
let videoPathMP4 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("rendered-Video.mp4")
do {
let fileData = try NSData(contentsOfFile:videoPathMP4.path, options:[]) as Data
let fileContent = String(data: fileData, encoding: .utf8)! //Exception thrown here - IF I change this to UTF16 it doesn't throw an exception but the file arrives corrupted on server side.
body += "; filename=\"\(paramName)\"\r\n"
+ "Content-Type: \"content-type header\"\r\n\r\n\(fileContent)\r\n" //paramName = paramSrc
// print ("Body: \(body)")
} catch {
print ("Could not open video file: \(videoPathMP4.path)")
return
}
}
}
}
body += "--\(boundary)--\r\n";
let postData = body.data(using: .utf8) //UTF8
var request = URLRequest(url: URL(string: videoURL)!,timeoutInterval: Double.infinity)
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
print ("error")
print(String(describing: error))
return
}
guard let data = data else {
print ("Error unpacking data")
print(String(describing: error))
//semaphore.signal()
return
}
print ("data \(data) - \(response)")
print(String(data: data, encoding: .utf8)!)
})
task.resume()
group.leave()
}
}
group.wait() // blocks current queue
}
let boundary = "Boundary-\(UUID().uuidString)"
var body = ""
var error: Error? = nil
for param in parameters {
if param["disabled"] == nil {
let paramName = param["key"]!
body += "--\(boundary)\r\n"
body += "Content-Disposition:form-data; name=\"\(paramName)\""
if param["contentType"] != nil {
body += "\r\nContent-Type: \(param["contentType"] as! String)"
}
let paramType = param["type"] as! String
if paramType == "text" {
let paramValue = param["value"] as! String
body += "\r\n\r\n\(paramValue)\r\n"
} else {
let paramSrc = param["src"] as! String
print("KMKM",paramSrc)
let fileData = try! NSData(contentsOfFile:paramSrc)
print("mcmjdc",fileData)
let fileContent = String(data: fileData! as Data, encoding: .utf8)!
body += "; filename=\"\(paramSrc)\"\r\n"
+ "Content-Type: \"content-type header\"\r\n\r\n\(fileContent)\r\n"
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.