Agent Skills by ALSEL
汎用ソフトウェア開発⭐ リポ 38品質スコア 80/100

axiom-camera-capture-ref

AVCaptureSession、AVCapturePhotoSettings、AVCapturePhotoOutput、RotationCoordinator、photoQualityPrioritization、deferred processing、AVCaptureMovieFileOutput、session presets、capture device APIについて解説します。これらのAPIを使用して、写真や動画のキャプチャ機能を実装できます。AVCaptureSessionでセッションを管理し、AVCapturePhotoSettingsで撮影設定を指定します。AVCapturePhotoOutputは写真データを出力し、AVCaptureMovieFileOutputは動画ファイルの保存に対応しています。RotationCoordinatorでデバイスの回転に対応でき、photoQualityPrioritizationで画質優先度を調整できます。session presetsではキャプチャ品質を選択でき、capture device APIでカメラやマイクなどのデバイスを制御できます。deferred processingで後処理に対応することも可能です。

description の原文を見る

Reference — AVCaptureSession, AVCapturePhotoSettings, AVCapturePhotoOutput, RotationCoordinator, photoQualityPrioritization, deferred processing, AVCaptureMovieFileOutput, session presets, capture device APIs

SKILL.md 本文

カメラキャプチャAPI リファレンス

クイックリファレンス

// SESSION SETUP
import AVFoundation

let session = AVCaptureSession()
let sessionQueue = DispatchQueue(label: "camera.session")

sessionQueue.async {
    session.beginConfiguration()
    session.sessionPreset = .photo

    guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
          let input = try? AVCaptureDeviceInput(device: camera),
          session.canAddInput(input) else { return }
    session.addInput(input)

    let photoOutput = AVCapturePhotoOutput()
    if session.canAddOutput(photoOutput) {
        session.addOutput(photoOutput)
    }

    session.commitConfiguration()
    session.startRunning()
}

// CAPTURE PHOTO
var settings = AVCapturePhotoSettings()
settings.photoQualityPrioritization = .balanced
photoOutput.capturePhoto(with: settings, delegate: self)

// ROTATION (iOS 17+)
let coordinator = AVCaptureDevice.RotationCoordinator(device: camera, previewLayer: previewLayer)
previewLayer.connection?.videoRotationAngle = coordinator.videoRotationAngleForHorizonLevelPreview

AVCaptureSession

キャプチャデータフロー用の中央コーディネーター。

セッションプリセット

プリセット解像度用途
.photo写真に最適写真撮影
.highデバイス最高品質ビデオ記録
.mediumVGA品質プレビュー、低ストレージ
.lowCIF品質最小ストレージ
.hd1280x720720pHDビデオ
.hd1920x10801080pフルHDビデオ
.hd4K3840x21604KウルトラHDビデオ
.inputPriorityデバイスフォーマットを使用カスタム設定

セッション設定

// バッチ設定(アトミック)
session.beginConfiguration()
defer { session.commitConfiguration() }

// プリセットサポートの確認
if session.canSetSessionPreset(.hd4K3840x2160) {
    session.sessionPreset = .hd4K3840x2160
}

// 入出力の追加
if session.canAddInput(input) {
    session.addInput(input)
}

if session.canAddOutput(output) {
    session.addOutput(output)
}

セッションライフサイクル

// 開始(常にバックグラウンドキューで実行)
sessionQueue.async {
    session.startRunning()  // ブロッキングコール
}

// 停止
sessionQueue.async {
    session.stopRunning()
}

// 状態確認
session.isRunning      // true/false
session.isInterrupted  // 通話中などはtrue

セッション通知

// セッション開始
NotificationCenter.default.addObserver(
    forName: .AVCaptureSessionDidStartRunning,
    object: session, queue: .main) { _ in }

// セッション停止
NotificationCenter.default.addObserver(
    forName: .AVCaptureSessionDidStopRunning,
    object: session, queue: .main) { _ in }

// セッション中断(通話など)
NotificationCenter.default.addObserver(
    forName: .AVCaptureSessionWasInterrupted,
    object: session, queue: .main) { notification in
        let reason = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as? Int
    }

// 中断終了
NotificationCenter.default.addObserver(
    forName: .AVCaptureSessionInterruptionEnded,
    object: session, queue: .main) { _ in }

