You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift技术求助:上传服务器前旋转已保存视频,MP4录制上传AWS

Hey there! Let's break down your two Swift video requirements with actionable code and explanations that fit your existing setup.


1. Rotating Saved Videos Before Upload to Server

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)")
    }
}

2. Recording MP4 Videos & Uploading to AWS Server

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:42:03