Swift技术求助:上传服务器前旋转已保存视频,MP4录制上传AWS
Hey there! Let's break down your two Swift video requirements with actionable code and explanations that fit your existing setup.
To fix video orientation before uploading, we'll use AVAssetExportSession to re-encode the video with the correct transform (this ensures compatibility across all servers/players, unlike just editing metadata). Here's a reusable function:
import AVFoundation import UIKit func rotateVideo(at inputURL: URL, to outputURL: URL, desiredOrientation: AVVideoOrientation, completion: @escaping (Result<URL, Error>) -> Void) { let asset = AVAsset(url: inputURL) // Get the original video track guard let originalVideoTrack = asset.tracks(withMediaType: .video).first else { completion(.failure(NSError(domain: "VideoRotationError", code: -1, userInfo: [NSLocalizedDescriptionKey: "No video track found in input file"]))) return } // Create a mutable composition to modify the video let composition = AVMutableComposition() guard let compositionVideoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else { completion(.failure(NSError(domain: "VideoRotationError", code: -2, userInfo: [NSLocalizedDescriptionKey: "Failed to create composition track"]))) return } do { // Insert the original video into the composition try compositionVideoTrack.insertTimeRange( CMTimeRangeMake(start: .zero, duration: asset.duration), of: originalVideoTrack, at: .zero ) // Calculate the correct transform for the desired orientation let originalTransform = originalVideoTrack.preferredTransform let naturalSize = originalVideoTrack.naturalSize var finalTransform: CGAffineTransform switch desiredOrientation { case .portrait: finalTransform = originalTransform .concatenating(CGAffineTransform(rotationAngle: .pi/2)) .concatenating(CGAffineTransform(translationX: naturalSize.width, y: 0)) case .portraitUpsideDown: finalTransform = originalTransform .concatenating(CGAffineTransform(rotationAngle: -.pi/2)) .concatenating(CGAffineTransform(translationX: 0, y: naturalSize.height)) case .landscapeRight: finalTransform = originalTransform .concatenating(CGAffineTransform(rotationAngle: .pi)) .concatenating(CGAffineTransform(translationX: naturalSize.width, y: naturalSize.height)) case .landscapeLeft: finalTransform = originalTransform @unknown default: finalTransform = originalTransform } compositionVideoTrack.preferredTransform = finalTransform // Export the rotated video guard let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else { completion(.failure(NSError(domain: "VideoRotationError", code: -3, userInfo: [NSLocalizedDescriptionKey: "Failed to create export session"]))) return } exportSession.outputURL = outputURL exportSession.outputFileType = .mp4 exportSession.shouldOptimizeForNetworkUse = true exportSession.exportAsynchronously { DispatchQueue.main.async { switch exportSession.status { case .completed: completion(.success(outputURL)) case .failed: completion(.failure(exportSession.error ?? NSError(domain: "VideoRotationError", code: -4, userInfo: [NSLocalizedDescriptionKey: "Export failed with unknown error"]))) case .cancelled: completion(.failure(NSError(domain: "VideoRotationError", code: -5, userInfo: [NSLocalizedDescriptionKey: "Export was cancelled"]))) default: break } } } } catch { completion(.failure(error)) } }
How to use this function:
// Example: Rotate a saved video to portrait orientation let savedVideoURL = URL(fileURLWithPath: "/path/to/your/saved/video.mp4") let rotatedOutputURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! .appendingPathComponent("rotated_video_\(UUID().uuidString).mp4") rotateVideo(at: savedVideoURL, to: rotatedOutputURL, desiredOrientation: .portrait) { result in switch result { case .success(let rotatedURL): // Upload rotatedURL to your server here print("Rotated video ready for upload: \(rotatedURL)") case .failure(let error): print("Rotation failed: \(error.localizedDescription)") } }
Building on your existing front camera setup, we'll configure the capture session to record MP4, then use AWS SDK's AWSS3TransferUtility to upload the file.
Step 1: Complete the Camera Setup
First, finish your front camera device setup and configure the capture session for MP4 recording:
import AVFoundation import AWSS3 class VideoRecorderViewController: UIViewController { private let captureSession = AVCaptureSession() private var movieFileOutput: AVCaptureMovieFileOutput? lazy var frontCameraDevice: AVCaptureDevice? = { let devices = AVCaptureDevice.DiscoverySession( deviceTypes: [.builtInDuoCamera, .builtInWideAngleCamera, .builtInTelephotoCamera], mediaType: .video, position: .front ).devices return devices.first { $0.position == .front } }() override func viewDidLoad() { super.viewDidLoad() setupCaptureSession() } private func setupCaptureSession() { captureSession.beginConfiguration() // Add front camera video input guard let frontCamera = frontCameraDevice, let videoInput = try? AVCaptureDeviceInput(device: frontCamera) else { print("Failed to initialize front camera input") captureSession.commitConfiguration() return } if captureSession.canAddInput(videoInput) { captureSession.addInput(videoInput) } // Add audio input (required for video recording) guard let audioDevice = AVCaptureDevice.default(for: .audio), let audioInput = try? AVCaptureDeviceInput(device: audioDevice) else { print("Failed to initialize audio input") captureSession.commitConfiguration() return } if captureSession.canAddInput(audioInput) { captureSession.addInput(audioInput) } // Configure MP4 output let movieOutput = AVCaptureMovieFileOutput() let mp4OutputSettings: [String: Any] = [ AVVideoCodecKey: AVVideoCodecType.h264, AVAudioCodecKey: AVAudioCodecType.aac, AVFileTypeKey: kUTTypeMP4 as String ] movieOutput.setOutputSettings(mp4OutputSettings, for: movieOutput.connection(with: .video)!) if captureSession.canAddOutput(movieOutput) { captureSession.addOutput(movieOutput) self.movieFileOutput = movieOutput } captureSession.commitConfiguration() captureSession.startRunning() } }
Step 2: Add Recording Controls
Add functions to start/stop recording, then handle the finished recording:
extension VideoRecorderViewController { func startRecording() { guard let movieOutput = movieFileOutput else { return } // Create a unique MP4 file in documents directory let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let outputURL = documentsDir.appendingPathComponent("recorded_\(UUID().uuidString).mp4") // Delete existing file if it exists if FileManager.default.fileExists(atPath: outputURL.path) { try? FileManager.default.removeItem(at: outputURL) } movieOutput.startRecording(to: outputURL, recordingDelegate: self) } func stopRecording() { movieFileOutput?.stopRecording() } } // Conform to recording delegate to get the finished video URL extension VideoRecorderViewController: AVCaptureFileOutputRecordingDelegate { func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) { if let error = error { print("Recording failed: \(error.localizedDescription)") return } // Upload the recorded MP4 to AWS S3 uploadToAWS(videoURL: outputFileURL) } private func uploadToAWS(videoURL: URL) { let transferUtility = AWSS3TransferUtility.default() let uploadRequest = AWSS3TransferUtilityUploadRequest( bucket: "your-aws-bucket-name", key: "videos/\(UUID().uuidString).mp4", // Unique key to avoid overwrites body: videoURL ) // Start upload and handle completion transferUtility.upload(uploadRequest).continueWith { task in DispatchQueue.main.async { if let error = task.error { print("AWS upload failed: \(error.localizedDescription)") } else { print("Video uploaded to AWS successfully!") // Optional: Delete local file after upload try? FileManager.default.removeItem(at: videoURL) } } return nil } } }
Important Notes:
- Make sure you've added camera and microphone permissions to your
Info.plist:<key>NSCameraUsageDescription</key> <string>Need camera access to record videos</string> <key>NSMicrophoneUsageDescription</key> <string>Need microphone access to record audio with videos</string> - Ensure the AWS SDK is installed (via CocoaPods or Swift Package Manager) and your app is configured with AWS credentials.
内容的提问来源于stack exchange,提问作者Shibin S

