photo credit: Lil Shepherd On Fairlop Plain via photopin (license)

動画中の指定した秒数の部分を静止画像として取り出す方法について調べたので紹介する。

また、記事の後半では、[0,1]の範囲で指定した位置を静止画として取り出す方法についても触れる。

秒数指定で静止画を取り出す方法

始めに結論を述べると、例えば以下コードでは動画の 10 秒目を取り出したimageが得られる。

let capturingTimeWithSeconds: Float64 = Float64(10)
let capturingTime: CMTime = CMTimeMakeWithSeconds(capturingTimeWithSeconds, 1)
let image: CGImage = captureImage(movieURL: yourMovieURL, capturingTime: capturingTime)

ここで、 captureImage関数は以下のものであり、またyourMovieURLはあなたが対象としたい動画のURLであるとする。

func captureImage(movieURL: URL, capturingTime: CMTime) -> CGImage? {
    let asset: AVAsset = AVURLAsset(url: movieURL, options: nil)
    let imageGenerator: AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
    do {
        let cgImage: CGImage = try imageGenerator.copyCGImage(at: capturingTime, actualTime: nil)
        return cgImage
    } catch {
        return nil
    }
}

解説

動画から静止画を取り出すには、AVAssetImageGenerator.copyCGImage関数を用いる。

この関数のインターフェースを確認すると次の様になっている。

func copyCGImage(at requestedTime: CMTime, actualTime: UnsafeMutablePointer<CMTime>?) throws -> CGImage

引数atには、CMTimeのオブジェクトを渡す必要があると分かる。

例えば次のようにすると、10秒目を指定するCMTimeのオブジェクトが作られる。

let capturingTime: CMTime = CMTimeMakeWithSeconds(Float64(10), 1)

ちなみに、第2引数を変えて、以下のようにした場合では、

let capturingTime: CMTime = CMTimeMakeWithSeconds(Float64(10), 60)

600秒目を指定する CMTime のオブジェクトが作られる。(10分ってこと)
つまり、第1引数 × 第2引数 秒目が指定されるということのようだ。

こうやって作成したCMTimeのオブジェクトをAVAssetImageGenerator.copyCGImageの引数atとして渡してやれば、その秒数の箇所のCGImageオブジェクトが得られる。

let cgImage: CGImage = try imageGenerator.copyCGImage(at: capturingTime, actualTime: nil)

ここで、第2引数actualTimeには何をどう設定できるのか私がよく知らないのだが、
公式ドキュメント を見た感じでは、大体の場合ではnilで良さそうな雰囲気?

actualTime
Upon return, contains the time at which the image was actually generated.
If you are not interested in this information, pass NULL.

取り出す位置を [0,1] 閉区間上で指定する場合

[0,1] 閉区間上の値で取り出し位置を指定したいケースがありそうなので、それについても触れておこうと思う。
例えば、スライダーなどで指定した箇所を静止画として得たいという場合などが該当する。

動画の始まりを0、終わりを1とて表したば場合、
「動画の丁度半分の位置にあたるフレームを取り出す」ということがしたいのであれば、それはつまり
「動画の0.5の位置を取り出す」ということになる。

次のコードでは、yourMovieURLの動画から、0.5 の位置を静止画imageとして得る。

let capturingPoint: Float64 = 0.5 // capturingPoint ∈ [0,1]
let capturingTime: CMTime = generateCMTime(movieURL: yourMovieURL, capturingPoint: capturingPoint)
let image: CGImage = captureImage(movieURL: yourMovieURL, capturingTime: capturingTime)

ここで、 generateCMTimeは次の関数であり、
capturiingPoint([0,1]閉区間上)を指定するCMTimeオブジェクトを返す。

func generateCMTime(movieURL: URL, capturingPoint: Float64) -> CMTime {
    let asset = AVURLAsset(url: movieURL, options: nil)
    let lastFrameSeconds: Float64 = CMTimeGetSeconds(asset.duration)
    let capturingTime: CMTime = CMTimeMakeWithSeconds(lastFrameSeconds * capturingPoint, 1)
    return capturingTime
}

captureImage関数は再掲になるが、以下のとおり。

func captureImage(movieURL: URL, capturingTime: CMTime) -> CGImage? {
    let asset: AVAsset = AVURLAsset(url: movieURL, options: nil)
    let imageGenerator: AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
    do {
        let cgImage: CGImage = try imageGenerator.copyCGImage(at: capturingTime, actualTime: nil)
        return cgImage
    } catch {
        return nil
    }
}

参考サイト

カテゴリー: Tips

hahnah

はーな Webエンジニア。モバイルアプリも少々。Elmが好き。

0件のコメント

コメントを残す

メールアドレスが公開されることはありません。