// ランタイムエラー
NotificationCenter.default.addObserver(
    forName: .AVCaptureSessionRuntimeError,
    object: session, queue: .main) { notification in
        let error = notification.userInfo?[AVCaptureSessionErrorKey] as? Error
    }

中断理由

理由原因
.videoDeviceNotAvailableInBackground1アプリがバックグラウンドに移行
.audioDeviceInUseByAnotherClient2別のアプリがオーディオを使用中
.videoDeviceInUseByAnotherClient3別のアプリがカメラを使用中
.videoDeviceNotAvailableWithMultipleForegroundApps4Split View(iPad)
.videoDeviceNotAvailableDueToSystemPressure5温度スロットリング

AVCaptureDevice

物理キャプチャデバイス(カメラ、マイク)を表します。

デバイス取得

// デフォルト背面カメラ
AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)

// デフォルト前面カメラ
AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)

// デフォルトマイク
AVCaptureDevice.default(for: .audio)

// すべてのカメラのディスカバリーセッション
let discoverySession = AVCaptureDevice.DiscoverySession(
    deviceTypes: [.builtInWideAngleCamera, .builtInUltraWideCamera, .builtInTelephotoCamera],
    mediaType: .video,
    position: .unspecified
)
let cameras = discoverySession.devices

デバイスタイプ

タイプ説明
.builtInWideAngleCamera標準カメラ(1x)
.builtInUltraWideCamera超広角カメラ(0.5x)
.builtInTelephotoCamera望遠カメラ(2x、3x)
.builtInDualCamera広角 + 望遠
.builtInDualWideCamera広角 + 超広角
.builtInTripleCamera広角 + 超広角 + 望遠
.builtInTrueDepthCamera前面TrueDepth(Face ID)
.builtInLiDARDepthCameraLiDARデプス

デバイス設定

do {
    try device.lockForConfiguration()
    defer { device.unlockForConfiguration() }

    // フォーカス
    if device.isFocusModeSupported(.continuousAutoFocus) {
        device.focusMode = .continuousAutoFocus
    }

    // 露出
    if device.isExposureModeSupported(.continuousAutoExposure) {
        device.exposureMode = .continuousAutoExposure
    }

    // トーチ(懐中電灯)
    if device.hasTorch && device.isTorchModeSupported(.on) {
        device.torchMode = .on
    }

    // ズーム
    device.videoZoomFactor = 2.0  // 2倍ズーム

} catch {
    print("Failed to configure device: \(error)")
}

カメラ切り替え

// アクティブなセッション中に前後カメラを切り替え
func switchCamera() {
    sessionQueue.async { [self] in
        session.beginConfiguration()
        defer { session.commitConfiguration() }

        // 現在のカメラ入力を削除
        if let currentInput = session.inputs.first(where: { ($0 as? AVCaptureDeviceInput)?.device.hasMediaType(.video) == true }) as? AVCaptureDeviceInput {
            session.removeInput(currentInput)

            // 反対側のカメラを取得
            let newPosition: AVCaptureDevice.Position = currentInput.device.position == .back ? .front : .back
            guard let newDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: newPosition),
                  let newInput = try? AVCaptureDeviceInput(device: newDevice) else { return }

            if session.canAddInput(newInput) {
                session.addInput(newInput)
            }
        }
    }
}

重要: セッションキュー内でbeginConfiguration/commitConfigurationの間に常に切り替えてください。

認可

// ステータス確認
let status = AVCaptureDevice.authorizationStatus(for: .video)

switch status {
case .authorized: break
case .notDetermined:
    await AVCaptureDevice.requestAccess(for: .video)
case .denied, .restricted:
    // 設定プロンプト表示
@unknown default: break
}

AVCaptureDevice.RotationCoordinator(iOS 17以上)

デバイスの向きを自動的に追跡し、回転角を提供します。

セットアップ

// デバイスとプレビューレイヤーで作成
let coordinator = AVCaptureDevice.RotationCoordinator(
    device: captureDevice,
    previewLayer: previewLayer
)

プロパティ

プロパティ説明
videoRotationAngleForHorizonLevelPreviewCGFloatプレビューレイヤーの回転
videoRotationAngleForHorizonLevelCaptureCGFloatキャプチャ出力の回転

監視

// プレビュー更新のKVO監視
let observation = coordinator.observe(
    \.videoRotationAngleForHorizonLevelPreview,
    options: [.new]
) { [weak previewLayer] coordinator, _ in
    DispatchQueue.main.async {
        previewLayer?.connection?.videoRotationAngle = coordinator.videoRotationAngleForHorizonLevelPreview
    }
}

