メディアファイルの書き出し

とりあえず、単純な書き出しができたので、今の時点でわかっているところまでメモ。

とりあえず、メディアデータの編集 - 一部の抜き出しでメモしたように、AVAsset オブジェクトから AVMutableComposition オブジェクトを作って、それを保存してみる。

asset = AVAsset.assetWithURL(url)

startTime = CMTimeMake(startValue,timescale)

duration = CMTimeMake(durationValue,timescale)

composition = AVMutableComposition.composition

composition.insertTimeRange(CMTimeRangeMake(startTime,duration),ofAsset:asset,atTime:KCMTimeZero,error:nil)

まずは、ファイルから読み込んだ映像ファイルを書き出す方法から。あやふやな理解なので、詳しくは他を探してください。

videoTrack = composition.tracksWithMediaType(AVMediaTypeVideo)[0]

videoSize = CGSizeApplyAffineTransform(videoTrack.naturalSize,videoTrack.preferredTransform)

とりあえず、映像トラックの情報を抜き出すために、上で作った AVMutableComposition オブジェクトから、tracksWithMediaType(mediaType) で mediaType に AVMediaTypeVideo を指定して、映像トラックの一番最初の物を抜き出している。取り出されたオブジェクトは AVMutableCompositionTrack のはず。そして、CGSizeApplyAffineTransform で、元の映像トラックのサイズを naturalSize で取り出して、preferredTransform で元のトラックの映像の変形情報(ここでは変形はないはず)を取り出して、それを元に、保存する映像トラックの表示サイズを決めている(らしい)。

次に、AVMutableVideoComposition オブジェクトを videoComposition で作って、ここに、書き出す映像ファイルの情報を設定していく。

videoComposition = AVMutableVideoComposition.videoComposition

videoComposition.frameDuration = CMTimeMake(1, 30)

videoComposition.renderSize = videoSize

frameDuration では、1 フレームの長さを設定する。要はフレームレートのことだろうか。ここでは、1/30 秒という設定。つまり、30 fps ってことかな。CMTime に関しては、CMTime を参照してみてください。

renderSize では、書き出す映像の大きさ(表示域)を指定している。ここでは、上で取り出した、元の映像ファイルの設定をそのまま使っている。

もとの映像トラックのフレームレートを反映させたい場合は、AVAssetTracknominalFrameRate で得ることができる。AVMutableCompositionTrack は AVAssetTrack のサブクラスなので、同じことができる。つまり、次のような形で抜き出せるということ。

fps = videoTrack.nominalFrameRate

fps は float で返ってくるので、それを使って、CMTimeMakeWithSeconds で frameDuration を指定するとこんな感じ。1.0 / fps で、1 フレームの秒数が出るので、それにタイムスケールを指定することになるが、よくある 600 だと、24 fps や 30 fps なら問題ないが、23.976 などとなっている場合は、長いファイルだと音声とのずれが出てくるので、videoTrack.naturalTimeScale で書き出すビデオトラックのタイムスケールを使うことにする。

videoComposition.frameDuration = CMTimeMakeWithSeconds( 1.0 / fps, videoTrack.naturalTimeScale)

もしくは、上のように CMTimeMake を使って次のような感じにする。ただし、小数点以下を反映させるために 100000 をかけている。

videoComposition.frameDuration = CMTimeMake( 100000, fps * 100000)

そして、AVMutableVideoCompositionInstruction オブジェクトを videoCompositionInstruction で作って、videoComposition に対して、背景色や再生時間、レイヤーの設定などを設定していき、最後に videoComposition の instruction として指定する。

instruction = AVMutableVideoCompositionInstruction.videoCompositionInstruction

instruction.timeRange = CMTimeRangeMake(KCMTimeZero, composition.duration)

layerInstruction = AVMutableVideoCompositionLayerInstruction.videoCompositionLayerInstructionWithAssetTrack(videoTrack)

instruction.layerInstructions = [layerInstruction]

videoComposition.instructions = [instruction]

まずは、timeRange で、映像トラックの再生時間を指定している。これは、元のトラックの物をそのまま指定している。layerInstruction は、AVMutableVideoCompositionLayerInstruction オブジェクトを元映像トラックから取り出して、そのまま作って、そして、配列として、layerInstructions で instruction に追加している。最後に、上で作った videoComposition に instructions で、配列として指定する。

ここまでできたら、AVAssetExportSession で書き出す。