// 初期値を設定
previewLayer.connection?.videoRotationAngle = coordinator.videoRotationAngleForHorizonLevelPreview

キャプチャへの適用

func capturePhoto() {
    if let connection = photoOutput.connection(with: .video) {
        connection.videoRotationAngle = coordinator.videoRotationAngleForHorizonLevelCapture
    }
    photoOutput.capturePhoto(with: settings, delegate: self)
}

AVCapturePhotoOutput

静止画をキャプチャする出力。

設定

let photoOutput = AVCapturePhotoOutput()

// 高解像度
photoOutput.isHighResolutionCaptureEnabled = true

// 最高品質優先度
photoOutput.maxPhotoQualityPrioritization = .quality

// 遅延処理(iOS 17以上)
photoOutput.isAutoDeferredPhotoDeliveryEnabled = true

// Live Photo
photoOutput.isLivePhotoCaptureEnabled = true

// デプス
photoOutput.isDepthDataDeliveryEnabled = true

// ポートレートエフェクトマット
photoOutput.isPortraitEffectsMatteDeliveryEnabled = true

サポート機能

// 有効化前にサポートを確認
photoOutput.isHighResolutionCaptureEnabled && photoOutput.isHighResolutionCaptureSupported
photoOutput.isLivePhotoCaptureSupported
photoOutput.isDepthDataDeliverySupported
photoOutput.isPortraitEffectsMatteDeliverySupported
photoOutput.maxPhotoQualityPrioritization  // .speed, .balanced, .quality

レスポンシブキャプチャAPI(iOS 17以上)

// Zero Shutter Lag - インスタント撮影用リングバッファを使用
photoOutput.isZeroShutterLagSupported
photoOutput.isZeroShutterLagEnabled  // iOS 17以上のアプリではデフォルトtrue

// レスポンシブキャプチャ - オーバーラップしたキャプチャ
photoOutput.isResponsiveCaptureSupported
photoOutput.isResponsiveCaptureEnabled

// 高速キャプチャ優先度 - バースト撮影のような品質に適応
photoOutput.isFastCapturePrioritizationSupported
photoOutput.isFastCapturePrioritizationEnabled

// 遅延処理 - プロキシ + バックグラウンド処理
photoOutput.isAutoDeferredPhotoDeliverySupported
photoOutput.isAutoDeferredPhotoDeliveryEnabled

AVCapturePhotoOutputReadinessCoordinator(iOS 17以上)

シャッターボタン状態の同期更新を提供します。

セットアップ

let coordinator = AVCapturePhotoOutputReadinessCoordinator(photoOutput: photoOutput)
coordinator.delegate = self

キャプチャ追跡

// capturePhoto()の前に呼び出す
coordinator.startTrackingCaptureRequest(using: settings)
photoOutput.capturePhoto(with: settings, delegate: self)

デリゲート

func readinessCoordinator(_ coordinator: AVCapturePhotoOutputReadinessCoordinator,
                          captureReadinessDidChange captureReadiness: AVCapturePhotoOutput.CaptureReadiness) {
    switch captureReadiness {
    case .ready:                         // すぐに撮影可能
    case .notReadyMomentarily:           // 短い遅延、ダブルタップを防ぐ
    case .notReadyWaitingForCapture:     // フラッシュ発火、センサー読み込み中
    case .notReadyWaitingForProcessing:  // 前の写真処理中
    case .sessionNotRunning:             // セッション停止中
    @unknown default: break
    }
}

AVCapturePhotoSettings

単一の写真撮影用設定。

基本設定

// 標準JPEG
var settings = AVCapturePhotoSettings()

// HEIF形式
settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])

// RAW
settings = AVCapturePhotoSettings(rawPixelFormatType: kCVPixelFormatType_14Bayer_BGGR)

// RAW + JPEG
settings = AVCapturePhotoSettings(
    rawPixelFormatType: kCVPixelFormatType_14Bayer_BGGR,
    processedFormat: [AVVideoCodecKey: AVVideoCodecType.jpeg]
)

品質優先度

速度品質用途
.speed最速低いSNS共有、高速撮影
.balanced中程度良好一般的な写真撮影
.quality最遅最高プロ、ドキュメント
settings.photoQualityPrioritization = .speed

フラッシュ

settings.flashMode = .auto  // .off, .on, .auto

Apple ProRAW とHDR

// ProRAWサポート確認
if photoOutput.isAppleProRAWSupported {
    photoOutput.isAppleProRAWEnabled = true

    // ProRAWをキャプチャ
    let query = photoOutput.isAppleProRAWEnabled
        ? AVCapturePhotoOutput.AppleProRAWQuery(photoOutput)
        : nil
    if let rawType = query?.availableRawPixelFormatTypes.first {
        let settings = AVCapturePhotoSettings(
            rawPixelFormatType: rawType,
            processedFormat: [AVVideoCodecKey: AVVideoCodecType.hevc]
        )
    }
}

// HDR設定
settings.photoQualityPrioritization = .quality  // 計算写真/HDRを有効化
// HDRは.balanced または .quality で自動 — 別のトグルは不要

注記: ProRAWはiPhone 12 Pro以降が必要です。HDRは品質優先度により自動です — AppleのDeep FusionとSmart HDRはシステムが品質設定に基づいて制御します。

解像度

// 高解像度静止画
settings.isHighResolutionPhotoEnabled = true

// 最大寸法(解像度制限)
settings.maxPhotoDimensions = CMVideoDimensions(width: 4032, height: 3024)

プレビュー/サムネイル

// 即座に表示するプレビュー
settings.previewPhotoFormat = [
    kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA
]

// サムネイル
settings.embeddedThumbnailPhotoFormat = [
    AVVideoCodecKey: AVVideoCodecType.jpeg,
    AVVideoWidthKey: 160,
    AVVideoHeightKey: 120
]

重要な注記

// 設定は再利用できない
// 各撮影には新しい設定インスタンスが必要
let settings1 = AVCapturePhotoSettings()  // 一度使用
let settings2 = AVCapturePhotoSettings()  // 2回目の撮影用

// 類似撮影用に設定をコピー
let settings2 = AVCapturePhotoSettings(from: settings1)

AVCapturePhotoCaptureDelegate

写真撮影イベント用デリゲート。

extension CameraManager: AVCapturePhotoCaptureDelegate {

    // 写真撮影が開始される
    func photoOutput(_ output: AVCapturePhotoOutput,
                     willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
        // シャッターアニメーション表示
    }

    // 写真撮影完了
    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishProcessingPhoto photo: AVCapturePhoto,
                     error: Error?) {
        guard error == nil else {
            print("Capture error: \(error!)")
            return
        }

        // JPEGデータ取得
        if let data = photo.fileDataRepresentation() {
            savePhoto(data)
        }

        // または生ピクセルバッファを取得
        if let pixelBuffer = photo.pixelBuffer {
            processBuffer(pixelBuffer)
        }
    }

    // 遅延処理プロキシ(iOS 17以上)
    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishCapturingDeferredPhotoProxy deferredPhotoProxy: AVCaptureDeferredPhotoProxy,
                     error: Error?) {
        guard error == nil, let data = deferredPhotoProxy.fileDataRepresentation() else { return }
        replaceThumbnailWithFinal(data)
    }
}

AVCaptureMovieFileOutput

ビデオをファイルに記録する出力。

セットアップ

let movieOutput = AVCaptureMovieFileOutput()

if session.canAddOutput(movieOutput) {
    session.addOutput(movieOutput)
}

// オーディオ入力を追加
if let microphone = AVCaptureDevice.default(for: .audio),
   let audioInput = try? AVCaptureDeviceInput(device: microphone),
   session.canAddInput(audioInput) {
    session.addInput(audioInput)
}

記録

// 記録開始
let outputURL = FileManager.default.temporaryDirectory
    .appendingPathComponent(UUID().uuidString)
    .appendingPathExtension("mov")

// 回転を適用
if let connection = movieOutput.connection(with: .video) {
    connection.videoRotationAngle = rotationCoordinator.videoRotationAngleForHorizonLevelCapture
}

movieOutput.startRecording(to: outputURL, recordingDelegate: self)

// 記録停止
movieOutput.stopRecording()

// 状態確認
movieOutput.isRecording
movieOutput.recordedDuration
movieOutput.recordedFileSize

デリゲート

extension CameraManager: AVCaptureFileOutputRecordingDelegate {

    func fileOutput(_ output: AVCaptureFileOutput,
                    didStartRecordingTo fileURL: URL,
                    from connections: [AVCaptureConnection]) {
        // 記録開始
    }