exportSession = AVAssetExportSession.alloc.initWithAsset(composition,presetName:AVAssetExportPresetAppleM4V480pSD)

exportSession.videoComposition = videoComposition

exportSession.outputURL = url

exportSession.outputFileType = AVFileTypeAppleM4V

上で作った composition を元に、書き出しのプリセットを指定して、initWithAsset(composition,presentName:preset)AVAssetExportSession オブジェクトを作る。ここでのプリセットは、AVAssetExportPresetAppleM4V480pSD を指定しているが、他にもいろいろある。そして、上で作った videoComposition を videoComposition で指定する。書き出すファイルパスは、outputURL で url として指定する。path の場合は、NSURL.fileURLWithPath(path) などで変換する。最後に outputFileType を指定する。ここは、preset に合った物でないとエラーが出る。ここでは、AVFileTypeAppleM4V として m4v ファイルとして書き出す設定にしている。

最後に、exportAsynchronouslyWithCompletionHandler(handler) で書き出す。書き出しの処理が何らかの理由で止まると、handler の処理が行われる。そのときの状態は、status で得られる。

exportSession.exportAsynchronouslyWithCompletionHandler(Proc.new{

case exportSession.status

when AVAssetExportSessionStatusCompleted

# 成功したときの処理

when AVAssetExportSessionStatusFailed

# 失敗したときの処理

end

})

ここでは、AVAssetExportSessionStatusCompletedAVAssetExportSessionStatusFailed で、書き出しが完了した場合と、失敗した場合の処理を分けている。

音声だけを保存する場合は、映像のように細かな設定は必要ないみたい。まずは、メディアデータの編集 - 一部の抜き出しのところでメモしたように、音声トラックのみを抜き出す。元が音声だけの場合は、insertTimeRange(cmTimeRange,ofAsset:asset,atTime:cmTime,error:error) で、asset すべてを抜き出してもいい。

composition = AVMutableComposition.composition

audioTrack = asset.tracksWithMediaType(AVMediaTypeAudio)[0]

newAudioTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio,preferredTrackID:KCMPersistentTrackID_Invalid)

newAudioTrack.insertTimeRange(CMTimeRangeMake(KCMTimeZero,audioTrack.timeRange.duration),ofTrack:audioTrack,atTime:KCMTimeZero,error:nil)

そして、映像ファイルと同じように、処理するのだけど、AVAssetExportSession の preset と outputFileType は、一致する音声の物を選ぶ。

exportSession = AVAssetExportSession.exportSessionWithAsset(composition,presetName:AVAssetExportPresetAppleM4A)

exportSession.outputFileType = AVFileTypeAppleM4A

exportSession.outputURL = @panel.URL

exportSession.exportAsynchronouslyWithCompletionHandler(Proc.new{

case exportSession.status

when AVAssetExportSessionStatusCompleted

# 成功したときの処理

when AVAssetExportSessionStatusFailed

# 失敗したときの処理

end

})

ここでは、preset に AVAssetExportPresetAppleM4A と outputFileType に AVFileTypeAppleM4A を選んでいる。そして、exportAsynchronouslyWithCompletionHandler(handler) で書き出す。

ちなみに、file type は以下の物がある。

AVFileTypeAIFC

AVFileTypeAIFF

AVFileTypeAMR

AVFileTypeAC3

AVFileTypeMPEGLayer3

AVFileTypeSunAU

AVFileTypeCoreAudioFormat

AVFileTypeAppleM4V

AVFileTypeMPEG4

AVFileTypeAppleM4A

AVFileTypeQuickTimeMovie

AVFileTypeWAVE

そして、AVFileTypeAppleM4V に関しては、次の preset が選べる。

AVAssetExportPresetAppleM4VCellular

AVAssetExportPresetAppleM4ViPod

AVAssetExportPresetAppleM4V480pSD

AVAssetExportPresetAppleM4VAppleTV

AVAssetExportPresetAppleM4VWiFi

AVAssetExportPresetAppleM4V720pHD

AVAssetExportPresetAppleProRes422LPCM

AVFileTypeQuickTimeMovie には、次の preset がある。

AVAssetExportPreset640x480

AVAssetExportPreset960x540

AVAssetExportPreset1280x720

AVAssetExportPreset1920x1080

AVFileTypeAppleM4A には、上の例にもある一つしか preset のオプションがない。

AVAssetExportPresetAppleM4A