    func fileOutput(_ output: AVCaptureFileOutput,
                    didFinishRecordingTo outputFileURL: URL,
                    from connections: [AVCaptureConnection],
                    error: Error?) {
        if let error = error {
            print("Recording failed: \(error)")
            return
        }

        // ビデオはoutputFileURLに保存
        saveToPhotoLibrary(outputFileURL)
    }
}

AVCaptureVideoPreviewLayer

カメラプレビュー表示用レイヤー。

セットアップ

let previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.videoGravity = .resizeAspectFill
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)

ビデオグラビティ

動作
.resizeAspect画像全体を収める(レターボックス可能)
.resizeAspectFillレイヤーを埋める(端がクロップされる可能性)
.resize拡大して埋める(歪む)

SwiftUI統合

struct CameraPreview: UIViewRepresentable {
    let session: AVCaptureSession

    func makeUIView(context: Context) -> PreviewView {
        let view = PreviewView()
        view.previewLayer.session = session
        view.previewLayer.videoGravity = .resizeAspectFill
        return view
    }

    func updateUIView(_ uiView: PreviewView, context: Context) {}

    class PreviewView: UIView {
        override class var layerClass: AnyClass { AVCaptureVideoPreviewLayer.self }
        var previewLayer: AVCaptureVideoPreviewLayer { layer as! AVCaptureVideoPreviewLayer }
    }
}

一般的なコードパターン

完全なカメラマネージャー

import AVFoundation

@MainActor
class CameraManager: NSObject, ObservableObject {
    let session = AVCaptureSession()
    let photoOutput = AVCapturePhotoOutput()
    private let sessionQueue = DispatchQueue(label: "camera.session")
    private var rotationCoordinator: AVCaptureDevice.RotationCoordinator?
    private var rotationObservation: NSKeyValueObservation?

    @Published var isSessionRunning = false

    func setup() async -> Bool {
        guard await AVCaptureDevice.requestAccess(for: .video) else { return false }

        return await withCheckedContinuation { continuation in
            sessionQueue.async { [self] in
                session.beginConfiguration()
                defer { session.commitConfiguration() }

                session.sessionPreset = .photo

                guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
                      let input = try? AVCaptureDeviceInput(device: camera),
                      session.canAddInput(input) else {
                    continuation.resume(returning: false)
                    return
                }
                session.addInput(input)

                guard session.canAddOutput(photoOutput) else {
                    continuation.resume(returning: false)
                    return
                }
                session.addOutput(photoOutput)
                photoOutput.maxPhotoQualityPrioritization = .quality

                continuation.resume(returning: true)
            }
        }
    }

    func start() {
        sessionQueue.async { [self] in
            session.startRunning()
            DispatchQueue.main.async {
                self.isSessionRunning = self.session.isRunning
            }
        }
    }

    func stop() {
        sessionQueue.async { [self] in
            session.stopRunning()
            DispatchQueue.main.async {
                self.isSessionRunning = false
            }
        }
    }

    func capturePhoto() {
        var settings = AVCapturePhotoSettings()
        settings.photoQualityPrioritization = .balanced

        if let connection = photoOutput.connection(with: .video),
           let angle = rotationCoordinator?.videoRotationAngleForHorizonLevelCapture {
            connection.videoRotationAngle = angle
        }

        photoOutput.capturePhoto(with: settings, delegate: self)
    }
}

extension CameraManager: AVCapturePhotoCaptureDelegate {
    nonisolated func photoOutput(_ output: AVCapturePhotoOutput,
                                  didFinishProcessingPhoto photo: AVCapturePhoto,
                                  error: Error?) {
        guard let data = photo.fileDataRepresentation() else { return }
        // 写真データを処理
    }
}

リソース

ドキュメント: /avfoundation/avcapturesession, /avfoundation/avcapturedevice, /avfoundation/avcapturephotosettings, /avfoundation/avcapturedevice/rotationcoordinator

スキル: axiom-camera-capture, axiom-camera-capture-diag

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
ComeOnOliver
リポジトリ
ComeOnOliver/skillshub
ライセンス
MIT
最終更新
2026/5/11

Source: https://github.com/ComeOnOliver/skillshub / ライセンス: MIT

本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: ComeOnOliver · ComeOnOliver/skillshub · ライセンス: MIT