AV Foundationプログラミングガイド

AVFoundation
プログラミングガイド
目次
AVFoundationについて 6
はじめに 7
AVFoundationでのメディアの表現と使用 8
AVFoundationでの並列プログラミング 10
必要事項 11
関連項目 11
アセットの使用 12
アセットオブジェクトの作成 12
アセットの初期化オプション 12
ユーザのアセットへのアクセス 13
アセットを使用する準備 14
ビデオからの静止画像の取得 15
1つの画像の生成 16
一連の画像の生成 17
ムービーのトリミングとフォーマット変換 18
再生 21
アセットの再生 21
各種のアセットの処理 23
アイテムの再生 25
再生速度の変更 25
シーク:再生ヘッドの位置変更 26
複数のアイテムの再生 27
再生の監視 27
ステータスの変化に対する対応 28
視覚表示の準備状況の追跡 29
時間の追跡 29
アイテムの末尾への到達 30
すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再生 31
プレーヤービュ 31
簡単なビューコントローラ 32
アセットの作成 33
プレーヤーアイテムのステータス変更に対する対応 35
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
2
目次
アイテムの再生 35
編集 37
コンポジションを生成する 40
コンポジショントラックを初期化する際に指定するオプション 41
オーディオビジュアルデータをコンポジションに追加する 41
適合するコンポジショントラックを検索する 42
音量勾配を生成する 42
独自のビデオ処理を施す 43
コンポジションの背景色を変更する 43
不透明度勾配を適用する 43
Core Animationの効果を取り入れる 45
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
45
コンポジションを生成する 46
アセットを追加する 46
ビデオの向きを調べる 47
ビデオコンポジションレイヤ命令を適用する 48
描画サイズとフレーム長を設定する 49
コンポジションをエクスポートしてカメラロールに保存する 50
静止画像とビデオのメディアキャプチャ 52
キャプチャセッションを使用したデータの流れの調整 54
セッションの設定 54
キャプチャセッションの状態監視 55
入力デバイスを表すAVCaptureDeviceオブジェクト 56
デバイスの特性 56
デバイスキャプチャの設定 58
デバイスの設定 62
デバイスの切り替え 63
キャプチャ入力を使用したセッションへのキャプチャデバイスの追加 63
キャプチャ出力を使用したセッションからの出力の取得 64
ムービーファイルへの保存 65
ビデオのフレームの処理 68
静止画像のキャプチャ 69
ユーザに対する録画内容の表示 71
ビデオのプレビュー 71
オーディオレベルの表示 72
すべてを組み合わせる:UIImageオブジェクトとしてのビデオフレームのキャプチャ 73
キャプチャセッションの作成と設定 73
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
3
目次
デバイスおよびデバイス入力の作成と設定 74
ビデオデータ出力の作成と設定 74
サンプルバッファのデリゲートメソッドの実装 75
録画の開始と停止 75
高フレームレートのビデオキャプチャ 77
再生 77
編集 78
エクスポート 78
録画 79
エクスポート 80
アセットを読み込む 80
アセットリーダーを生成する 80
アセットリーダー出力を設定する 81
アセットのメディアデータを読み込む 83
アセットを書き出す 84
アセットライターを生成する 85
アセットライター入力を設定する 85
メディアデータを書き出す 87
アセットを再エンコードする 89
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコー
ドする 90
初期設定を行う 91
アセットリーダー/ライターを初期化する 93
アセットを再エンコードする 98
終了時の処理 103
取り消し時の処理 103
アセット出力設定アシスタント 105
時間およびメディアの表現 107
アセットの表現 107
時間の表現 108
時間の長さを表すCMTime 108
時間範囲を表すCMTimeRange 110
メディアの表現 112
CMSampleBufferからUIImageオブジェクトへの変換 113
書類の改訂履歴 115
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
4
図、リスト
AVFoundationについて 6
図 I-1
図 I-2
iOSでのAVFoundationスタック 6
OS XでのAVFoundationスタック 7
アセットの使用 12
エクスポートセッションの流れ 18
図 1-1
再生 21
図 2-1
図 2-2
アセットの再生 22
同じアセットを異なる設定で再生している様子 23
編集 37
図 3-1
図 3-2
図 3-3
図 3-4
AVMutableCompositionが各種のアセットを組み合わせる様子 37
AVMutableAudioMixでオーディオミキシングを実行する様子 38
AVMutableVideoComposition 39
AVAssetExportSessionを使ってメディア要素を組み合わせ、ファイルに出力する様子
40
静止画像とビデオのメディアキャプチャ 52
図 4-1
図 4-2
図 4-3
リスト 4-1
単一のセッションで複数の入出力を設定する様子 52
AVCaptureConnectionが入力と出力の接続を表している様子 53
iOSデバイスの前面/背面カメラの位置 57
キャプチャ接続の向きを設定するコード例 62
エクスポート 80
リスト 5-1
AVOutputSettingsAssistantの使用例 105
時間およびメディアの表現 107
図 6-1
図 6-2
AVAssetによる時間ベースのオーディオビジュアルデータの抽象化 107
AVAssetTrack 108
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
5
AVFoundationについて
AVFoundationは、時間ベースのオーディオビジュアルメディアの再生と作成に使用できるフレーム
ワークで、時間ベースのオーディオビジュアルデータに関する作業を細かいレベルで行うための
Objective-Cのインターフェイスを提供します。たとえば、メディアファイルの検査、作成、編集、再
エンコードなどができます。デバイスから入力ストリームを取得して、リアルタイムでキャプチャ中
および再生中のビデオを操作することもできます。図 I-1にiOSのアーキテクチャを示します。
図 I-1
iOSでのAVFoundationスタック
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
6
AVFoundationについて
はじめに
図 I-2 (7 ページ)に、対応するOS Xのメディアアーキテクチャを示します。
図 I-2
OS XでのAVFoundationスタック
必要な作業を行うには、一般に、使用できる最上位の抽象クラスを使用する必要があります。
●
●
ムービーを再生するだけの場合、AVKitフレームワークを使います。
iOS上で、ビデオを録画するときに最小限のフォーマット制御だけ必要な場合は、UIKitフレーム
ワーク(UIImagePickerController)を使用します。
ただし、AV Foundationで使用する基本的なデータ構造の中には、Core Mediaフレームワークで宣言さ
れるものがあります。たとえば、時間に関するデータ構造や、メディアデータの格納と記述に使用す
る不透過オブジェクトなどです。
はじめに
AVFoundationフレームワークは、大きく2つに分かれます。ビデオまわりのAPIと、オーディオのみに
関係するAPIです。古いオーディオ関連のクラスは、オーディオを簡単に扱うための手段を提供しま
す。これらについては、この文書ではなく、『Multimedia Programming Guide 』で説明しています。
●
サウンドファイルを再生するには、AVAudioPlayerを使用します。
●
オーディオを録音するには、AVAudioRecorderを使用します。
AVAudioSessionを使用してアプリケーションのオーディオ動作を設定することもできます。これに
ついては、『Audio Session Programming Guide 』で説明しています。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
7
AVFoundationについて
はじめに
AVFoundationでのメディアの表現と使用
AV Foundationフレームワークでメディアを表すために使用する主要なクラスは、AVAssetです。フ
レームワークの大部分は、この表現に基づいて設計されています。このクラスの構造を理解すると、
フレームワークの動作を理解しやすくなります。AVAssetインスタンスは、1つ以上のメディアデー
タのコレクションを集約して表したものです。コレクションのタイトル、再生時間、本来の表示サイ
ズなど、コレクションに関する情報をまとめて提供します。AVAssetは特定のデータフォーマットに
関連付けられていません。AVAssetは、あるURLのメディアからアセットインスタンスを作成したり
(“アセットの使用” (12 ページ)を参照)、新しいコンポジションを作成したり(“編集” (9 ペー
ジ)を参照)するために使用する他のクラスのスーパークラスです。
アセット内の個々のメディアデータは、タイプが同じであり、トラックと呼ばれます。通常の簡単な
ケースでは、オーディオコンポーネントを表すトラックとビデオコンポーネントを表すトラックが別
個に存在しますが、複雑なコンポジションでは、オーディオとビデオのトラックが複数重複して存在
する場合もあります。アセットにはメタデータも格納できます。
AV Foundationで非常に重要なことは、アセットやトラックを初期化しただけでは必ずしも使用できる
状態にはならないということです。場合によっては、アイテムの再生時間も計算する必要があります
(たとえば、MP3ファイルには要約情報が含まれていない場合があります)。現在のスレッドをブ
ロックしてその間に値を計算するのではなく、ブロックを使用してデベロッパが定義するコールバッ
クを通じて値を問い合わせ、非同期に応答を取得します。
関連する章: “アセットの使用” (12 ページ)、“時間およびメディアの表現” (107 ページ)
再生
AV Foundationでは、アセットの再生を洗練された方法で管理できます。これに対応するため、アセッ
ト自体からアセットの表示状態が分離されています。これにより、たとえば、同じアセットの2つの
異なるセグメントを異なる解像度でレンダリングして同時に再生できます。アセットの表示状態はプ
レーヤーアイテムオブジェクトによって管理され、アセット内の各トラックの表示状態はプレーヤー
アイテムトラックオブジェクトによって管理されます。プレーヤーアイテムとプレーヤーアイテムト
ラックを使用して、たとえば、アイテムの表示部分をプレーヤーに表示する際のサイズを設定した
り、再生中に適用するオーディオミックスパラメータやビデオコンポジションを設定したり、再生中
にアセットのコンポーネントを無効にしたりできます。
プレーヤーオブジェクトを使用してプレーヤーアイテムを再生し、プレーヤーの出力をCore Animation
レイヤに送ります。プレーヤーキューを使用してプレーヤーアイテムのコレクションの再生を順にス
ケジューリングできます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
8
AVFoundationについて
はじめに
関連する章: “再生” (21 ページ)
アセットの読み取り、書き込み、および再エンコード
AVFoundationでは、アセットの新しい表現を複数の方法で作成できます。既存のアセットを再エン
コードすることもできますが、iOS 4.1以降では、アセットの内容に対して操作を実行し、その結果を
新しいアセットとして保存することもできます。
既存のアセットを再エンコードして、あらかじめ用意されている定義済みのフォーマットの1つに変
換するには、エクスポートセッションを使用します。iOS 4.1以降では、変換方法を詳細に制御する必
要がある場合に、アセットリーダーオブジェクトとアセットライターオブジェクトの組み合わせを使
用して、アセットをある表現から別の表現に変換できます。これらのオブジェクトを使用して、たと
えば、出力ファイルで表現するトラックを選んだり、独自の出力フォーマットを指定したり、変換処
理中にアセットを変更したりできます。
波形の視覚表現を作成するには、アセットリーダーを使用してアセットのオーディオトラックを読み
取ります。
関連する章: “アセットの使用” (12 ページ)
サムネイル
ビデオ表現のサムネイル画像を生成するには、サムネイルの作成元となるアセットを使用して
AVAssetImageGeneratorのインスタンスを初期化します。AVAssetImageGeneratorは、デフォルト
で有効になっているビデオトラックを使用して画像を生成します。
関連する章: “アセットの使用” (12 ページ)
編集
AVFoundationでは、コンポジションを使用して既存のメディア(通常は1つ以上のビデオおよびオー
ディオトラック)から新しいアセットを作成します。トラックの追加、トラックの削除、およびト
ラックの時間順序の調整を行うには、可変コンポジションを使用します。オーディオトラックの相対
的なボリュームとランピングを設定したり、ビデオトラックの不透明度(および不透明度のランピン
グ)を設定したりすることもできます。コンポジションは、メディアのメモリに格納されている部分
を組み合わせたものです。エクスポートセッションを使用してコンポジションをエクスポートする
と、コンポジションが1つのファイルに集約されます。
アセットライターを使用してサンプルバッファや静止画像などのメディアからアセットを作成するこ
ともできます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
9
AVFoundationについて
はじめに
関連する章: “編集” (37 ページ)
静止画像とビデオのメディアキャプチャ
カメラとマイクからの入力の録音録画は、キャプチャセッションによって管理されます。キャプチャ
セッションは、入力デバイスから出力(ムービーファイルなど)へのデータの流れを調整します。1
つのセッションに複数の入力および出力を設定できます。設定はセッションの実行中でも可能です。
データの流れを開始したり停止したりするには、セッションにメッセージを送信します。
さらに、プレビューレイヤのインスタンスを使用して、カメラで録画している内容をユーザに表示す
ることもできます。
関連する章: “静止画像とビデオのメディアキャプチャ” (52 ページ)
AVFoundationでの並列プログラミング
一般的に、特定のスレッドやキューでAVFoundationからのコールバック(ブロック、キー値監視、通
知ハンドラの呼び出し)が行われる保証はありません。代わりに、AVFoundationは内部タスクを実行
するスレッドまたはキューでこれらのハンドラを呼び出します。
通知やスレッドに関しては一般的なガイドラインが2つあります。
●
UI関係の通知はメインスレッド上で発生します。
●
キューを生成/指定する必要があるクラスやメソッドは、そのキュー上で通知を返します。
以上のガイドライン(例外もあり、これについては参照資料に注記)にかかわらず、通知が返される
スレッドを、これと決めてかかって処理してはなりません。
マルチスレッドアプリケーションを作成する場合は、NSThreadのメソッドisMainThreadまたは
[[NSThread currentThread] isEqual:<#A stored thread reference#>]を使用して、呼び出し
元のスレッドが作業の実行環境として期待したスレッドかどうかを確認できます。
performSelectorOnMainThread:withObject:waitUntilDone:や
performSelector:onThread:withObject:waitUntilDone:modes:などのメソッドを使用して、メッ
セージを適切なスレッドにリダイレクトできます。また、dispatch_async(3) Mac OS X Developer
Tools Manual Pageを使用してブロックを適切なキュー(UIタスク用のメインキュー、または並列操
作のために設定したキュー)に「バウンス」することもできます。並列操作については『Concurrency
ProgrammingGuide 』、ブロックについては『BlocksProgrammingTopics 』を参照してください。『AVCam
for iOS 』にはAVFoundationのあらゆる機能に関するコード例が揃っており、もちろんスレッドやキュー
の使用例も載っています。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
10
AVFoundationについて
必要事項
必要事項
AVFoundationは高度なCocoaフレームワークです。このフレームワークを効果的に使用するには、次
の知識が必要です。
●
Cocoaの基本的な開発ツールおよび手法
●
ブロックの基本
●
キー値コーディングおよびキー値監視の基本
●
再生に関しては、Core Animationの基本(『Core Animation Programming Guide 』、あるいは基本的
な再生処理については『AV Kit Framework Reference 』を参照)
関連項目
AVFoundationの例がいくつか用意されています。これには、カメラのキャプチャ機能を理解および実
装する際の基本となる2つの例などがあります。
『AVCam for iOS 』は、カメラ機能を使用するプログラムを実装する際の基本コード例です。これ
は完全に動作するサンプルで、詳細なドキュメントが用意されています。ほとんどの機能とベス
トプラクティスについても説明されています。
『AVCamManual: Using the Manual Capture API 』は、AVCamと連携するアプリケーションです。手
動によるカメラ制御を使用して、カメラ機能を実装しています。同様に、完全に動作するコード
例で、詳細なドキュメントが用意されています。手動制御を利用するカメラアプリケーションを
作成する際の基本例として使用してください。
『RosyWriter 』は、リアルタイムのフレーム処理を説明する例です。特にビデオコンテンツにフィ
ルタを適用する方法について説明しています。開発者に必要な共通の情報であり、この例でその
機能について説明しています。
『AVLocationPlayer: Using AVFoundation Metadata Reading APIs 』では、メタデータAPIの使用方法に
ついて説明しています。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
11
アセットの使用
アセットは、ファイルまたはユーザのiPodライブラリまたはフォトライブラリ内のメディアから入力
されます。アセットオブジェクトを作成する際、そのアイテムに関して取得したいすべての情報が、
すぐに得られるとは限りません。ムービーアセットを作成した後で、アセットから静止画像を抽出し
たり、別のフォーマットに変換したり、コンテンツをトリミングしたりできます。
アセットオブジェクトの作成
URLで指定できるリソースを表すアセットを作成するには、AVURLAssetを使用します。最も簡単な
ケースでは、次のようにファイルからアセットを作成します。
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
アセットの初期化オプション
AVURLAssetの初期化メソッドは、第2引数としてoptionsディクショナリを取ります。このディクショ
ナリで使用される唯一のキーはAVURLAssetPreferPreciseDurationAndTimingKeyです。対応する
値は、正確な再生時間を指定し、時間単位の正確なランダムアクセスを許可するようにアセットを準
備する必要があるかどうかを示す(NSValueオブジェクトに格納された)ブール値です。
アセットの正確な再生時間を取得すると、かなりの処理オーバーヘッドが生じる可能性があります。
大まかな再生時間を使用することで、通常は操作の負担が軽くなり、再生には十分です。したがっ
て、次のようにします。
●
●
アセットの再生だけを目的とする場合は、ディクショナリの代わりにnilを渡すか、
AVURLAssetPreferPreciseDurationAndTimingKeyキーとその値として(NSValueオブジェクト
に格納された)NOを含むディクショナリを渡します。
アセットをコンポジション(AVMutableComposition)に追加する場合は、通常、正確なランダ
ムアクセスが必要です。次のように、AVURLAssetPreferPreciseDurationAndTimingKeyキー
とその値として(YESオブジェクトに格納された — 先に説明したように、NSValueはNSNumberか
ら継承している)NSValueを含むディクショナリを渡します。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
12
アセットの使用
アセットオブジェクトの作成
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie
file#>;
NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey :
@YES };
AVURLAsset *anAssetToUseInAComposition = [[AVURLAsset alloc] initWithURL:url
options:options];
ユーザのアセットへのアクセス
iPodライブラリや「写真(Photo)」アプリケーションで管理されているアセットにアクセスするには、
アセットのURLを取得する必要があります。
●
iPodライブラリにアクセスするには、MPMediaQueryインスタンスを作成して必要なアイテムを見
つけ、MPMediaItemPropertyAssetURLでURLを取得します。
メディアライブラリの詳細については、『Multimedia Programming Guide 』を参照してください。
●
「写真(Photo)」アプリケーションで管理されているアセットにアクセスするには、
ALAssetsLibraryを使用します。
次の例は、「カメラロール(Saved Photos)」アルバムの最初のビデオを表すアセットを取得する方法を
示しています。
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup
*group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just videos.
[group setAssetsFilter:[ALAssetsFilter allVideos]];
// For this example, we're only interested in the first item.
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:0]
options:0
usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL
*innerStop) {
// The end of the enumeration is signaled by asset == nil.
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
13
アセットの使用
アセットを使用する準備
if (alAsset) {
ALAssetRepresentation *representation = [alAsset
defaultRepresentation];
NSURL *url = [representation url];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url
options:nil];
// Do something interesting with the AV asset.
}
}];
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than
this.
NSLog(@"No groups");
}];
アセットを使用する準備
アセット(またはトラック)を初期化しても、そのアイテムに関して取得したいすべての情報がすぐ
に得られるとは限りません。場合によっては、アイテムの再生時間も計算する必要があります(たと
えば、MP3ファイルには要約情報が含まれていない場合があります)。
AVAsynchronousKeyValueLoadingそのような場合は、現在のスレッドを遮断してその間に値を計算
するのではなく、プロトコルを使用して値を問い合わせ、、ブロックで定義した終了ハンドラを通じ
て回答を得る必要があります(AVAssetおよびAVAssetTrackは、AVAsynchronousKeyValueLoading
プロトコルに準拠しています)。
statusOfValueForKey:error:を使用してプロパティの値がロードされたかどうかを確認します。最
初にアセットをロードしたときは、ほとんどまたはすべてのプロパティの値が
AVKeyValueStatusUnknownです。1つ以上のプロパティの値をロードするには、
loadValuesAsynchronouslyForKeys:completionHandler:を呼び出します。終了ハンドラでは、
プロパティのステータスに応じて適切なアクションを実行します。ロードが何らかの理由(ネット
ワークベースのURLにアクセスできないなど)で失敗したり、キャンセルされたりするため、常にロー
ドが正常に完了しない場合に備える必要があります。
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
14
アセットの使用
ビデオからの静止画像の取得
NSArray *keys = @[@"duration"];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {
NSError *error = nil;
AVKeyValueStatus tracksStatus = [asset statusOfValueForKey:@"duration"
error:&error];
switch (tracksStatus) {
case AVKeyValueStatusLoaded:
[self updateUserInterfaceForDuration];
break;
case AVKeyValueStatusFailed:
[self reportError:error forAsset:asset];
break;
case AVKeyValueStatusCancelled:
// Do whatever is appropriate for cancelation.
break;
}
}];
再生用のアセットを準備するには、アセットのtracksプロパティをロードする必要があります。ア
セットの再生の詳細については、“再生” (21 ページ)を参照してください。
ビデオからの静止画像の取得
再生用のアセットからサムネイルなどの静止画像を取得するには、AVAssetImageGeneratorオブジェ
クトを使用します。アセットを使用して画像ジェネレータを初期化します。ただし、初期化の時点で
アセットにビジュアルトラックが存在しなくても初期化に成功することがあるため、必要な場合は
tracksWithMediaCharacteristic:を使用して、ビジュアル特性を持つトラックがアセットに存在
するかどうかを確認してください。
AVAsset anAsset = <#Get an asset#>;
if ([[anAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
AVAssetImageGenerator *imageGenerator =
[AVAssetImageGenerator assetImageGeneratorWithAsset:anAsset];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
15
アセットの使用
ビデオからの静止画像の取得
// Implementation continues...
}
画像ジェネレータのいくつかの側面を設定できます。たとえば、maximumSizeとapertureModeを使
用して、生成される画像の最大サイズと開口モードをそれぞれ指定できます。1度に1つの画像を生成
することも、一連の画像を生成することもできます。すべての画像が生成されるまで、画像ジェネ
レータの強い参照を保持する必要があります。
1つの画像の生成
1度に1つの画像を生成するには、copyCGImageAtTime:actualTime:error:を使用します。
AVFoundationが要求された正確な時間に画像を生成できるとは限りません。そこで、2番目の引数と
してCMTimeへのポインタを渡すと、戻ったときに、画像が実際に生成された時間がそこに格納され
ます。
AVAsset *myAsset = <#An asset#>];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc]
initWithAsset:myAsset];
Float64 durationSeconds = CMTimeGetSeconds([myAsset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600);
NSError *error;
CMTime actualTime;
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:midpoint
actualTime:&actualTime error:&error];
if (halfWayImage != NULL) {
NSString *actualTimeString = (NSString *)CMTimeCopyDescription(NULL, actualTime);
NSString *requestedTimeString = (NSString *)CMTimeCopyDescription(NULL,
midpoint);
NSLog(@"Got halfWayImage: Asked for %@, got %@", requestedTimeString,
actualTimeString);
// Do something interesting with the image.
CGImageRelease(halfWayImage);
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
16
アセットの使用
ビデオからの静止画像の取得
}
一連の画像の生成
一連の画像を生成するには、画像ジェネレータに
generateCGImagesAsynchronouslyForTimes:completionHandler:メッセージを送信します。最初
の引数は、NSValueオブジェクトの配列です。この配列には、各画像を生成するアセット時間を指定
したCMTime構造体を格納します。2番目の引数は、生成された画像ごとに呼び出されるコールバック
として機能するブロックです。このブロック引数は、画像が正常に作成されたか、または操作がキャ
ンセルされたかを示す結果が返されます。また、該当する場合は次の情報も提供されます。
●
画像
●
画像を要求した時間と画像が実際に生成された時間
●
生成に失敗した理由を示すエラーオブジェクト
このブロックの実装において、結果定数を確認し、画像が作成されたかどうかを判定してください。
また、画像の作成が完了するまで画像ジェネレータの強い参照を保持する必要があります。
AVAsset *myAsset = <#An asset#>];
// Assume: @property (strong) AVAssetImageGenerator *imageGenerator;
self.imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:myAsset];
Float64 durationSeconds = CMTimeGetSeconds([myAsset duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 600);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 600);
CMTime end = CMTimeMakeWithSeconds(durationSeconds, 600);
NSArray *times = @[NSValue valueWithCMTime:kCMTimeZero],
[NSValue valueWithCMTime:firstThird], [NSValue
valueWithCMTime:secondThird],
[NSValue valueWithCMTime:end]];
[imageGenerator generateCGImagesAsynchronouslyForTimes:times
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime
actualTime,
AVAssetImageGeneratorResult result, NSError
*error) {
NSString *requestedTimeString = (NSString *)
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
17
アセットの使用
ムービーのトリミングとフォーマット変換
CFBridgingRelease(CMTimeCopyDescription(NULL, requestedTime));
NSString *actualTimeString = (NSString *)
CFBridgingRelease(CMTimeCopyDescription(NULL, actualTime));
NSLog(@"Requested: %@; actual %@", requestedTimeString,
actualTimeString);
if (result == AVAssetImageGeneratorSucceeded) {
// Do something interesting with the image.
}
if (result == AVAssetImageGeneratorFailed) {
NSLog(@"Failed with error: %@", [error localizedDescription]);
}
if (result == AVAssetImageGeneratorCancelled) {
NSLog(@"Canceled");
}
}];
画像シーケンスの生成をキャンセルするには、画像ジェネレータにcancelAllCGImageGeneration
メッセージを送信します。
ムービーのトリミングとフォーマット変換
AVAssetExportSessionオブジェクトを使用して、ムービーのフォーマット変換やムービーのトリミ
ングを行うことができます。その流れを図 1-1に示します。エクスポートセッションは、アセットの
非同期エクスポートを管理するコントローラオブジェクトです。セッションを初期化するときは、エ
クスポートするアセットと、適用するエクスポートオプションを指定するエクスポートプリセット
(allExportPresetsを参照)の名前を使用します。次に、エクスポートセッションを設定して出力
のURLとファイルタイプを指定します。また、メタデータや、出力をネットワークで使用できるよう
に最適化するかどうかなど、その他のオプションも必要に応じて指定できます。
図 1-1
エクスポートセッションの流れ
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
18
アセットの使用
ムービーのトリミングとフォーマット変換
特定のプリセットを使用して特定のアセットをエクスポートできるかどうかを確認するには、次の例
に示すようにexportPresetsCompatibleWithAsset:を使用します。
AVAsset *anAsset = <#Get an asset#>;
NSArray *compatiblePresets = [AVAssetExportSession
exportPresetsCompatibleWithAsset:anAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
initWithAsset:anAsset presetName:AVAssetExportPresetLowQuality];
// Implementation continues.
}
セッションの設定を終えるには、出力のURLを指定します(URLはファイルURLでなければなりませ
ん)。AVAssetExportSessionは出力のファイルタイプを、URLのパス拡張子から推測できます。し
かし通常は、outputFileTypeで直接設定します。時間範囲、出力ファイルの長さの制限、エクスポー
トしたファイルをネットワークで使用できるように最適化するかどうか、ビデオコンポジションな
ど、追加のプロパティを指定することもできます。次の例は、timeRangeプロパティを使用してムー
ビーをトリミングする方法を示しています。
exportSession.outputURL = <#A file URL#>;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTime start = CMTimeMakeWithSeconds(1.0, 600);
CMTime duration = CMTimeMakeWithSeconds(3.0, 600);
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;
新しいファイルを作成するには、exportAsynchronouslyWithCompletionHandler:を呼び出しま
す。エクスポート操作が終了すると、終了ハンドラブロックが呼び出されます。このハンドラの実装
では、セッションのstatus値を確認して、エクスポートが成功したか、失敗したか、またはキャン
セルされたかを判定する必要があります。
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
19
アセットの使用
ムービーのトリミングとフォーマット変換
NSLog(@"Export failed: %@", [[exportSession error]
localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"Export canceled");
break;
default:
break;
}
}];
エクスポートをキャンセルするには、セッションにcancelExportメッセージを送信します。
既存のファイルに上書きしようとしたり、アプリケーションのサンドボックスの外部にファイルを書
き込もうとしたりすると、エクスポートに失敗します。次の場合も失敗します。
●
●
電話の着信があった場合
現在のアプリケーションがバックグラウンドにあり、別のアプリケーションが再生を開始した場
合
このような状況では、通常、エクスポートに失敗したことをユーザに通知し、ユーザがエクスポート
を再開できるようにする必要があります。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
20
再生
アセットの再生を制御するには、AVPlayerオブジェクトを使用します。再生中は、AVPlayerItemの
インスタンスを使用してアセット全体の表示状態を管理し、AVPlayerItemTrackオブジェクトを使
用して個々のトラックの表示状態を管理します。ビデオを表示するには、AVPlayerLayerを使用しま
す。
アセットの再生
プレーヤーは、アセットの再生を管理する(再生の開始と停止、特定の時間へのシークなど)コント
ローラオブジェクトです。1つのアセットを再生するには、AVPlayerのインスタンスを使用します。
AVQueuePlayerオブジェクトを使用して複数のアイテムを順に再生できます(AVQueuePlayerは
AVPlayerのサブクラスです)。OS Xでは、AVKitフレームワークのAVPlayerViewクラスを使って、
ビュー内でコンテンツを再生することも可能です。
プレーヤーから再生の状態に関する情報が提供されるので、必要であれば、ユーザインターフェイス
をプレーヤーの状態と同期させることができます。通常は、プレーヤーの出力を特別なCore Animation
レイヤ(AVPlayerLayerまたはAVSynchronizedLayerのインスタンス)に送ります。レイヤの詳細
については、『Core Animation Programming Guide 』を参照してください。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
21
再生
アセットの再生
複数のプレーヤーレイヤ: 1つのAVPlayerインスタンスから任意の数のAVPlayerLayerオブ
ジェクトを作成できますが、画面上にビデオが表示されるのは直前に作成されたレイヤだけ
です。
最終的にアセットを再生する必要があっても、AVPlayerオブジェクトにはアセットを直接提供しま
せん。代わりに、AVPlayerItemのインスタンスを提供します。プレーヤーアイテムは、そのアイテ
ムが関連付けられたアセットの表示状態を管理します。プレーヤーアイテムには、アセット内のト
ラックに対応するプレーヤーアイテムトラック(AVPlayerItemTrackのインスタンス)が含まれて
います。各種オブジェクト間の関係を図 2-1に示します。
図 2-1
アセットの再生
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
22
再生
各種のアセットの処理
この抽象化によって、特定のアセットを複数のプレーヤーでレンダリング方法を変えながら同時に再
生することができます。図 2-2にその一例を示します。2つのプレーヤーで、同じアセットを異なる設
定で再生しています。アイテムトラックを使用して、たとえば、再生中に特定のトラックを無効にす
ることができます(サウンドコンポーネントを再生したくない場合など)。
図 2-2
同じアセットを異なる設定で再生している様子
既存のアセットを使用してプレーヤーアイテムを初期化することもできますが、特定の場所でリソー
スを再生できるようにURLから直接プレーヤーアイテムを初期化することもできます(その場合、
AVPlayerItemはリソースに合わせてアセットを作成および設定します)。ただし、AVAssetと同じ
ように、プレーヤーアイテムを初期化しただけでは、必ずしもただちに再生できる状態にはなりませ
ん。(キー値監視を使用して)アイテムのstatusプロパティを監視することにより、再生できる状
態になったかどうかを確認できます。
各種のアセットの処理
再生用のアセットの設定方法は、再生するアセットの種類によって異なる場合があります。大まかに
言うと、2つの主なタイプがあります。1つはランダムアクセスが可能なファイルベースのアセット
(ローカルファイル、カメラロール、メディアライブラリなどにあるファイル)で、もう1つはスト
リームベースのアセット(HTTPライブストリーミングフォーマット)です。
ファイルベースのアセットをロードして再生するには、次の手順に従います。
●
AVURLAssetでアセットを作成する
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
23
再生
各種のアセットの処理
●
アセットを使ってAVPlayerItemのインスタンスを生成します。
●
アイテムをAVPlayerのインスタンスに関連付けます。
●
アイテムのstatusプロパティ再生可能な状態を示すまで待機します(通常は、キー値監視を使用
して状態が変化したときに通知を受信します)。
この手法については、“すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再
生” (31 ページ)で具体的に示します。
再生用のHTTPライブストリームを作成して準備するには、URLを使用してAVPlayerItemのインスタ
ンスを初期化します(HTTPライブストリーム内のメディアを表すためにAVAssetインスタンスを直接
作成することはできません)。
NSURL *url = [NSURL URLWithString:@"<#Live stream URL#>];
// You may find a test stream at
<http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8>.
self.playerItem = [AVPlayerItem playerItemWithURL:url];
[playerItem addObserver:self forKeyPath:@"status" options:0
context:&ItemStatusContext];
self.player = [AVPlayer playerWithPlayerItem:playerItem];
プレーヤーアイテムをプレーヤーに関連付けると、再生の準備が始まります。再生の準備が完了する
と、プレーヤーアイテムによってAVAssetインスタンスとAVAssetTrackインスタンスが作成されま
す。これらのインスタンスを使用してライブストリームのコンテンツを調べることができます。
ストリーミングアイテムの長さは、プレイヤーアイテムのdurationプロパティで調べることができ
ます。このプロパティは、再生できる状態になると正しい値に変わります。
注意: プレイヤーアイテムのdurationプロパティが使えるのはiOS 4.3以降です。statusプ
ロパティを参照する方法であれば、どの版のiOSでも通用します。この値が
AVPlayerItemStatusReadyToPlayになれば、次のコードで長さを取得できます。
[[[[[playerItem tracks] objectAtIndex:0] assetTrack] asset] duration];
ライブストリームを再生するだけの場合は、次のコード例のように簡単な方法でURLを使用してプレー
ヤーを直接作成できます。
self.player = [AVPlayer playerWithURL:<#Live stream URL#>];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
24
再生
アイテムの再生
[player addObserver:self forKeyPath:@"status" options:0
context:&PlayerStatusContext];
アセットやアイテムと同じように、プレーヤーを初期化しただけでは、ただちに再生できる状態には
なりません。プレーヤーのstatusプロパティを監視する必要があります。このプロパティの値は、
再生できる状態になるとAVPlayerStatusReadyToPlayに変わります。また、ストリーム用に作成さ
れたプレーヤーアイテムにアクセスする場合はcurrentItemプロパティを監視することもできます。
URLの種類がわからない場合は次のようにしてください。
URLを使用してAVURLAssetを初期化し、そのtracksキーをロードします。
1.
トラックのロードに成功したら、アセットのプレーヤーアイテムを作成します。
1に失敗したら、URLから直接AVPlayerItemを作成します。
2.
プレーヤーのstatusプロパティを監視して、再生可能になったかどうかを判定します。
どちらかの方法が成功すれば、プレーヤーアイテムをプレーヤーと関連付けることができます。
アイテムの再生
再生を開始するには、プレーヤーにplayメッセージを送信します。
- (IBAction)play:sender {
[player play];
}
再生だけでなく、速度や再生ヘッドの位置など、再生に関連するさまざまな側面を管理することがで
きます。プレーヤーの再生状態を監視することもできます。これは、たとえば、ユーザインターフェ
イスをアセットの表示状態と同期させる場合などに便利です(“再生の監視” (27 ページ)を参照)。
再生速度の変更
再生速度を変更するには、プレーヤーのrateプロパティを設定します。
aPlayer.rate = 0.5;
aPlayer.rate = 2.0;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
25
再生
アイテムの再生
値1.0は、「現在のアイテム本来の速度での再生」を意味します。速度を0.0に設定することは、再生
の一時停止と同じです。pauseを使用することもできます。
反転再生可能なアイテムは、rateプロパティに負の値を与えることにより、反転再生のレートを指定
できます。実行可能な反転再生の種類は、playerItemプロパティで判断できます:canPlayReverse
(rate値として-1.0を指定可能)、canPlaySlowReverse(rate値として0.0~1.0を指定可能)、
canPlayFastReverse(-1.0未満の値も指定可能)。
シーク:再生ヘッドの位置変更
再生ヘッドを特定の時間に移動するには、一般に、次のようにseekToTime:を使用します。
CMTime fiveSecondsIn = CMTimeMake(5, 1);
[player seekToTime:fiveSecondsIn];
ただし、seekToTime:メソッドは正確さよりパフォーマンスを重視してチューニングされています。
再生ヘッドを正確に移動する必要がある場合は、次のコード例のように、代わりに
seekToTime:toleranceBefore:toleranceAfter:を使用します。
CMTime fiveSecondsIn = CMTimeMake(5, 1);
[player seekToTime:fiveSecondsIn toleranceBefore:kCMTimeZero
toleranceAfter:kCMTimeZero];
許容誤差としてゼロを使用すると、フレームワークが大量のデータをデコードしなければならなくな
る可能性があります。ゼロを使用するのは、たとえば、厳密に制御する必要がある高度なメディア編
集アプリケーションを作成する場合などに限定してください。
再生後は、プレーヤーのヘッドがアイテムの末尾にセットされ、その後はplayを呼び出しても何も起
きません。再生ヘッドをアイテムの先頭に位置付けるには、アイテムから
AVPlayerItemDidPlayToEndTimeNotification通知を受信するように登録します。通知のコール
バックメソッドで、引数seekToTime:kCMTimeZeroとともにを呼び出します。
// Register with the notification center after creating the player item.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:<#The player item#>];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
26
再生
複数のアイテムの再生
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[player seekToTime:kCMTimeZero];
}
複数のアイテムの再生
AVQueuePlayerオブジェクトを使用して複数のアイテムを順に再生できます。AVQueuePlayerクラス
はAVPlayerのサブクラスです。次のようにプレーヤーアイテムの配列を使用してキュープレーヤー
を初期化します。
NSArray *items = <#An array of player items#>;
AVQueuePlayer *queuePlayer = [[AVQueuePlayer alloc] initWithItems:items];
これで、AVPlayerオブジェクトと同じようにplayを使用してキューを再生できます。キュープレー
ヤーは各アイテムを順に再生します。次のアイテムをスキップしたい場合は、キュープレーヤーに
advanceToNextItemメッセージを送信します。
キューを変更するには、insertItem:afterItem:、removeItem:、およびremoveAllItemsを使用し
ます。新しいアイテムを追加するときは、通常、canInsertItem:afterItem:を使用してキューに挿
入できるかどうかを確認する必要があります。新しいアイテムをキューに追加できるかどうかを確認
するには、次のように2番目の引数としてnilを渡します。
AVPlayerItem *anItem = <#Get a player item#>;
if ([queuePlayer canInsertItem:anItem afterItem:nil]) {
[queuePlayer insertItem:anItem afterItem:nil];
}
再生の監視
プレーヤーおよび再生中のプレーヤーアイテムの表示状態に関するさまざまな側面を監視できます。
これは、たとえば次のようにデベロッパが直接制御しない状態の変化に対して有効です。
●
ユーザがマルチタスクを使用して別のアプリケーションに切り替えると、プレーヤーのrateプロ
パティが0.0になります。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
27
再生
再生の監視
●
リモートメディアを再生している場合は、利用可能なデータが増えるにつれて、プレーヤーアイ
テムのloadedTimeRangesプロパティとseekableTimeRangesプロパティが変化します。
これらのプロパティから、プレーヤーアイテムのタイムラインのどの部分が利用可能であるかが
わかります。
●
●
HTTPライブストリーム用のプレーヤーアイテムが作成されると、プレーヤーのcurrentItemプロ
パティが変化します。
HTTPライブストリームの再生中は、プレーヤーアイテムのtracksプロパティが変化することが
あります。
この変化は、ストリームのコンテンツに複数のエンコードが使用されている場合に発生します。
つまり、プレーヤーがエンコードを切り替えると、トラックが変化します。
●
何らかの理由で再生に失敗すると、プレーヤーまたはプレーヤーアイテムのstatusプロパティが
変化することがあります。
キー値監視を使用して、これらのプロパティ値の変更を監視できます。
Important: KVO変更通知を登録し、メインスレッドのKVO変更通知を登録解除する必要がありま
す。これにより、別のスレッドで変更が行われた場合に部分的な通知を受信する可能性を避ける
ことができます。AV Foundationは、別のスレッドで変更操作が行われた場合でも、メインスレッ
ド上でobserveValueForKeyPath:ofObject:change:context:を呼び出します。
ステータスの変化に対する対応
プレーヤーまたはプレーヤーアイテムのステータスが変化すると、キー値監視の変更通知が発行され
ます。何らかの理由(たとえば、メディアサービスがリセットされた場合など)でオブジェクトを再
生できない場合は、ステータスが状況に応じてAVPlayerStatusFailedまたは
AVPlayerItemStatusFailedに変化します。このような場合は、オブジェクトのerrorプロパティの
値が、オブジェクトが再生できなくなった理由を記述するエラーオブジェクトに変更されます。
AV Foundationは通知が送信されたときのスレッドを明示しません。ユーザインターフェイスを更新し
たい場合は、関連するコードを必ずメインスレッド上で呼び出す必要があります。次の例では、メイ
ンスレッド上でコードを実行するためにdispatch_async(3) Mac OS X Developer Tools Manual
Pageを使用しています。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (context == <#Player status context#>) {
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
28
再生
再生の監視
AVPlayer *thePlayer = (AVPlayer *)object;
if ([thePlayer status] == AVPlayerStatusFailed) {
NSError *error = [<#The AVPlayer object#> error];
// Respond to error: for example, display an alert sheet.
return;
}
// Deal with other status change if appropriate.
}
// Deal with other change notifications if appropriate.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
return;
}
視覚表示の準備状況の追跡
AVPlayerLayerオブジェクトのreadyForDisplayプロパティを監視することで、ユーザに表示する
コンテンツがレイヤに存在するかどうかを知ることができます。特に、ユーザに表示するコンテンツ
がある場合にのみプレーヤーレイヤをレイヤツリーに挿入し、新しいコンテンツへの移行を実行でき
ます。
時間の追跡
AVPlayerオブジェクトに含まれる再生ヘッドの位置の変化を追跡するには、
addPeriodicTimeObserverForInterval:queue:usingBlock:または
addBoundaryTimeObserverForTimes:queue:usingBlock:を使用します。これによって、たとえ
ば、ユーザインターフェイスに表示した経過時間や残り時間の情報を更新したり、その他のユーザイ
ンターフェイス同期処理を実行したりできます。
●
addPeriodicTimeObserverForInterval:queue:usingBlock:を使用すると、時間がジャンプし
た場合や、再生の開始時または終了時に、指定したブロックが指定した間隔で呼び出されます。
●
addBoundaryTimeObserverForTimes:queue:usingBlock:を使用すると、NSValueオブジェク
トに格納されたCMTimeの配列を渡すことができます。配列に含まれる時間になると、指定したブ
ロックが呼び出されます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
29
再生
再生の監視
これらのメソッドは、いずれもオブザーバとして機能する不透過オブジェクトを返します。プレー
ヤーから時間監視ブロックを呼び出す必要がある間は、返されたオブジェクトの強い参照を保持する
必要があります。また、これらのメソッドの呼び出しと、対応するremoveTimeObserver:の呼び出
しのバランスを取る必要があります。
AV Foundationでは、これらのメソッドのどちらを使用した場合でも、渡された間隔または境界ごとに
ブロックが呼び出されるという保証はありません。AV Foundationは、直前に呼び出されたブロックの
実行が完了しなかった場合、ブロックを呼び出しません。したがって、ブロック内で実行する作業が
システムに過大な負荷を掛けないようにする必要があります。
// Assume a property: @property (strong) id playerObserver;
Float64 durationSeconds = CMTimeGetSeconds([<#An asset#> duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 1);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 1);
NSArray *times = @[[NSValue valueWithCMTime:firstThird], [NSValue
valueWithCMTime:secondThird]];
self.playerObserver = [<#A player#> addBoundaryTimeObserverForTimes:times queue:NULL
usingBlock:^{
NSString *timeDescription = (NSString *)
CFBridgingRelease(CMTimeCopyDescription(NULL, [self.player currentTime]));
NSLog(@"Passed a boundary at %@", timeDescription);
}];
アイテムの末尾への到達
プレーヤーアイテムの再生が完了したときに、AVPlayerItemDidPlayToEndTimeNotificationの通
知を受信するように登録できます。
[[NSNotificationCenter defaultCenter] addObserver:<#The observer, typically self#>
selector:@selector(<#The selector name#>)
name:AVPlayerItemDidPlayToEndTimeNotification
object:<#A player item#>];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
30
再生
すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再生
すべてを組み合わせる:AVPlayerLayerを使用したビデオファ
イルの再生
次の簡単なコード例は、AVPlayerオブジェクトを使用してビデオファイルを再生する方法を示して
います。具体的な方法は次のとおりです。
●
AVPlayerLayerレイヤを使用するようにビューを設定する
●
AVPlayerオブジェクトを作成する
ファイルベースのアセット用のAVPlayerItemオブジェクトを作成し、キー値監視を使用してそ
のステータスを監視する
●
●
再生できる状態になったアイテムに対応してボタンを有効にする
●
アイテムを再生し、プレーヤーのヘッドを先頭に戻す
注意: この例では最も重要なコードに注目するため、完全なアプリケーションからメモリ管
理や(キー値監視用または通知センター用の)オブザーバの登録解除など、いくつかの部分
を省略しています。AV Foundationを使用するには、欠落した部分を推測できるように、Cocoa
に関する十分な経験を積んでいることが期待されます。
再生の概念については、“アセットの再生” (21 ページ)を参照してください。
プレーヤービュ
アセットのビジュアルコンポーネントを再生するには、AVPlayerの出力の送り先となるAVPlayerLayer
レイヤを含むビューが必要です。これに対応するため、次のようにUIViewの簡単なサブクラスを作
成します。
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface PlayerView : UIView
@property (nonatomic) AVPlayer *player;
@end
@implementation PlayerView
+ (Class)layerClass {
return [AVPlayerLayer class];
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
31
再生
すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再生
- (AVPlayer*)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
@end
簡単なビューコントローラ
次のように宣言された簡単なView Controllerがあるとします。
@class PlayerView;
@interface PlayerViewController : UIViewController
@property (nonatomic) AVPlayer *player;
@property (nonatomic) AVPlayerItem *playerItem;
@property (nonatomic, weak) IBOutlet PlayerView *playerView;
@property (nonatomic, weak) IBOutlet UIButton *playButton;
- (IBAction)loadAssetFromFile:sender;
- (IBAction)play:sender;
- (void)syncUI;
@end
syncUIメソッドは、次のようにボタンの状態をプレーヤーの状態と同期させます。
- (void)syncUI {
if ((self.player.currentItem != nil) &&
([self.player.currentItem status] == AVPlayerItemStatusReadyToPlay)) {
self.playButton.enabled = YES;
}
else {
self.playButton.enabled = NO;
}
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
32
再生
すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再生
View ControllerのviewDidLoadメソッドでsyncUIを呼び出すことにより、ビューが最初に表示された
ときに一貫性のあるユーザインターフェイスを提供します。
- (void)viewDidLoad {
[super viewDidLoad];
[self syncUI];
}
残りのセクションでは、その他のプロパティおよびメソッドについて説明します。
アセットの作成
AVURLAssetを使用してURLからアセットを作成します。(次の例では、プロジェクトに適切なビデオ
リソースが含まれているとします)。
- (IBAction)loadAssetFromFile:sender {
NSURL *fileURL = [[NSBundle mainBundle]
URLForResource:<#@"VideoFileName"#> withExtension:<#@"extension"#>];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
NSString *tracksKey = @"tracks";
[asset loadValuesAsynchronouslyForKeys:@[tracksKey] completionHandler:
^{
// The completion block goes here.
}];
}
完了ブロックでは、アセット用のAVPlayerItemのインスタンスを作成し、それをプレーヤービュー
のプレーヤーとして設定します。アセットの作成と同じように、プレーヤーアイテムを作成しただけ
では、ただちに使用できる状態にはなりません。再生できる状態になったかどうかを判定するには、
アイテムのstatusプロパティを監視します。この監視の設定は、プレーヤーアイテムのインスタン
スを、プレーヤー自身と対応づける前に行う必要があります。
アイテムをプレーヤーに関連付けたときに、プレーヤーアイテムを再生するための準備が開始されま
す。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
33
再生
すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再生
// Define this constant for the key-value observation context.
static const NSString *ItemStatusContext;
// Completion handler block.
dispatch_async(dispatch_get_main_queue(),
^{
NSError *error;
AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey
error:&error];
if (status == AVKeyValueStatusLoaded) {
self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
// ensure that this is done before the playerItem is associated
with the player
[self.playerItem addObserver:self forKeyPath:@"status"
options:NSKeyValueObservingOptionInitial
context:&ItemStatusContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerItem];
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
[self.playerView setPlayer:self.player];
}
else {
// You should deal with the error appropriately.
NSLog(@"The asset's tracks were not loaded:\n%@", [error
localizedDescription]);
}
});
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
34
再生
すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再生
プレーヤーアイテムのステータス変更に対する対応
プレーヤーアイテムのステータスが変化すると、ビューコントローラはキー値監視の変更通知を受け
取ります。AV Foundationは通知が送信されたときのスレッドを明示しません。ユーザインターフェイ
スを更新したい場合は、関連するコードを必ずメインスレッド上で呼び出す必要があります。次の例
では、メインスレッド上でメッセージをキューに入れ、ユーザインターフェイスを同期するために、
dispatch_async(3) Mac OS X Developer Tools Manual Pageを使用しています。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (context == &ItemStatusContext) {
dispatch_async(dispatch_get_main_queue(),
^{
[self syncUI];
});
return;
}
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
return;
}
アイテムの再生
アイテムの再生は、playメッセージをプレーヤーに送信することにより始まります。
- (IBAction)play:sender {
[player play];
}
アイテムの再生は1度だけです。再生後は、プレーヤーのヘッドがアイテムの末尾にセットされ、そ
の後はplayメソッドを呼び出しても何も起きません。再生ヘッドをアイテムの先頭に位置付けるに
は、アイテムからAVPlayerItemDidPlayToEndTimeNotificationを受信するように登録します。通
知のコールバックメソッドで、引数kCMTimeZeroとともにseekToTime:を呼び出します。
// Register with the notification center after creating the player item.
[[NSNotificationCenter defaultCenter]
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
35
再生
すべてを組み合わせる:AVPlayerLayerを使用したビデオファイルの再生
addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.player currentItem]];
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[self.player seekToTime:kCMTimeZero];
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
36
編集
AVFoundationフレームワークは、オーディオビジュアルアセットの編集に役立つ、機能豊富なクラス
群を備えています。編集用APIの中核となるのは「コンポジション」です。その実体は、さまざまな
メディアアセットから成る、トラックを集約したものに過ぎません。AVMutableCompositionクラス
には、トラックを追加/削除し、その時間的順序を管理するためのインターフェイスがあります。図
3-1に、既存のアセットをコンポジションとして組み合わせ、新規アセットを形成する様子を示しま
す。いくつかのアセットを順につなげてひとつのファイルにするだけであれば、ほかに何も必要あり
ません。一方、コンポジションを構成するトラック上のオーディオやビデオに対し、独自の処理を施
したいのであれば、オーディオミックスやビデオコンポジションがそれぞれ必要になります。
図 3-1
AVMutableCompositionが各種のアセットを組み合わせる様子
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
37
編集
AVMutableAudioMixクラスを使うと、コンポジションのオーディオトラックに対して、独自のオー
ディオ処理を施すことができます(図 3-2を参照)。現状では、最大音量を指定する、音量勾配を設
定する、といった処理が可能です。
図 3-2
AVMutableAudioMixでオーディオミキシングを実行する様子
AVMutableVideoCompositionクラスを使うと、コンポジションのビデオトラックを直接操作して、
編集を施すことができます(図 3-3を参照)。個々のビデオコンポジションに関しては、出力ビデオ
の描画サイズや拡大率、あるいはフレーム長が指定可能です。ビデオコンポジションの命令
(AVMutableVideoCompositionInstructionクラスで実装)で、ビデオの背景色を変更し、あるい
はレイヤ命令を適用することができます。レイヤ命令
(AVMutableVideoCompositionLayerInstructionクラスで実装)によりコンポジションのビデオ
トラックに適用できる命令としては、アフィン変換およびその勾配(ある時間範囲にわたる変化)、
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
38
編集
不透明度およびその勾配(ある時間範囲にわたる変化)などがあります。ビデオコンポジションのク
ラスには、Core Animationフレームワークの各種効果を、animationToolプロパティを使ってビデオ
に取り入れる機能もあります。
図 3-3
AVMutableVideoComposition
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
39
編集
コンポジションを生成する
オーディオミックスとビデオコンポジションを組み合わせ、コンポジションとして出力するには、
AVAssetExportSessionオブジェクトを使います(図 3-4を参照)。コンポジションを指定してエク
スポートセッションを初期化した後、オーディオミックスとビデオコンポジションを、audioMixプ
ロパティ、videoCompositionプロパティにそれぞれ与えるだけの手順です。
図 3-4
AVAssetExportSessionを使ってメディア要素を組み合わせ、ファイルに出力する様子
コンポジションを生成する
独自のコンポジションを生成するにはAVMutableCompositionクラスを使います。メディアデータを
コンポジションを追加するためには、AVMutableCompositionTrackクラスで実装される、コンポジ
ショントラックをいくつか追加しなければなりません。最も簡単なのは、1本ずつのビデオトラック
とオーディオトラックから、可変コンポジションを生成する場合でしょう。
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
// Create the video composition track.
AVMutableCompositionTrack *mutableCompositionVideoTrack = [mutableComposition
addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
// Create the audio composition track.
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
40
編集
オーディオビジュアルデータをコンポジションに追加する
AVMutableCompositionTrack *mutableCompositionAudioTrack = [mutableComposition
addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
コンポジショントラックを初期化する際に指定するオプション
トラックを新たにコンポジションに追加する際には、メディア型とトラックIDを指定しなければなり
ません。メディア型としては「オーディオ」と「ビデオ」が典型的ですが、ほかにも
AVMediaTypeSubtitle、AVMediaTypeTextなどの型を指定できます。
オーディオビジュアルデータに対応するトラックには、それぞれ一意的な識別子が割り当てられてお
り、これをトラックIDと呼びます。トラックIDとしてkCMPersistentTrackID_Invalidが指定されれ
ば、自動的に一意的な識別子を生成し、トラックに対応づけるようになっています。
オーディオビジュアルデータをコンポジションに追加する
いくつかのトラックから成るコンポジションが生成できたので、次にメディアデータを適当なトラッ
クに追加していきます。そのためには、当該メディアデータを収容する、AVAssetオブジェクトにア
クセスする必要があります。可変コンポジションのトラックに対するインターフェイスを利用して、
同じメディア型のトラックをいくつでも、同じトラックに配置することができます。2つのビデオア
セットトラックを順に連結して、同じコンポジショントラックに追加するコード例を示します。
// You can retrieve AVAssets from a number of places, like the camera roll for
example.
AVAsset *videoAsset = <#AVAsset with at least one video track#>;
AVAsset *anotherVideoAsset = <#another AVAsset with at least one video track#>;
// Get the first video track from each asset.
AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo]
objectAtIndex:0];
AVAssetTrack *anotherVideoAssetTrack = [[anotherVideoAsset
tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
// Add them both to the composition.
[mutableCompositionVideoTrack
insertTimeRange:CMTimeRangeMake(kCMTimeZero,videoAssetTrack.timeRange.duration)
ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];
[mutableCompositionVideoTrack
insertTimeRange:CMTimeRangeMake(kCMTimeZero,anotherVideoAssetTrack.timeRange.duration)
ofTrack:anotherVideoAssetTrack atTime:videoAssetTrack.timeRange.duration error:nil];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
41
編集
音量勾配を生成する
適合するコンポジショントラックを検索する
コンポジショントラックはできるだけ、メディア型ごとに1つだけ用いるようにしてください。適合
するアセットトラックを統合すれば、リソース消費量を抑制できます。メディアデータを順に再生す
るのであれば、同じ型のデータは同じコンポジショントラックに置くようにしてください。可変コン
ポジションに対して、あるアセットトラックと適合する(統合可能な)コンポジショントラックがな
いか、問い合わせることができます。
AVMutableCompositionTrack *compatibleCompositionTrack = [mutableComposition
mutableTrackCompatibleWithTrack:<#the AVAssetTrack you want to insert#>];
if (compatibleCompositionTrack) {
// Implementation continues.
}
注意: 複数のビデオセグメントを同じコンポジショントラックに置くと、特に組み込みデバ
イスの場合、セグメント間を遷移する際に、フレームが脱落することがあります。ビデオセ
グメントを置くコンポジショントラックの数は、アプリケーションの設計および想定するプ
ラットフォームによって完全に決まります。
音量勾配を生成する
AVMutableAudioMixオブジェクトは単独で、コンポジションを構成するオーディオトラックすべて
に対し、個別に独自のオーディオ処理を施すことができます。オーディオミックスをaudioMixとい
うクラスメソッドで生成し、AVMutableAudioMixInputParametersクラスのインスタンスを使って、
コンポジションを構成する特定のトラックに、このオーディオミックスを対応づけます。オーディオ
ミックスを使って、オーディオトラックの音量を調整できます。あるオーディオトラックに対して音
量勾配(ある時間にわたる音量変化)を設定することにより、コンポジションの再生時間全体にわ
たって、徐々に音を消していくコード例を示します。
AVMutableAudioMix *mutableAudioMix = [AVMutableAudioMix audioMix];
// Create the audio mix input parameters object.
AVMutableAudioMixInputParameters *mixParameters = [AVMutableAudioMixInputParameters
audioMixInputParametersWithTrack:mutableCompositionAudioTrack];
// Set the volume ramp to slowly fade the audio out over the duration of the
composition.
[mixParameters setVolumeRampFromStartVolume:1.f toEndVolume:0.f
timeRange:CMTimeRangeMake(kCMTimeZero, mutableComposition.duration)];
// Attach the input parameters to the audio mix.
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
42
編集
独自のビデオ処理を施す
mutableAudioMix.inputParameters = @[mixParameters];
独自のビデオ処理を施す
オーディオミックスと同様、AVMutableVideoCompositionオブジェクトも単独で、コンポジション
を構成するビデオトラックすべてに対し、独自のビデオ処理を施すことができます。たとえば、適切
な描画サイズや拡大率、フレームレートを、コンポジションのビデオトラックに直接設定できるので
す。こういったプロパティに適切な値を設定する詳しいコード例が、“描画サイズとフレーム長を設
定する” (49 ページ)に載っています。
コンポジションの背景色を変更する
ビデオコンポジションにはAVVideoCompositionInstructionオブジェクトの配列を用意して、少な
くとも1つ、ビデオコンポジション命令を設定しなければなりません。独自のビデオコンポジション
命令を生成するにはAVMutableVideoCompositionInstructionクラスを使います。ビデオコンポジ
ション命令を使うと、コンポジションの背景色を変更し、後処理が必要か、レイヤ命令を適用する
か、を指定できます。
コンポジション全体にわたって背景色を赤に変更する、というビデオコンポジション命令を生成する
コード例を以下に示します。
AVMutableVideoCompositionInstruction *mutableVideoCompositionInstruction =
[AVMutableVideoCompositionInstruction videoCompositionInstruction];
mutableVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,
mutableComposition.duration);
mutableVideoCompositionInstruction.backgroundColor = [[UIColor redColor] CGColor];
不透明度勾配を適用する
ビデオコンポジション命令を使うと、ビデオコンポジションに対し、レイヤ命令を適用することも可
能です。AVMutableVideoCompositionLayerInstructionオブジェクトは、コンポジションを構成
するビデオトラックに対する、アフィン変換およびその勾配、不透明度およびその勾配を表します。
コンポジション命令を適用する際、ソーストラックのビデオフレームをどのようにレイヤ分け、合成
するかは、layerInstructions配列におけるレイヤ命令の順序によって決まります。コンポジショ
ンを構成する第1のビデオを徐々にフェードアウトした後、第2のビデオに遷移するよう、不透明度勾
配を設定するコード例を示します。
AVAsset *firstVideoAssetTrack = <#AVAssetTrack representing the first video segment
played in the composition#>;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
43
編集
独自のビデオ処理を施す
AVAsset *secondVideoAssetTrack = <#AVAssetTrack representing the second video
segment played in the composition#>;
// Create the first video composition instruction.
AVMutableVideoCompositionInstruction *firstVideoCompositionInstruction =
[AVMutableVideoCompositionInstruction videoCompositionInstruction];
// Set its time range to span the duration of the first video track.
firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,
firstVideoAssetTrack.timeRange.duration);
// Create the layer instruction and associate it with the composition video track.
AVMutableVideoCompositionLayerInstruction *firstVideoLayerInstruction =
[AVMutableVideoCompositionLayerInstruction
videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];
// Create the opacity ramp to fade out the first video track over its entire
duration.
[firstVideoLayerInstruction setOpacityRampFromStartOpacity:1.f toEndOpacity:0.f
timeRange:CMTimeRangeMake(kCMTimeZero, firstVideoAssetTrack.timeRange.duration)];
// Create the second video composition instruction so that the second video track
isn't transparent.
AVMutableVideoCompositionInstruction *secondVideoCompositionInstruction =
[AVMutableVideoCompositionInstruction videoCompositionInstruction];
// Set its time range to span the duration of the second video track.
secondVideoCompositionInstruction.timeRange =
CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,
CMTimeAdd(firstVideoAssetTrack.timeRange.duration,
secondVideoAssetTrack.timeRange.duration));
// Create the second layer instruction and associate it with the composition video
track.
AVMutableVideoCompositionLayerInstruction *secondVideoLayerInstruction =
[AVMutableVideoCompositionLayerInstruction
videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];
// Attach the first layer instruction to the first video composition instruction.
firstVideoCompositionInstruction.layerInstructions = @[firstVideoLayerInstruction];
// Attach the second layer instruction to the second video composition instruction.
secondVideoCompositionInstruction.layerInstructions = @[secondVideoLayerInstruction];
// Attach both of the video composition instructions to the video composition.
mutableVideoComposition.instructions = @[firstVideoCompositionInstruction,
secondVideoCompositionInstruction];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
44
編集
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
Core Animationの効果を取り入れる
ビデオコンポジションには、animationToolプロパティを用いて、Core Animationのさまざまな能力
を取り入れる機能があります。このアニメーションツールで、ウォーターマーク(識別用情報)を埋
め込む、タイトルを追加する、アニメーションをオーバーレイ表示する、などの処理が可能です。
Core Animationの機能をビデオコンポジションに適用する方法は2通りあります。Core Animationレイ
ヤを独立したコンポジショントラックとして追加する方法と、(Core Animationレイヤを使って)Core
Animationの効果をコンポジションのビデオフレームに直接レンダリングする方法です。2つ目の方法
で、ビデオの中央にウォーターマークを追加するコード例を示します。
CALayer *watermarkLayer = <#CALayer representing your desired watermark image#>;
CALayer *parentLayer = [CALayer layer];
CALayer *videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0, 0, mutableVideoComposition.renderSize.width,
mutableVideoComposition.renderSize.height);
videoLayer.frame = CGRectMake(0, 0, mutableVideoComposition.renderSize.width,
mutableVideoComposition.renderSize.height);
[parentLayer addSublayer:videoLayer];
watermarkLayer.position = CGPointMake(mutableVideoComposition.renderSize.width/2,
mutableVideoComposition.renderSize.height/4);
[parentLayer addSublayer:watermarkLayer];
mutableVideoComposition.animationTool = [AVVideoCompositionCoreAnimationTool
videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer
inLayer:parentLayer];
すべてを組み合わせる:さまざまなアセットを組み合わせ、
その結果をカメラロールに保存する
アセットトラック(2本のビデオ、1本のオーディオ)を組み合わせて、単一のビデオファイルにする
コード例を以下に示します。次に挙げる処理が含まれています。
●
AVMutableCompositionオブジェクトを生成し、AVMutableCompositionTrackオブジェクトを
いくつか追加する
●
AVAssetTrackオブジェクトの時間範囲を、適合するコンポジショントラックに追加する
●
ビデオアセットトラックのpreferredTransformプロパティを調べて、ビデオの向きを判断する
●
AVMutableVideoCompositionLayerInstructionオブジェクトを使って、コンポジションを構成
するビデオトラックにアフィン変換を適用する
●
ビデオコンポジションのrenderSizeおよびframeDurationプロパティに適切な値を設定する
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
45
編集
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
●
●
ビデオファイルとしてエクスポートする際に、ビデオコンポジションと組み合わせてコンポジ
ションを使う
ビデオファイルをカメラロールに保存する
注意: 以下の例では、本質的な部分に着目できるよう、メモリ管理、エラー処理などを省略
しています。実際にAVFoundationを使用する際には、Cocoaに関する充分な経験に基づき、
欠けている部分を補ってください。
コンポジションを生成する
個々のアセットを収容するトラックを組み合わせるために、AVMutableCompositionオブジェクトを
使います。コンポジションを生成し、オーディオトラック、ビデオトラックを1本ずつ追加します。
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition
addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *audioCompositionTrack = [mutableComposition
addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
アセットを追加する
コンポジションを生成しても、空のままでは何の役にも立ちません。ビデオアセットトラック(2本)
とオーディオアセットトラックを、コンポジションに追加します。
AVAssetTrack *firstVideoAssetTrack = [[firstVideoAsset
tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack *secondVideoAssetTrack = [[secondVideoAsset
tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,
firstVideoAssetTrack.timeRange.duration) ofTrack:firstVideoAssetTrack
atTime:kCMTimeZero error:nil];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,
secondVideoAssetTrack.timeRange.duration) ofTrack:secondVideoAssetTrack
atTime:firstVideoAssetTrack.timeRange.duration error:nil];
[audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,
CMTimeAdd(firstVideoAssetTrack.timeRange.duration,
secondVideoAssetTrack.timeRange.duration)) ofTrack:[[audioAsset
tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
46
編集
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
注意: ここでは、それぞれ少なくとも1本のビデオトラックを収容するアセットが2つと、
少なくとも1本のオーディオトラックを収容するアセットが1つあるものと想定します。たと
えば、ビデオはカメラロールから、オーディオトラックは音楽ライブラリやビデオ自身から
取り込むとよいでしょう。
ビデオの向きを調べる
ビデオトラックやオーディオトラックをコンポジションに追加した後、2本のビデオトラックの向き
が正しいかどうか確認する必要があります。デフォルトでは、ビデオトラックはすべて横長モードと
想定します。縦長モードの場合、エクスポートすると正しい向きになりません。また、縦長モードの
ビデオを横長モードのそれと組み合わせようとしても、エクスポートセッションで処理に失敗してし
まいます。
BOOL isFirstVideoPortrait = NO;
CGAffineTransform firstTransform = firstVideoAssetTrack.preferredTransform;
// Check the first video track's preferred transform to determine if it was recorded
in portrait mode.
if (firstTransform.a == 0 && firstTransform.d == 0 && (firstTransform.b == 1.0 ||
firstTransform.b == -1.0) && (firstTransform.c == 1.0 || firstTransform.c == -1.0))
{
isFirstVideoPortrait = YES;
}
BOOL isSecondVideoPortrait = NO;
CGAffineTransform secondTransform = secondVideoAssetTrack.preferredTransform;
// Check the second video track's preferred transform to determine if it was recorded
in portrait mode.
if (secondTransform.a == 0 && secondTransform.d == 0 && (secondTransform.b == 1.0
|| secondTransform.b == -1.0) && (secondTransform.c == 1.0 || secondTransform.c
== -1.0)) {
isSecondVideoPortrait = YES;
}
if ((isFirstVideoAssetPortrait && !isSecondVideoAssetPortrait) ||
(!isFirstVideoAssetPortrait && isSecondVideoAssetPortrait)) {
UIAlertView *incompatibleVideoOrientationAlert = [[UIAlertView alloc]
initWithTitle:@"Error!" message:@"Cannot combine a video shot in portrait mode with
a video shot in landscape mode." delegate:self cancelButtonTitle:@"Dismiss"
otherButtonTitles:nil];
[incompatibleVideoOrientationAlert show];
return;
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
47
編集
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
ビデオコンポジションレイヤ命令を適用する
ビデオセグメントどうしの向きが合致していることを確認すると、それぞれに対してレイヤ命令を施
せるようになるので、必要なレイヤ命令をビデオコンポジションに追加します。
AVMutableVideoCompositionInstruction *firstVideoCompositionInstruction =
[AVMutableVideoCompositionInstruction videoCompositionInstruction];
// Set the time range of the first instruction to span the duration of the first
video track.
firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,
firstVideoAssetTrack.timeRange.duration);
AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction =
[AVMutableVideoCompositionInstruction videoCompositionInstruction];
// Set the time range of the second instruction to span the duration of the second
video track.
secondVideoCompositionInstruction.timeRange =
CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,
CMTimeAdd(firstVideoAssetTrack.timeRange.duration,
secondVideoAssetTrack.timeRange.duration));
AVMutableVideoCompositionLayerInstruction *firstVideoLayerInstruction =
[AVMutableVideoCompositionLayerInstruction
videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
// Set the transform of the first layer instruction to the preferred transform of
the first video track.
[firstVideoLayerInstruction setTransform:firstTransform atTime:kCMTimeZero];
AVMutableVideoCompositionLayerInstruction *secondVideoLayerInstruction =
[AVMutableVideoCompositionLayerInstruction
videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
// Set the transform of the second layer instruction to the preferred transform of
the second video track.
[secondVideoLayerInstruction setTransform:secondTransform
atTime:firstVideoAssetTrack.timeRange.duration];
firstVideoCompositionInstruction.layerInstructions = @[firstVideoLayerInstruction];
secondVideoCompositionInstruction.layerInstructions = @[secondVideoLayerInstruction];
AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition
videoComposition];
mutableVideoComposition.instructions = @[firstVideoCompositionInstruction,
secondVideoCompositionInstruction];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
48
編集
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
AVAssetTrackオブジェクトにはすべて、アセットトラックの向き情報を保持するpreferredTransform
プロパティがあります。この変換は、アセットトラックを画面に表示する際に、必ず適用されます。
先に挙げたコードでは、レイヤ命令の変換を、アセットトラックに適用する変換として設定して、新
しいコンポジション内のビデオが、描画サイズを調整すれば適切に表示されるようにしています。
描画サイズとフレーム長を設定する
ビデオの向きを確定するためには、renderSizeプロパティも適切に調整しなければなりません。さ
らにframeDurationプロパティにも、適切な値(たとえば30分の1秒、すなわち30フレーム/秒)を設
定する必要があります。renderScaleプロパティのデフォルト値は1.0であり、今回はそのままで構
いません。
CGSize naturalSizeFirst, naturalSizeSecond;
// If the first video asset was shot in portrait mode, then so was the second one
if we made it here.
if (isFirstVideoAssetPortrait) {
// Invert the width and height for the video tracks to ensure that they display
properly.
naturalSizeFirst = CGSizeMake(firstVideoAssetTrack.naturalSize.height,
firstVideoAssetTrack.naturalSize.width);
naturalSizeSecond = CGSizeMake(secondVideoAssetTrack.naturalSize.height,
secondVideoAssetTrack.naturalSize.width);
}
else {
// If the videos weren't shot in portrait mode, we can just use their natural sizes.
naturalSizeFirst = firstVideoAssetTrack.naturalSize;
naturalSizeSecond = secondVideoAssetTrack.naturalSize;
}
float renderWidth, renderHeight;
// Set the renderWidth and renderHeight to the max of the two videos widths and
heights.
if (naturalSizeFirst.width > naturalSizeSecond.width) {
renderWidth = naturalSizeFirst.width;
}
else {
renderWidth = naturalSizeSecond.width;
}
if (naturalSizeFirst.height > naturalSizeSecond.height) {
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
49
編集
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
renderHeight = naturalSizeFirst.height;
}
else {
renderHeight = naturalSizeSecond.height;
}
mutableVideoComposition.renderSize = CGSizeMake(renderWidth, renderHeight);
// Set the frame duration to an appropriate value (i.e. 30 frames per second for
video).
mutableVideoComposition.frameDuration = CMTimeMake(1,30);
コンポジションをエクスポートしてカメラロールに保存する
最後に、コンポジション全体を単一のビデオファイルとしてエクスポートし、カメラロールに保存し
ます。AVAssetExportSessionオブジェクトを使ってビデオファイルを生成し、出力ファイルの保存
先として用いるURLを指定します。次にALAssetsLibraryクラスを使って、得られたビデオファイル
をカメラロールに保存します。
// Create a static date formatter so we only have to initialize it once.
static NSDateFormatter *kDateFormatter;
if (!kDateFormatter) {
kDateFormatter = [[NSDateFormatter alloc] init];
kDateFormatter.dateStyle = NSDateFormatterMediumStyle;
kDateFormatter.timeStyle = NSDateFormatterShortStyle;
}
// Create the export session with the composition and set the preset to the highest
quality.
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset:mutableComposition presetName:AVAssetExportPresetHighestQuality];
// Set the desired output URL for the file created by the export process.
exporter.outputURL = [[[[NSFileManager defaultManager]
URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil
create:@YES error:nil] URLByAppendingPathComponent:[kDateFormatter
stringFromDate:[NSDate date]]]
URLByAppendingPathExtension:CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie,
kUTTagClassFilenameExtension))];
// Set the output file type to be a QuickTime movie.
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
50
編集
すべてを組み合わせる:さまざまなアセットを組み合わせ、その結果をカメラロールに保存する
exporter.videoComposition = mutableVideoComposition;
// Asynchronously export the composition to a video file and save this file to the
camera roll once export completes.
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (exporter.status == AVAssetExportSessionStatusCompleted) {
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
if ([assetsLibrary
videoAtPathIsCompatibleWithSavedPhotosAlbum:exporter.outputURL]) {
[assetsLibrary writeVideoAtPathToSavedPhotosAlbum:exporter.outputURL
completionBlock:NULL];
}
}
});
}];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
51
静止画像とビデオのメディアキャプチャ
カメラやマイクなどのデバイスからのキャプチャを処理するには、入力と出力を表すオブジェクトを
組み合わせ、AVCaptureSessionのインスタンスを使用して各オブジェクト間のデータの流れを調整
します。少なくとも次のインスタンスが必要です。
●
カメラやマイクなどの入力デバイスを表すAVCaptureDeviceのインスタンス
●
入力デバイスのポートを設定するAVCaptureInputの具象サブクラスのインスタンス
●
●
ムービーファイルや静止画像への出力を管理するAVCaptureOutputの具象サブクラスのインスタ
ンス
入力から出力へのデータの流れを調整するAVCaptureSessionの具象サブクラスのインスタンス
カメラで録画している内容をプレビュー表示するには、AVCaptureVideoPreviewLayer(CALayerの
サブクラス)のインスタンスを使用します。
複数の入力と出力を設定し、それらを1つのセッションによって調整できます(図 4-1を参照)。
図 4-1
単一のセッションで複数の入出力を設定する様子
多くのアプリケーションは、このレベルで十分対応できます。しかし、操作の種類によっては(たと
えば、オーディオチャンネルの出力レベルを監視したい場合など)、入力デバイスの各ポートの表現
方法や各ポートの出力への接続方法を考慮する必要があります。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
52
静止画像とビデオのメディアキャプチャ
キャプチャセッションにおけるキャプチャ入力とキャプチャ出力の接続は、AVCaptureConnection
オブジェクトで表されます。キャプチャ入力(AVCaptureInputのインスタンス)には、1つ以上の入
力ポート(AVCaptureInputPortのインスタンス)があります。キャプチャ出力(AVCaptureOutput
のインスタンス)は、1つ以上のソースからデータを受け取ります(たとえば、
AVCaptureMovieFileOutputオブジェクトはビデオデータとオーディオデータの両方を受け取りま
す)。
セッションに入力または出力を追加すると、そのセッションは互換性のあるすべてのキャプチャ入力
ポートとキャプチャ出力の接続を作成します(図 4-2を参照)。キャプチャ入力とキャプチャ出力の
接続は、AVCaptureConnectionオブジェクトで表されます。
図 4-2
AVCaptureConnectionが入力と出力の接続を表している様子
キャプチャ接続を使用して、特定の入力から特定の出力へのデータの流れを有効または無効にするこ
とができます。また、接続を使用して、オーディオチャンネルの平均出力レベルやピーク出力レベル
を監視することもできます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
53
静止画像とビデオのメディアキャプチャ
キャプチャセッションを使用したデータの流れの調整
注意: メディアキャプチャで、iOSデバイスの前面と背面のカメラを、同時に両方使って録
画することはできません。
キャプチャセッションを使用したデータの流れの調整
AVCaptureSessionオブジェクトは、データキャプチャの管理に使用する中心的な調整オブジェクト
です。このオブジェクトのインスタンスを使用して、AV入力デバイスから出力へのデータの流れを調
整します。必要なキャプチャデバイスと出力をセッションに追加した後、データの流れを開始すると
きにはセッションにstartRunningメッセージを、データの流れを終了するときにはstopRunningメッ
セージを送信します。
AVCaptureSession *session = [[AVCaptureSession alloc] init];
// Add inputs and outputs.
[session startRunning];
セッションの設定
画像の品質や解像度を指定するには、セッションのプリセットを使用します。プリセットは、考えら
れるいくつかの設定を識別する定数です。実際の設定は次のようにデバイスごとに異なる場合があり
ます。
シンボル
解像度
コメント
AVCaptureSessionPresetHigh
高
最高の録画品質
デバイスによって異なる
AVCaptureSessionPresetMedium
中
WiFi共有に適する
実際の値は変わる可能性がある
AVCaptureSessionPresetLow
低
3G共有に適する
実際の値は変わる可能性がある
AVCaptureSessionPreset640x480
640×480
VGA
AVCaptureSessionPreset1280x720
1280×720
720p HD
AVCaptureSessionPresetPhoto
写真
写真用の最大解像度
ビデオ出力ではサポートされない
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
54
静止画像とビデオのメディアキャプチャ
キャプチャセッションを使用したデータの流れの調整
メディアフレームのサイズ固有の設定を行う場合は、設定の前に、次のようにそのサイズがサポート
されていることを確認する必要があります。
if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
session.sessionPreset = AVCaptureSessionPreset1280x720;
}
else {
// Handle the failure.
}
セッションパラメータを、プリセットで可能なレベルよりきめ細かく調整する必要がある場合、ある
いは動作中のセッションに変更を施す場合は、変更処理をbeginConfigurationメソッドと
commitConfigurationメソッドの間に記述します。beginConfigurationとcommitConfiguration
を呼び出すことにより、デバイスに対する変更を一括して行い、途中の状態が見えてしまったり、不
安定になったりしないよう保証しています。beginConfigurationの呼び出し後、出力を追加/削除
し、sessionPresetプロパティを変更し、個々のキャプチャ入力/出力プロパティを設定することが
できます。実際の変更は、commitConfigurationを呼び出すまで行われません。このメソッドを呼
び出した時点で、変更がまとめて適用されます。
[session beginConfiguration];
// Remove an existing capture device.
// Add a new capture device.
// Reset the preset.
[session commitConfiguration];
キャプチャセッションの状態監視
キャプチャセッションは通知を送信します。この通知を監視することで、実行の開始、停止、中断な
どを知ることができます。ランタイムエラーが発生した場合に
AVCaptureSessionRuntimeErrorNotificationを受信するように登録することもできます。セッ
ションが実行中かどうかを確認するには、セッションのrunningプロパティを調べます。セッション
が中断されたかどうかを確認するには、セッションのinterruptedプロパティを調べます。また、
runningおよびinterruptedプロパティはキー値監視に対応しており、通知はメインスレッドに配送
されます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
55
静止画像とビデオのメディアキャプチャ
入力デバイスを表すAVCaptureDeviceオブジェクト
入力デバイスを表すAVCaptureDeviceオブジェクト
AVCaptureDeviceオブジェクトは、AVCaptureSessionオブジェクトに入力データ(オーディオやビ
デオなど)を提供する物理的なキャプチャデバイスを抽象化したものです。オブジェクトは入力デバ
イスごとに1つずつ存在します。たとえば、ビデオ入力が2つ(正面のカメラ用と背面のカメラ用)
と、マイク用のオーディオ入力が1つあります。
どのキャプチャデバイスを現在使用できるかを調べるには、AVCaptureDeviceクラスのdevicesメ
ソッド、devicesWithMediaType:メソッドを使います。必要ならば、iPhone、iPad、iPodの機能を調
べることもできます(“デバイスキャプチャの設定” (58 ページ)を参照)。ただし、利用可能なデ
バイスのリストは変わる可能性があります。(別のアプリケーションが使用しているために)現在の
入力デバイスが使用できなくなったり、(別のアプリケーションが手放したために)新しい入力デバ
イスが使用できるようになったりする可能性があります。利用可能なデバイスのリストが変更された
ことを知るには、AVCaptureDeviceWasConnectedNotificationおよび
AVCaptureDeviceWasDisconnectedNotificationの通知を受信するように登録する必要がありま
す。
キャプチャセッションに入力デバイスを追加するには、キャプチャ入力を使用します(“キャプチャ
入力を使用したセッションへのキャプチャデバイスの追加” (63 ページ)を参照)。
デバイスの特性
デバイスに対してさまざまな特性を問い合わせることができます。また、特定のメディアタイプを提
供しているかを確認するには、hasMediaType:を使用します。特定のキャプチャセッションプリセッ
トをサポートしているかを確認するには、supportsAVCaptureSessionPreset:を使用します。ユー
ザに情報を提供するために、キャプチャデバイスの位置(テスト対象ユニットの前面と背面のどちら
にあるか)とローカライズされたデバイス名を特定できます。これは、キャプチャデバイスのリスト
を表示して、ユーザがデバイスを選べるようにしたい場合に便利です。
図 4-3に、背面カメラ(AVCaptureDevicePositionBack)および前面カメラ
(AVCaptureDevicePositionFront)の位置を示します。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
56
静止画像とビデオのメディアキャプチャ
入力デバイスを表すAVCaptureDeviceオブジェクト
注意: メディアキャプチャで、iOSデバイスの前面と背面のカメラを、同時に両方使って録
画することはできません。
図 4-3
iOSデバイスの前面/背面カメラの位置
次のコード例では、利用可能なすべてのデバイスに対して反復処理を実行し、デバイス名とユニット
上の位置(ビデオデバイスの場合)をログに記録しています。
NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices) {
NSLog(@"Device name: %@", [device localizedName]);
if ([device hasMediaType:AVMediaTypeVideo]) {
if ([device position] == AVCaptureDevicePositionBack) {
NSLog(@"Device position : back");
}
else {
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
57
静止画像とビデオのメディアキャプチャ
入力デバイスを表すAVCaptureDeviceオブジェクト
NSLog(@"Device position : front");
}
}
}
さらに、デバイスの機種IDや固有IDを特定することもできます。
デバイスキャプチャの設定
デバイスの機能はデバイスごとに異なります。たとえば、複数のフォーカスモードまたはフラッシュ
モードをサポートするデバイスや、関心のある場所へのフォーカスをサポートするデバイスがありま
す。
次のコードは、トーチモードを備え、特定のキャプチャセッションプリセットをサポートするビデオ
入力デバイスを見つける方法を示しています。
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSMutableArray *torchDevices = [[NSMutableArray alloc] init];
for (AVCaptureDevice *device in devices) {
[if ([device hasTorch] &&
[device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) {
[torchDevices addObject:device];
}
}
条件を満たすデバイスが複数見つかった場合は、使用するデバイスをユーザに選ばせることもできま
す。ユーザに対してデバイスの説明を表示するには、デバイスのlocalizedNameプロパティを使用し
ます。
異なる複数の機能を同じような方法で使用します。特定のモードを指定するための定数があり、特定
のモードをサポートしているかどうかをデバイスに問い合わせることができます。いくつかのケース
では、プロセスを監視して、機能が変更されたかどうかを知ることができます。どのケースでも、“デ
バイスの設定” (62 ページ)で説明しているように、特定の機能のモードを変更する前にデバイスを
ロックする必要があります。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
58
静止画像とビデオのメディアキャプチャ
入力デバイスを表すAVCaptureDeviceオブジェクト
注意: フォーカスモードと露出モードは相互に排他的であるため、関心のある場所へのフォー
カスと関心のある場所の露出も相互に排他的です。
フォーカスモード
次の3つのフォーカスモードがあります。
AVCaptureFocusModeLocked:焦点位置が固定されます。
●
これは、ユーザがシーンを作ってから焦点をロックできるようにする場合に便利です。
AVCaptureFocusModeAutoFocus:カメラがシングルスキャンフォーカスを実行してからロック
●
状態に戻ります。
これは、フォーカスの対象を選択し、対象がシーンの中心から外れても対象への焦点を維持した
い場合に適しています。
AVCaptureFocusModeContinuousAutoFocus:カメラが必要に応じて継続的にオートフォーカス
●
を実行します。
デバイスが特定のフォーカスモードをサポートするかどうかを確認するには、isFocusModeSupported:
メソッドを使用します。モードを設定するには、focusModeプロパティを使用します。
さらに、関心のある場所へのフォーカスをデバイスがサポートしている場合もあります。サポートし
ているかどうかは、focusPointOfInterestSupportedを使用して確認します。サポートしている場
合は、focusPointOfInterestを使用して焦点を設定します。CGPointを渡します。ホームボタンが
右側になる横長モードでは 、{0,0}が画像領域の左上を表し、{1,1}が右下を表します。これは、デ
バイスが縦長モードであっても同じです。
デバイスが現在焦点を合わせているかどうかを確認するには、adjustingFocusプロパティを使用し
ます。キー値監視を使用してこのプロパティを監視することにより、デバイスによるフォーカスの開
始と停止を知ることができます。
フォーカスモードの設定を変更した場合、次のようにすればデフォルト設定に戻ります。
if ([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f);
[currentDevice setFocusPointOfInterest:autofocusPoint];
[currentDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
59
静止画像とビデオのメディアキャプチャ
入力デバイスを表すAVCaptureDeviceオブジェクト
露出モード
次の2つの露出モードがあります。
AVCaptureExposureModeContinuousAutoExposure:デバイスが必要に応じて自動的に露出レベルを
調節します。
●
AVCaptureExposureModeLocked:露出レベルは現在の値に固定です。
●
デバイスが特定の露出モードをサポートするかどうかを確認するには、isExposureModeSupported:
メソッドを使用します。モードを設定するには、exposureModeプロパティを使用します。
さらに、デバイスが関心のある場所の露出をサポートしている場合もあります。サポートしているか
どうかは、exposurePointOfInterestSupportedを使用して確認します。サポートしている場合は、
exposurePointOfInterestを使用して露出を設定します。CGPointを渡します。ホームボタンが右
側になる横長モードでは 、{0,0}が画像領域の左上を表し、{1,1}が右下を表します。これは、デバ
イスが縦長モードであっても同じです。
デバイスが現在露出の設定を変更しているかどうかを確認するには、adjustingExposureプロパティ
を使用します。キー値監視を使用してこのプロパティを監視することにより、デバイスによる露出の
設定変更の開始と停止を知ることができます。
露出の設定を変更しても、次のようにすればデフォルト設定に戻ります。
if ([currentDevice
isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
CGPoint exposurePoint = CGPointMake(0.5f, 0.5f);
[currentDevice setExposurePointOfInterest:exposurePoint];
[currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
フラッシュモード
次の3つのフラッシュモードがあります。
●
AVCaptureFlashModeOff:フラッシュが点灯しません。
●
AVCaptureFlashModeOn:フラッシュが常に点灯します。
●
AVCaptureFlashModeAuto:周囲の状態に応じてフラッシュが点灯します。
デバイスにフラッシュ機能があるかを確認するには、hasFlashを使用します。メソッドの戻り値が
YESであれば、適当なフラッシュモードを引数としてisFlashModeSupported:メソッドを実行し、デ
バイスが当該モードに対応しているか判断した上で、flashModeプロパティにモードを設定します。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
60
静止画像とビデオのメディアキャプチャ
入力デバイスを表すAVCaptureDeviceオブジェクト
トーチモード
トーチモードでは、ビデオキャプチャに照明を当てるために、フラッシュが低電力で点灯したままに
なります。次の3つのトーチモードがあります。
●
AVCaptureTorchModeOff:トーチが常にオフになります。
●
AVCaptureTorchModeOn:トーチが常にオンになります。
●
AVCaptureTorchModeAuto:必要に応じて自動的に、トーチのオンとオフが切り替わります。
デバイスにフラッシュ機能があるかを確認するには、hasTorchを使用します。デバイスが特定のフ
ラッシュモードをサポートするかを確認するには、isTorchModeSupported:メソッドを使用します。
モードを設定するには、torchModeプロパティを使用します。
トーチがあるデバイスでは、デバイスが実行中のキャプチャセッションに関連付けられている場合に
のみトーチがオンになります。
手ぶれ補正
手ぶれ補正機能は、デバイスのハードウェアにもよりますが、シネマ形式のビデオ信号を受け渡しす
る接続に対して働きます。ただし、ビデオ信号形式や解像度によっては、この機能が使えないことも
あります。
手ぶれ補正を作動すると、シネマ形式のビデオ信号の取り込みパイプラインに、さらに遅延が生じる
可能性があります。手ぶれ補正が有効になったとき、その検出にはvideoStabilizationEnabledプ
ロパティを使います。enablesVideoStabilizationWhenAvailableプロパティをオンにすれば、カ
メラが対応していると、アプリケーションが自動的に手ぶれ補正を作動するようになります。上述の
ように遅延の問題があるので、初期状態では、自動的に作動する設定はオフになっています。
ホワイトバランス
次の2つのホワイトバランスモードがあります。
●
AVCaptureWhiteBalanceModeLocked:ホワイトバランスモードが固定されます。
●
AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance:カメラが必要に応じて継続的に
ホワイトバランスを調整します。
デバイスが特定のホワイトバランスモードをサポートするかを確認するには、
isWhiteBalanceModeSupported:メソッドを使用します。モードを設定するには、whiteBalanceMode
プロパティを使用します。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
61
静止画像とビデオのメディアキャプチャ
入力デバイスを表すAVCaptureDeviceオブジェクト
デバイスが現在ホワイトバランスの設定を変更しているかを確認するには、adjustingWhiteBalance
プロパティを使用します。キー値監視を使用してこのプロパティを監視することにより、デバイスに
よるホワイトバランスの設定変更の開始と停止を知ることができます。
デバイスの向きを設定する
AVCaptureOutput(AVCaptureMovieFileOutput、AVCaptureStillImageOutput、
AVCaptureVideoDataOutput)に接続したとき、画像を出力する向きは、AVCaptureConnectionに
設定します。
AVCaptureConnectionのsupportsVideoOrientationプロパティで、ビデオの向き変更が可能かど
うか判断できます。また、videoOrientationプロパティで、出力ポートで画像をどの向きにしたい
か指定できます。リスト 4-1に、AVCaptureConnectionの向きを
AVCaptureVideoOrientationLandscapeLeftと設定する例を示します。
リスト 4-1
キャプチャ接続の向きを設定するコード例
AVCaptureConnection *captureConnection = <#A capture connection#>;
if ([captureConnection isVideoOrientationSupported])
{
AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationLandscapeLeft;
[captureConnection setVideoOrientation:orientation];
}
デバイスの設定
デバイスのキャプチャプロパティを設定するには、最初にlockForConfiguration:を使用してデバ
イスのロックを取得する必要があります。これにより、ほかのアプリケーションの設定と矛盾する変
更を避けることができます。次のコードは、デバイスのフォーカスモードを変更する方法を示してい
ます。具体的には、最初にモードがサポートされているかどうかを確認し、次にデバイスをロックし
て再設定できるようにします。ロックを取得した場合にのみフォーカスモードが変更され、その後た
だちにロックが解放されます。
if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeLocked;
[device unlockForConfiguration];
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
62
静止画像とビデオのメディアキャプチャ
キャプチャ入力を使用したセッションへのキャプチャデバイスの追加
else {
// Respond to the failure as appropriate.
デバイスのロックは、設定可能なデバイスプロパティを変更されないようにする必要がある場合にの
み保持してください。デバイスのロックを不必要に保持すると、そのデバイスを共有するほかのアプ
リケーションのキャプチャ品質が低下することがあります。
デバイスの切り替え
ユーザが入力デバイスを、たとえば前面カメラから背面カメラに、切り替えられるようにしたい場合
があります。セッションの実行中もセッションを再設定できますが、処理の中断や不連続を避けるた
め、beginConfigurationおよびcommitConfigurationを使用して設定変更をひとまとめにする必要
があります。
AVCaptureSession *session = <#A capture session#>;
[session beginConfiguration];
[session removeInput:frontFacingCameraDeviceInput];
[session addInput:backFacingCameraDeviceInput];
[session commitConfiguration];
一番外側のcommitConfigurationが呼び出されると、すべての変更がまとめて行われます。これに
よって切り替えをスムーズに実行できます。
キャプチャ入力を使用したセッションへのキャプチャデバイ
スの追加
キャプチャデバイスをキャプチャセッションに追加するには、AVCaptureDeviceInput
(AVCaptureInput抽象クラスの具象サブクラス)のインスタンスを使用します。キャプチャデバイ
ス入力は、デバイスのポートを管理します。
NSError *error;
AVCaptureDeviceInput *input =
[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
63
静止画像とビデオのメディアキャプチャ
キャプチャ出力を使用したセッションからの出力の取得
// Handle the error appropriately.
}
セッションに入力を追加するには、addInput:を使用します。必要な場合は、canAddInput:を使用
して、キャプチャ入力が既存のセッションに対応しているかどうかを確認します。
AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureDeviceInput *captureDeviceInput = <#Get a capture device input#>;
if ([captureSession canAddInput:captureDeviceInput]) {
[captureSession addInput:captureDeviceInput];
}
else {
// Handle the failure.
}
実行中のセッションを再設定する方法の詳細については、“セッションの設定” (54 ページ)を参照
してください。
AVCaptureInputは、メディアデータの1つ以上のストリームを提供します。たとえば、入力デバイス
はオーディオデータとビデオデータの両方を提供できます。入力によって提供される各メディアスト
リームは、AVCaptureInputPortオブジェクトで表されます。キャプチャセッションは、
AVCaptureConnectionオブジェクトを使用して一連のAVCaptureInputPortオブジェクトと1つの
AVCaptureOutputとのマッピングを定義します。
キャプチャ出力を使用したセッションからの出力の取得
キャプチャセッションから出力を取得するには、1つ以上の出力を追加します。出力は、
AVCaptureOutputの具象サブクラスのインスタンスです。
●
AVCaptureMovieFileOutputは、ムービーファイルに出力する場合に使用します。
●
AVCaptureVideoDataOutputは、キャプチャ中のビデオのフレームを処理する場合に使用しま
す。(たとえば、独自のビューレイヤを生成するため)
●
AVCaptureAudioDataOutputは、キャプチャ中のオーディオデータを処理する場合に使用しま
す。
●
AVCaptureStillImageOutputは、付随するメタデータを使用して静止画像をキャプチャする場
合に使用します。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
64
静止画像とビデオのメディアキャプチャ
キャプチャ出力を使用したセッションからの出力の取得
キャプチャセッションに出力を追加するには、addOutput:を使用します。canAddOutput:を使用し
て、キャプチャ出力が既存のセッションに対応しているかどうかを確認します。セッションの実行中
も、必要に応じて出力を追加または削除できます。
AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureMovieFileOutput *movieOutput = <#Create and configure a movie output#>;
if ([captureSession canAddOutput:movieOutput]) {
[captureSession addOutput:movieOutput];
}
else {
// Handle the failure.
}
ムービーファイルへの保存
ムービーデータをファイルに保存するには、AVCaptureMovieFileOutputオブジェクトを使用します
(AVCaptureMovieFileOutputは、ほとんどの基本動作を定義するAVCaptureFileOutputの具象サ
ブクラスです)。最大録画時間や最大ファイルサイズなど、ムービーファイル出力のさまざまな側面
を設定できます。ディスクの残り容量が少なくなった場合に録画を禁止することもできます。
AVCaptureMovieFileOutput *aMovieFileOutput = [[AVCaptureMovieFileOutput alloc]
init];
CMTime maxDuration = <#Create a CMTime to represent the maximum duration#>;
aMovieFileOutput.maxRecordedDuration = maxDuration;
aMovieFileOutput.minFreeDiskSpaceLimit = <#An appropriate minimum given the quality
of the movie format and the duration#>;
出力の解像度とビットレートは、キャプチャセッションのsessionPresetによって異なります。通
常、ビデオエンコードはH.264、オーディオエンコードはAACです。実際の値はデバイスによって異な
ります。
録画の開始
QuickTimeムービーの録画を開始するには、startRecordingToOutputFileURL:recordingDelegate:
を使用します。ファイルベースのURLとデリゲートを指定する必要があります。ムービーファイル出
力は既存のリソースを上書きしないため、URLに既存のファイルを指定してはいけません。また、指
定した場所に書き込むためのアクセス権が必要です。デリゲートは、
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
65
静止画像とビデオのメディアキャプチャ
キャプチャ出力を使用したセッションからの出力の取得
AVCaptureFileOutputRecordingDelegateプロトコルに準拠し、
captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:メソッドを
実装する必要があります。
AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;
NSURL *fileURL = <#A file URL that identifies the output location#>;
[aMovieFileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:<#The
delegate#>];
デリゲートのcaptureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:
の実装では、作成されたムービーをカメラロールアルバムに書き込むこともできます。また、エラー
が発生したかどうかも確認する必要があります。
ファイルが正常に書き込まれたことの確認
ファイルが正常に保存されたことを確認するには、
captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:の実装で、
エラーをチェックするだけなく、次のようにエラーのユーザ情報ディクショナリに含まれる
AVErrorRecordingSuccessfullyFinishedKeyの値も確認します。
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error {
BOOL recordedSuccessfully = YES;
if ([error code] != noErr) {
// A problem occurred: Find out if the recording was successful.
id value = [[error userInfo]
objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value) {
recordedSuccessfully = [value boolValue];
}
}
// Continue as appropriate...
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
66
静止画像とビデオのメディアキャプチャ
キャプチャ出力を使用したセッションからの出力の取得
エラーが発生してもファイルが正常に保存されている場合があるため、エラーのユーザ情報ディク
ショナリに含まれるAVErrorRecordingSuccessfullyFinishedKeyの値を確認する必要があります。
AVErrorMaximumDurationReachedやAVErrorMaximumFileSizeReachedなど、録画に関するいずれ
かの制限に達したことを示すエラーもあります。録画が停止する理由として、ほかにも次のようなも
のがあります。
●
ディスクがいっぱいになった - AVErrorDiskFull
●
録画デバイスが切断された - AVErrorDeviceWasDisconnected
●
セッションが中断された(電話がかかってきた場合など) - AVErrorSessionWasInterrupted
ファイルへのメタデータの追加
ムービーファイルのメタデータは、録画中を含めていつでも設定できます。これは、場所の情報な
ど、録画の開始時に情報を取得できない場合に便利です。ファイル出力のメタデータは、
AVMetadataItemオブジェクトの配列で表されます。このオブジェクトの可変サブクラスである
AVMutableMetadataItemのインスタンスを使用して独自のメタデータを作成します。
AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;
NSArray *existingMetadataArray = aMovieFileOutput.metadata;
NSMutableArray *newMetadataArray = nil;
if (existingMetadataArray) {
newMetadataArray = [existingMetadataArray mutableCopy];
}
else {
newMetadataArray = [[NSMutableArray alloc] init];
}
AVMutableMetadataItem *item = [[AVMutableMetadataItem alloc] init];
item.keySpace = AVMetadataKeySpaceCommon;
item.key = AVMetadataCommonKeyLocation;
CLLocation *location - <#The location to set#>;
item.value = [NSString stringWithFormat:@"%+08.4lf%+09.4lf/"
location.coordinate.latitude, location.coordinate.longitude];
[newMetadataArray addObject:item];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
67
静止画像とビデオのメディアキャプチャ
キャプチャ出力を使用したセッションからの出力の取得
aMovieFileOutput.metadata = newMetadataArray;
ビデオのフレームの処理
AVCaptureVideoDataOutputオブジェクトは、デリゲートを使用してビデオフレームを提供します。
デリゲートを設定するにはsetSampleBufferDelegate:queue:を使用します。デリゲートの設定に
加えて、デリゲートメソッドを呼び出すシリアルキューを指定します。フレームが適切な順序でデリ
ゲートに配信されるように、シリアルキューを使用する必要があります。このキューを使用して、ビ
デオフレームの配信と処理の優先度を変更できます。実装例が『SquareCam 』に載っています。
フレームは、デリゲートメソッドcaptureOutput:didOutputSampleBuffer:fromConnection:に、
不透過型CMSampleBufferRefのインスタンスとして提供されます(“メディアの表現” (112 ページ)
を参照)。デフォルトでは、カメラの最も効率的なフォーマットでバッファが送出されます。カスタ
ム出力フォーマットを指定するには、videoSettingsプロパティを使用します。ビデオ設定プロパ
ティはディクショナリです。現在サポートされている唯一のキーは
kCVPixelBufferPixelFormatTypeKeyです。推奨するピクセル形式は
availableVideoCVPixelFormatTypesプロパティ、サポートされているコーデックの値は
availableVideoCodecTypesプロパティで取得できます。BGRAフォーマットはCore GraphicsでもOpenGL
でも正常に動作します。
AVCaptureVideoDataOutput *videoDataOutput = [AVCaptureVideoDataOutput new];
NSDictionary *newSettings =
@{ (NSString *)kCVPixelBufferPixelFormatTypeKey :
@(kCVPixelFormatType_32BGRA) };
videoDataOutput.videoSettings = newSettings;
// discard if the data output queue is blocked (as we process the still image
[videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];)
// create a serial dispatch queue used for the sample buffer delegate as well as
when a still image is captured
// a serial dispatch queue must be used to guarantee that video frames will be
delivered in order
// see the header doc for setSampleBufferDelegate:queue: for more information
videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue",
DISPATCH_QUEUE_SERIAL);
[videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
68
静止画像とビデオのメディアキャプチャ
キャプチャ出力を使用したセッションからの出力の取得
AVCaptureSession *captureSession = <#The Capture Session#>;
if ( [captureSession canAddOutput:videoDataOutput] )
[captureSession addOutput:videoDataOutput];
ビデオ処理のパフォーマンスの検討事項
セッション出力をアプリケーションの最も低い実用解像度に設定してください。出力を必要以上に高
い解像度に設定すると、処理サイクルが浪費され、電力の無駄遣いになります。
captureOutput:didOutputSampleBuffer:fromConnection:の実装がフレームに割り当てられた時
間内に確実にサンプルバッファを処理できるようにする必要があります。この処理に時間がかかりす
ぎて、ビデオフレームを手放さないでいると、AV Foundationはデリゲートだけでなくほかの出力(プ
レビューレイヤなど)に対するフレームの配信も停止します。
キャプチャビデオデータ出力のminFrameDurationプロパティを使用して、フレームを処理する十分
な時間を確保できるようにフレームレートを下げることができます。alwaysDiscardsLateVideoFrames
プロパティをYES(デフォルト)に設定することもできます。これにより、遅いビデオフレームが処
理対象として渡されずに破棄されます。また、録画中に出力フレームが多少遅れても問題ない場合
は、prefer プロパティをNOに設定してすべてのフレームを取得することもできます。これはフレーム
が破棄されないという意味ではありません(つまり、この場合もフレームが破棄されることはありま
す)が、ただちに(効率的に)破棄されなくなります。
静止画像のキャプチャ
付随するメタデータを使用して静止画像をキャプチャしたい場合は、AVCaptureStillImageOutput
出力を使用します。画像の解像度は、セッションのプリセットやデバイスによって異なります。
ピクセルとエンコードフォーマット
次のように、デバイスによってサポートされる画像フォーマットが異なります。デバイスがサポート
するピクセルとコーデックの種類を調べるには、availableImageDataCVPixelFormatTypesと
availableImageDataCodecTypesをそれぞれ使用します。いずれも、指定されたデバイスがサポー
トする値の配列を返します。たとえば、次のようにoutputSettingsディクショナリを設定して必要
な画像フォーマットを指定します。
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc]
init];
NSDictionary *outputSettings = @{ AVVideoCodecKey : AVVideoCodecJPEG};
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
69
静止画像とビデオのメディアキャプチャ
キャプチャ出力を使用したセッションからの出力の取得
[stillImageOutput setOutputSettings:outputSettings];
JPEG画像をキャプチャする場合は、通常、独自の圧縮フォーマットを指定するべきではありません。
代わりに、ハードウェアで高速化される静止画像出力固有の圧縮を使用するべきです。画像のデータ
表現が必要な場合は、jpegStillImageNSDataRepresentation:を使用すると、画像のメタデータを
変更した場合でもデータを再圧縮せずにNSDataオブジェクトを取得できます。
画像のキャプチャ
画像をキャプチャするときは、出力に
captureStillImageAsynchronouslyFromConnection:completionHandler:メッセージを送信しま
す。最初の引数は、キャプチャに使用する接続です。次のように、入力ポートでビデオが収集されて
いる接続を探す必要があります。
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo] ) {
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
captureStillImageAsynchronouslyFromConnection:completionHandler:に対する2番目の引数
は、画像データを格納した不透過型のCMSampleBufferとエラーの2つの引数を取るブロックです。サ
ンプルバッファ自体にアタッチメントとしてメタデータ(EXIFディクショナリなど)が含まれる場合
があります。必要な場合はアタッチメントを変更できますが、“ピクセルとエンコードフォーマッ
ト” (69 ページ)で説明したJPEG画像の最適化に注意してください。
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection
completionHandler:
^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
CFDictionaryRef exifAttachments =
CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary,
NULL);
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
70
静止画像とビデオのメディアキャプチャ
ユーザに対する録画内容の表示
if (exifAttachments) {
// Do something with the attachments.
}
// Continue as appropriate.
}];
ユーザに対する録画内容の表示
カメラで録画している内容をユーザに提供するには、プレビューレイヤを使用します。マイクで録音
している内容をユーザに提供するには、オーディオチャンネルを監視します。
ビデオのプレビュー
録画している内容のプレビューをユーザに対して表示するには、AVCaptureVideoPreviewLayerオブ
ジェクトを使用します。AVCaptureVideoPreviewLayerはCALayerのサブクラスです(『Core Animation
Programming Guide 』を参照)。プレビューを表示するための出力は必要ありません。
クライアントアプリケーションはAVCaptureVideoDataOutputクラスを使って、実際に表示する前
に、ビデオのピクセルにアクセスできます。
キャプチャ出力とは異なり、ビデオのプレビューレイヤは関連付けられたセッションの強い参照を保
持します。これは、レイヤがビデオを表示しようとしている間にセッションが割り当て解除されない
ようにするためです。これは、次のように、プレビューレイヤを初期化する方法に反映されていま
す。
AVCaptureSession *captureSession = <#Get a capture session#>;
CALayer *viewLayer = <#Get a layer from the view in which you want to present the
preview#>;
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer
alloc] initWithSession:captureSession];
[viewLayer addSublayer:captureVideoPreviewLayer];
一般に、プレビューレイヤはレンダリングツリー内のほかのCALayerオブジェクトと同じように動作
します(『Core Animation Programming Guide 』を参照)。他のレイヤと同じように、画像の拡大縮
小、変換、回転などを実行できます。1つ異なる点として、カメラから入力された画像の回転方法を
指定するためにレイヤのorientationプロパティを設定する必要がある場合があります。さらに、ビ
デオのミラーリングが可能かどうか、supportsVideoMirroringプロパティで調べることができま
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
71
静止画像とビデオのメディアキャプチャ
ユーザに対する録画内容の表示
す。必要に応じてvideoMirroredプロパティを設定できます。ただし、
automaticallyAdjustsVideoMirroringプロパティがYES(既定値)であれば、セッションの設定に
もとづき自動的に設定されます。
ビデオ映像の重心設定モード
プレビューレイヤでは、videoGravityを使用して設定する重心位置として、次の3つがサポートされ
ます。
●
AVLayerVideoGravityResizeAspect:アスペクト比が維持され、利用可能な画面領域いっぱい
にビデオ映像が表示されないときは黒いバーが残ります。
●
AVLayerVideoGravityResizeAspectFill:アスペクト比が維持されますが、必要に応じてビデ
オ映像はトリミングされ、利用可能な画面領域いっぱいに表示されます。
●
AVLayerVideoGravityResize:利用可能な画面領域いっぱいに表示されるようにビデオが引き
伸ばされます(画像が変形することもあります)。
プレビューでの「タップしてフォーカス」の使用
「タップしてフォーカス」をプレビューレイヤと組み合わせて実装するときは、注意が必要です。プ
レビューの向きとレイヤの重心位置、およびプレビューがミラーリングされている可能性を考慮する
必要があります。この機能の実装方法については、『AVCam for iOS 』サンプルコードプロジェクトを
参照してください。
オーディオレベルの表示
キャプチャ接続に含まれるオーディオチャンネルの平均出力レベルやピーク出力レベルを監視するに
は、AVCaptureAudioChannelオブジェクトを使用します。オーディオレベルのキー値監視はできな
いため、ユーザインターフェイスを更新する頻度に合わせて(たとえば毎秒10回)レベルの更新を
ポーリングする必要があります。
AVCaptureAudioDataOutput *audioDataOutput = <#Get the audio data output#>;
NSArray *connections = audioDataOutput.connections;
if ([connections count] > 0) {
// There should be only one connection to an AVCaptureAudioDataOutput.
AVCaptureConnection *connection = [connections objectAtIndex:0];
NSArray *audioChannels = connection.audioChannels;
for (AVCaptureAudioChannel *channel in audioChannels) {
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
72
静止画像とビデオのメディアキャプチャ
すべてを組み合わせる:UIImageオブジェクトとしてのビデオフレームのキャプチャ
float avg = channel.averagePowerLevel;
float peak = channel.peakHoldLevel;
// Update the level meter user interface.
}
}
すべてを組み合わせる:UIImageオブジェクトとしてのビデオ
フレームのキャプチャ
次の簡単なコード例は、ビデオをキャプチャし、取得したフレームをUIImageオブジェクトに変換す
る方法を示しています。具体的な方法は次のとおりです。
●
AVCaptureSessionオブジェクトを作成して、AV入力デバイスから出力へのデータの流れを調整
する
●
必要な入力タイプのAVCaptureDeviceオブジェクトを見つける
●
デバイスのAVCaptureDeviceInputオブジェクトを作成する
●
AVCaptureVideoDataOutputオブジェクトを作成して、ビデオフレームを生成する
●
AVCaptureVideoDataOutputオブジェクトのデリゲートを実装して、ビデオフレームを処理する
●
デリゲートから受け取ったCMSampleBufferをUIImageオブジェクトに変換する関数を実装する
注意: この例では最も重要なコードに注目するため、完全なアプリケーションからメモリ管
理など、いくつかの部分を省略しています。AV Foundationを使用するには、欠落した部分
を推測できるように、Cocoaに関する十分な経験を積んでいることが期待されます。
キャプチャセッションの作成と設定
AV入力デバイスから出力へのデータの流れを調整するには、AVCaptureSessionオブジェクトを使用
します。セッションを作成し、中程度の解像度を持つビデオフレームを生成するように設定します。
AVCaptureSession *session = [[AVCaptureSession alloc] init];
session.sessionPreset = AVCaptureSessionPresetMedium;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
73
静止画像とビデオのメディアキャプチャ
すべてを組み合わせる:UIImageオブジェクトとしてのビデオフレームのキャプチャ
デバイスおよびデバイス入力の作成と設定
キャプチャデバイスはAVCaptureDeviceオブジェクトで表されます。このクラスには、必要な入力タ
イプのオブジェクトを取得するためのメソッドが用意されています。デバイスには1つ以上のポート
があり、AVCaptureInputオブジェクトを使用して各ポートを設定します。通常、キャプチャ入力は
デフォルト設定で使用します。
ビデオキャプチャデバイスを見つけ、そのデバイスを使用してデバイス入力を作成し、そのデバイス
入力にセッションを追加します。適切なデバイスが見つからなかった場合、
deviceInputWithDevice:error:メソッドは参照型の引数を介してエラー情報を返します。
AVCaptureDevice *device =
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
AVCaptureDeviceInput *input =
[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
// Handle the error appropriately.
}
[session addInput:input];
ビデオデータ出力の作成と設定
キャプチャ中のビデオから入力された非圧縮フレームを処理するには、AVCaptureVideoDataOutput
を使用します。通常は、出力のいくつかの側面を設定します。たとえば、ビデオの場合は、
videoSettingsプロパティを使用してピクセルフォーマットを指定したり、minFrameDurationプロ
パティを設定してフレームレートを制限したりできます。
次のように、ビデオデータ用の出力を作成して設定し、それをセッションに追加します。また、
minFrameDurationプロパティを1/15秒に設定してフレームレートを15fpsまでに制限します。
AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
[session addOutput:output];
output.videoSettings =
@{ (NSString *)kCVPixelBufferPixelFormatTypeKey :
@(kCVPixelFormatType_32BGRA) };
output.minFrameDuration = CMTimeMake(1, 15);
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
74
静止画像とビデオのメディアキャプチャ
すべてを組み合わせる:UIImageオブジェクトとしてのビデオフレームのキャプチャ
データ出力オブジェクトは、デリゲートを使用してビデオフレームを提供します。デリゲートは、
AVCaptureVideoDataOutputSampleBufferDelegateプロトコルを採用する必要があります。データ
出力のデリゲートを設定するときは、コールバックを呼び出すキューも指定する必要があります。
dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);
キューを使用して、ビデオフレームの配信と処理の優先度を変更します。
サンプルバッファのデリゲートメソッドの実装
デリゲートクラスには、サンプルバッファが書き込まれたときに呼び出されるメソッド
(captureOutput:didOutputSampleBuffer:fromConnection:)を実装します。ビデオデータ出力
オブジェクトはフレームをCMSampleBufferという不透過型の値として配信するので、CMSampleBuffer
をUIImageオブジェクトに変換する必要があります。この操作の機能は、“CMSampleBufferからUIImage
オブジェクトへの変換” (113 ページ)に示しています。
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection {
UIImage *image = imageFromSampleBuffer(sampleBuffer);
// Add your code here that uses the image.
}
このデリゲートメソッドはsetSampleBufferDelegate:queue:で指定したキュー上で呼び出されま
す。ユーザインターフェイスを更新したい場合は、関連するコードをメインスレッド上で呼び出す必
要があります。
録画の開始と停止
キャプチャセッションの設定後、ユーザの環境設定を調べ、録画が許可されているかどうか確認する
必要があります。
NSString *mediaType = AVMediaTypeVideo;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
75
静止画像とビデオのメディアキャプチャ
すべてを組み合わせる:UIImageオブジェクトとしてのビデオフレームのキャプチャ
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL
granted) {
if (granted)
{
//Granted access to mediaType
[self setDeviceAuthorized:YES];
}
else
{
//Not granted access to mediaType
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"AVCam!"
message:@"AVCam doesn't have permission to use
Camera, please change privacy settings"
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
[self setDeviceAuthorized:NO];
});
}
}];
カメラセッションが設定されていて、ユーザがカメラ(および、必要な場合はマイク)へのアクセスを
承認している場合は、startRunningメッセージを送信して録画を開始します。
Important: startRunningメソッドはブロック型で多少時間がかかるので、セッションのセット
アップ処理を直列キュー上で実行し、メインキューがブロックされない(UIの応答性を維持する)よ
うにしなければなりません。基本的な実装例については、『AVCamforiOS 』を参照してください。
[session startRunning];
録画を停止するには、セッションにstopRunningメッセージを送信します。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
76
静止画像とビデオのメディアキャプチャ
高フレームレートのビデオキャプチャ
高フレームレートのビデオキャプチャ
iOS 7.0では、一部のハードウェアで、高フレームレートのビデオキャプチャ(「SloMo」ビデオ)が
可能になりました。AVFoundationフレームワークは全面的に高フレームレートのコンテンツを扱える
ようになっています。
デバイスのキャプチャ能力は、AVCaptureDeviceFormatクラスで調べることができます。メディア
タイプ、フレームレート、ビューフィールド、最大拡大率、手ぶれ補正の有無などを返すメソッド群
を備えています。
●
●
●
●
キャプチャ機能は、720p(1280×720ピクセル)の解像度で、毎秒60フレーム(fps)まで対応し、
手ぶれ補正やPフレームを抜く処理(H264エンコードした動画の機能で、旧型の低速ハードウェ
アでも滑らかに再生できるようにするもの)も可能です。
再生機能は、音声の低速/高速再生が可能になり、再生速度にかかわらず時間ピッチは保存されま
す。
可変コンポジションについては、時間スケールの編集に完全対応しています。
60fpsの動画を扱う場合、エクスポート時に2つのオプションを適用できます。可変のフレームレー
ト(低速/高速の動き)を保存でき、また、30fpsなど低速のフレームレートに変換することも可
能です。
『SloPoke 』に載っているサンプルコードでは、AVFoundationの高速ビデオキャプチャや、高フレーム
レートのビデオキャプチャにハードウェアが対応しているかどうかの判断、さまざまなサンプルレー
トやタイムピッチアルゴリズムによる再生、編集(コンポジション位置に対する時間スケールの設定
を含む)などの実装方法が示されています。
再生
AVPlayerのインスタンスは、setRate:メソッドで値を設定することにより、さまざまな再生速度を
一括して管理できます。この値は再生速度の乗数を表します。値が1.0であれば通常の再生、0.5なら
ば半分の速度、5.0ならば5倍速、などとなります。
AVPlayerItemオブジェクトにはaudioTimePitchAlgorithmというプロパティがあります。動画を各
種のフレームレートで再生する場合の音声再生方法を、Time Pitch Algorithm Settingsに示す定
数で、このプロパティに指定できます。
適用可能なタイムピッチアルゴリズムを表に示します。音質、特定のフレームレートに補正するか否
か、フレームレートの範囲も記載します。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
77
静止画像とビデオのメディアキャプチャ
高フレームレートのビデオキャプチャ
タイムピッチアルゴリズム
音質
特定のフ
レームレー
トに補正す
るか
レートの範囲
AVAudioTimePitchAlgorithmLowQualityZeroLatency
低音質、早送りや巻き戻
し、低音質の音声に適。
YES
0.5、0.666667、
0.8、1.0、1.25、
1.5、2.0倍。
AVAudioTimePitchAlgorithmTimeDomain
中音質、低計算量、音声に
適。
NO
0.5~2倍。
AVAudioTimePitchAlgorithmSpectral
高音質、高計算量、元の
ピッチを保存。
NO
1/32~32倍。
AVAudioTimePitchAlgorithmVarispeed
高音質再生、ピッチ補正な
し。
NO
1/32~32倍。
編集
AVMutableCompositionクラスを使って、時間軸に沿った編集が可能です。
●
AVMutableCompositionのインスタンスは、クラスメソッドcompositionで生成します。
●
ビデオアセットの挿入はinsertTimeRange:ofAsset:atTime:error:メソッドで行います。
●
コンポジションの一部分の時間スケールを、scaleTimeRange:toDuration:で設定できます。
エクスポート
60 fpsのビデオエクスポート機能は、AVAssetExportSessionクラスを使ってアセットをエクスポー
トするようになっています。実際の方法は2通りあります。
●
AVAssetExportPresetPassthroughプリセットを使えば、動画の再エンコードを回避できます。
メディアの各セクションに、「60 fps」、「低速化」、「高速化」のタグをつけて時間軸を変更
します。
●
定フレームレートでエクスポートすれば、多くの環境でそのまま再生できます。ビデオコンポジ
ションのframeDurationプロパティを30 fpsと設定します。タイムピッチは、エクスポートセッ
ションのaudioTimePitchAlgorithmプロパティでも指定できます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
78
静止画像とビデオのメディアキャプチャ
高フレームレートのビデオキャプチャ
録画
高フレームレートのビデオはAVCaptureMovieFileOutputクラスで取り込むことができ、これは自動
的に高フレームレート録画にも対応しています。H264ピッチレベルやビットレートも自動的に正しい
値になります。
独自の設定で録画するためにはAVAssetWriterクラスを使いますが、多少のセットアップ手続きを要
します。
assetWriterInput.expectsMediaDataInRealTime=YES;
この設定により、入力データに追随して取り込めるようになります。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
79
エクスポート
オーディオビジュアルアセットを読み書きするためには、AVFoundationフレームワークに組み込まれ
た、エクスポート処理APIを使う必要があります。AVAssetExportSessionクラスには、エクスポー
トの際に必要な、単純な処理を行うインターフェイスが備わっています。ファイル形式を変更する、
アセットの長さを調整する、などの処理です(“ムービーのトリミングとフォーマット変換” (18 ペー
ジ)を参照)。より高度なエクスポート処理が必要であれば、AVAssetReaderクラス、AVAssetWriter
クラスを使ってください。
AVAssetReaderクラスは、アセットの中身に対して何らかの処理を施す場合に使います。たとえば、
アセットのオーディオトラックを読み込んで、波形を表示できます。サンプルバッファ、静止画像な
どのメディアからアセットを生成するためには、AVAssetWriterオブジェクトを使います。
注意: アセットの読み書きをする上記のクラスは、実時間処理を想定したものではありませ
ん。HTTPライブストリーミングなど、実時間で供給されるデータ源から読み込むことすら
できないのです。もっとも、AVCaptureOutputオブジェクトなどの実時間データ源と連携
してアセットライターを使うことは、その入力のexpectsMediaDataInRealTimeプロパティ
をYESとすることにより可能です。ただし、実時間データ源でない場合にこのプロパティを
YESにすると、ファイルが適切にインターリーブされません。
アセットを読み込む
各AVAssetReaderオブジェクトには、同時に1つのアセットしか対応づけることができません。もっ
とも、このアセットが複数のトラックから成ることはありえます。そこで、各トラックに対応して
AVAssetReaderOutputの具象サブクラスを与えることにより、メディアデータの読み込み方法を設
定しておきます。AVAssetReaderOutputの具象サブクラスとしては、AVAssetReaderTrackOutput、
AVAssetReaderAudioMixOutput、AVAssetReaderVideoCompositionOutputの3種類があり、読み
込むアセットの種類に応じて使い分けます。
アセットリーダーを生成する
AVAssetReaderオブジェクトの初期化に必要なのは、読み込み対象のアセットだけです。
NSError *outError;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
80
エクスポート
アセットを読み込む
AVAsset *someAsset = <#AVAsset that you want to read#>;
AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:someAsset
error:&outError];
BOOL success = (assetReader != nil);
注意: 戻り値であるアセットリーダーがnilでない(正常に初期化できた)ことを、必ず確
認してください。nilの場合、「error:」引数(上の例ではoutError)を介してエラーの詳
細情報が返されます。
アセットリーダー出力を設定する
アセットリーダーを生成した後、読み込んだメディアデータを受け取るために、出力を少なくとも1
つ設定する必要があります。その際、alwaysCopiesSampleDataプロパティはNOとしてください。こ
れには性能向上の効果があります。以下に示す例はすべて、このプロパティをNOとしています。
いくつかのトラックからメディアデータを読み込み、別の形式に変換するだけであれば、該当するト
ラック(AVAssetTrackオブジェクト)それぞれについて、アセットリーダー出力として
AVAssetReaderTrackOutputクラスを指定するとよいでしょう。アセットリーダーで、(圧縮され
た)オーディオトラックを伸長してLinear PCM形式にする場合、トラック出力を次のように設定して
ください。
AVAsset *localAsset = assetReader.asset;
// Get the audio track to read.
AVAssetTrack *audioTrack = [[localAsset tracksWithMediaType:AVMediaTypeAudio]
objectAtIndex:0];
// Decompression settings for Linear PCM
NSDictionary *decompressionAudioSettings = @{ AVFormatIDKey : [NSNumber
numberWithUnsignedInt:kAudioFormatLinearPCM] };
// Create the output with the audio track and decompression settings.
AVAssetReaderOutput *trackOutput = [AVAssetReaderTrackOutput
assetReaderTrackOutputWithTrack:audioTrack
outputSettings:decompressionAudioSettings];
// Add the output to the reader if possible.
if ([assetReader canAddOutput:trackOutput])
[assetReader addOutput:trackOutput];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
81
エクスポート
アセットを読み込む
注意: あるアセットトラックから、(何の手も加えず)保存されているままの形式でメディ
アデータを読み込みたい場合は、outputSettings引数としてnilを渡してください。
AVAssetReaderAudioMixOutputクラス、AVAssetReaderVideoCompositionOutputクラスはそれぞ
れ、AVAudioMixオブジェクトやAVVideoCompositionオブジェクトでミックス/合成されたメディア
データを読み込むために使います。この出力を使うのは一般に、アセットリーダーがAVComposition
オブジェクトから読み込む場合です。
オーディオミックス出力は1つだけで、AVAudioMixオブジェクトでミックスされたアセットから、複
数のオーディオトラックを読み込めます。オーディオトラックのミックス方法を指定したいときは、
初期化の後、AVAssetReaderAudioMixOutputオブジェクトに設定してください。アセットのオー
ディオトラックすべてを用いるオーディオミックス出力を生成し、伸長してLinear PCM形式にし、オー
ディオミックスオブジェクトを出力として割り当てるコード例を以下に示します。オーディオミック
スの設定方法について詳しくは、“編集” (37 ページ)を参照してください。
AVAudioMix *audioMix = <#An AVAudioMix that specifies how the audio tracks from
the AVAsset are mixed#>;
// Assumes that assetReader was initialized with an AVComposition object.
AVComposition *composition = (AVComposition *)assetReader.asset;
// Get the audio tracks to read.
NSArray *audioTracks = [composition tracksWithMediaType:AVMediaTypeAudio];
// Get the decompression settings for Linear PCM.
NSDictionary *decompressionAudioSettings = @{ AVFormatIDKey : [NSNumber
numberWithUnsignedInt:kAudioFormatLinearPCM] };
// Create the audio mix output with the audio tracks and decompression setttings.
AVAssetReaderOutput *audioMixOutput = [AVAssetReaderAudioMixOutput
assetReaderAudioMixOutputWithAudioTracks:audioTracks
audioSettings:decompressionAudioSettings];
// Associate the audio mix used to mix the audio tracks being read with the output.
audioMixOutput.audioMix = audioMix;
// Add the output to the reader if possible.
if ([assetReader canAddOutput:audioMixOutput])
[assetReader addOutput:audioMixOutput];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
82
エクスポート
アセットを読み込む
注意: 引数audioSettingsとしてnilを渡せば、アセットリーダーはサンプルを非圧縮形式
で返すようになります。これはAVAssetReaderVideoCompositionOutputクラスについて
も同様です。
ビデオコンポジション出力の動作もほぼ同様です。AVVideoCompositionオブジェクトで合成された
アセットから、複数のビデオトラックを読み込めます。合成された複数のビデオトラックからメディ
アデータを読み込み、伸長してARGB形式にするためには、出力を次のように設定します。
AVVideoComposition *videoComposition = <#An AVVideoComposition that specifies how
the video tracks from the AVAsset are composited#>;
// Assumes assetReader was initialized with an AVComposition.
AVComposition *composition = (AVComposition *)assetReader.asset;
// Get the video tracks to read.
NSArray *videoTracks = [composition tracksWithMediaType:AVMediaTypeVideo];
// Decompression settings for ARGB.
NSDictionary *decompressionVideoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey
: [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB],
(id)kCVPixelBufferIOSurfacePropertiesKey : [NSDictionary dictionary] };
// Create the video composition output with the video tracks and decompression
setttings.
AVAssetReaderOutput *videoCompositionOutput = [AVAssetReaderVideoCompositionOutput
assetReaderVideoCompositionOutputWithVideoTracks:videoTracks
videoSettings:decompressionVideoSettings];
// Associate the video composition used to composite the video tracks being read
with the output.
videoCompositionOutput.videoComposition = videoComposition;
// Add the output to the reader if possible.
if ([assetReader canAddOutput:videoCompositionOutput])
[assetReader addOutput:videoCompositionOutput];
アセットのメディアデータを読み込む
出力の設定がすべて済んだ後、アセットリーダーのstartReadingメソッドを呼び出すと読み込みが
始まります。その後、copyNextSampleBufferメソッドで、各出力から個別にメディアデータを取得
します。出力を1つ割り当てたアセットリーダーを起動し、メディアサンプルをすべて読み込むコー
ド例を以下に示します。
// Start the asset reader up.
[self.assetReader startReading];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
83
エクスポート
アセットを書き出す
BOOL done = NO;
while (!done)
{
// Copy the next sample buffer from the reader output.
CMSampleBufferRef sampleBuffer = [self.assetReaderOutput copyNextSampleBuffer];
if (sampleBuffer)
{
// Do something with sampleBuffer here.
CFRelease(sampleBuffer);
sampleBuffer = NULL;
}
else
{
// Find out why the asset reader output couldn't copy another sample buffer.
if (self.assetReader.status == AVAssetReaderStatusFailed)
{
NSError *failureError = self.assetReader.error;
// Handle the error here.
}
else
{
// The asset reader output has read all of its samples.
done = YES;
}
}
}
アセットを書き出す
AVAssetWriterクラスを使うと、複数のデータ源から収集したメディアデータを、指定した形式の、
単一のファイルに書き出すことができます。アセットライターオブジェクトを特定のアセットに対応
づける必要はありませんが、出力ファイルをいくつか生成する場合、それぞれに対してアセットライ
ターを用意する必要があります。アセットライターは複数のデータ源から得たメディアデータを書き
出せるので、出力ファイルに書き出そうとする個々のトラックごとに、AVAssetWriterInputオブ
ジェクトを生成する必要があります。各AVAssetWriterInputオブジェクトは、CMSampleBufferRef
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
84
エクスポート
アセットを書き出す
オブジェクトの形でデータを受け取るものと想定しますが、CVPixelBufferRefオブジェクトをア
セットライター入力に追加する場合は、AVAssetWriterInputPixelBufferAdaptorクラスを使って
ください。
アセットライターを生成する
アセットライターを生成する際には、出力ファイルのURLと、そのファイル型を指定します。QuickTime
ムービーを生成するように、アセットライターを初期化するコード例を示します。
NSError *outError;
NSURL *outputURL = <#NSURL object representing the URL where you want to save the
video#>;
AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:outputURL
fileType:AVFileTypeQuickTimeMovie
error:&outError];
BOOL success = (assetWriter != nil);
アセットライター入力を設定する
アセットライターがメディアデータを書き出すためには、アセットライター入力を少なくとも1つ設
定する必要があります。たとえば、メディアデータ源が既に、メディアサンプルをCMSampleBufferRef
オブジェクトの形で供給しているのであれば、AVAssetWriterInputクラスを使うだけで構いませ
ん。オーディオメディアデータを圧縮して128 kbps AAC形式にするようアセットライター入力を設定
し、これをアセットライターに接続するコード例を以下に示します。
// Configure the channel layout as stereo.
AudioChannelLayout stereoChannelLayout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo,
.mChannelBitmap = 0,
.mNumberChannelDescriptions = 0
};
// Convert the channel layout object to an NSData object.
NSData *channelLayoutAsData = [NSData dataWithBytes:&stereoChannelLayout
length:offsetof(AudioChannelLayout, mChannelDescriptions)];
// Get the compression settings for 128 kbps AAC.
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
85
エクスポート
アセットを書き出す
NSDictionary *compressionAudioSettings = @{
AVFormatIDKey
: [NSNumber numberWithUnsignedInt:kAudioFormatMPEG4AAC],
AVEncoderBitRateKey
: [NSNumber numberWithInteger:128000],
AVSampleRateKey
: [NSNumber numberWithInteger:44100],
AVChannelLayoutKey
: channelLayoutAsData,
AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:2]
};
// Create the asset writer input with the compression settings and specify the
media type as audio.
AVAssetWriterInput *assetWriterInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:compressionAudioSettings];
// Add the input to the writer if possible.
if ([assetWriter canAddInput:assetWriterInput])
[assetWriter addInput:assetWriterInput];
注意: メディアデータを、(何の手も加えず)保存されているままの形式で書き出したい場
合は、outputSettings引数としてnilを渡してください。ただし、このようにnilを渡して
よいのは、アセットライターを初期化する際、fileTypeとしてAVFileTypeQuickTimeMovie
を指定した場合に限ります。
アセットライター入力には、若干のメタデータを収容し、あるいは特定のトラックに対して異なる変
換を指定することも可能です。これはそれぞれ、metadataプロパティ、transformプロパティを使っ
て行います。データ源がビデオトラックであるアセットライター入力に対して、次のように、出力
ファイルにおけるビデオの変換をそのまま指定することも可能です。
AVAsset *videoAsset = <#AVAsset with at least one video track#>;
AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo]
objectAtIndex:0];
assetWriterInput.transform = videoAssetTrack.preferredTransform;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
86
エクスポート
アセットを書き出す
注意: metadataプロパティやtransformプロパティは、アセットライターで書き出しを始
める前に設定しなければ、効果が現れません。
メディアデータを出力ファイルに書き出す際、ピクセルバッファを確保する必要があるかも知れませ
ん。これはAVAssetWriterInputPixelBufferAdaptorクラスを使って実装できます。処理効率を考
慮して、独立したプールに確保したピクセルバッファを追加するのではなく、ピクセルバッファアダ
プタが用意しているピクセルバッファプールを使ってください。ピクセルバッファオブジェクトを生
成するコード例を以下に示します。RGBドメインで機能し、CGImageオブジェクトを使ってピクセル
バッファを生成するオブジェクトです。
NSDictionary *pixelBufferAttributes = @{
kCVPixelBufferCGImageCompatibilityKey : [NSNumber numberWithBool:YES],
kCVPixelBufferCGBitmapContextCompatibilityKey : [NSNumber numberWithBool:YES],
kCVPixelBufferPixelFormatTypeKey : [NSNumber
numberWithInt:kCVPixelFormatType_32ARGB]
};
AVAssetWriterInputPixelBufferAdaptor *inputPixelBufferAdaptor =
[AVAssetWriterInputPixelBufferAdaptor
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:self.assetWriterInput
sourcePixelBufferAttributes:pixelBufferAttributes];
注意: AVAssetWriterInputPixelBufferAdaptorオブジェクトはすべて、単一のアセット
ライター入力に接続しなければなりません。アセットライター入力は、AVMediaTypeVideo
型のメディアデータを受け付ける必要があります。
メディアデータを書き出す
アセットライターに必要な入力をすべて設定すれば、メディアデータを書き出せます。アセットリー
ダーの場合と同様に、startWritingメソッドを呼び出すと書き出し処理が始まります。その後、
startSessionAtSourceTime:メソッドで、サンプル書き出しセッションを起動します。アセットラ
イターによる書き出しはすべて、いずれかのセッション内で行う必要があります。各セッションの時
間範囲によって、データ源から取り込まれるメディアデータの時間範囲が決まります。たとえば、
データ源がAVAssetオブジェクトから読み込んだメディアデータを供給するアセットリーダーである
場合、アセットの前半部分に当たるメディアデータを除外したければ、次のようなコードになりま
す。
CMTime halfAssetDuration = CMTimeMultiplyByFloat64(self.asset.duration, 0.5);
[self.assetWriter startSessionAtSourceTime:halfAssetDuration];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
87
エクスポート
アセットを書き出す
//Implementation continues.
通常、書き出しセッションを終了するためには、endSessionAtSourceTime:メソッドを呼び出さな
ければなりません。しかし、ファイルの末尾までセッションが進んでいれば、finishWritingメソッ
ドだけで終了させることができます。単一の入力のアセットライターを起動し、メディアデータをす
べて書き出すコード例を以下に示します。
// Prepare the asset writer for writing.
[self.assetWriter startWriting];
// Start a sample-writing session.
[self.assetWriter startSessionAtSourceTime:kCMTimeZero];
// Specify the block to execute when the asset writer is ready for media data and
the queue to call it on.
[self.assetWriterInput requestMediaDataWhenReadyOnQueue:myInputSerialQueue
usingBlock:^{
while ([self.assetWriterInput isReadyForMoreMediaData])
{
// Get the next sample buffer.
CMSampleBufferRef nextSampleBuffer = [self copyNextSampleBufferToWrite];
if (nextSampleBuffer)
{
// If it exists, append the next sample buffer to the output file.
[self.assetWriterInput appendSampleBuffer:nextSampleBuffer];
CFRelease(nextSampleBuffer);
nextSampleBuffer = nil;
}
else
{
// Assume that lack of a next sample buffer means the sample buffer
source is out of samples and mark the input as finished.
[self.assetWriterInput markAsFinished];
break;
}
}
}];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
88
エクスポート
アセットを再エンコードする
上のコード例で、copyNextSampleBufferToWriteメソッドは単なるスタブです。実際には、何らか
の処理ロジックを実装して、書き出そうとするメディアデータを表す、CMSampleBufferRefオブジェ
クトを返すようにしてください。サンプルバッファ源としては、たとえばアセットリーダー出力が考
えられます。
アセットを再エンコードする
アセットリーダーとアセットライターを連携させることにより、アセットの表現(形式)を変換でき
ます。AVAssetExportSessionを使用するよりもきめ細かく、変換方法を制御できるのです。たとえ
ば、出力ファイルで表現するトラックを選ぶ、独自の出力フォーマットを指定する、変換処理中にア
セットを変更する、といったことが可能です。まず、アセットリーダー出力およびアセットライター
入力を、要件に応じて設定します。その後、startReadingメソッド、startWritingメソッドをそれ
ぞれ使って起動します。アセットリーダー出力から供給されるメディアデータを、アセットライター
入力を使って書き出すコード例を示します。
NSString *serializationQueueDescription = [NSString stringWithFormat:@"%@
serialization queue", self];
// Create a serialization queue for reading and writing.
dispatch_queue_t serializationQueue =
dispatch_queue_create([serializationQueueDescription UTF8String], NULL);
// Specify the block to execute when the asset writer is ready for media data and
the queue to call it on.
[self.assetWriterInput requestMediaDataWhenReadyOnQueue:serializationQueue
usingBlock:^{
while ([self.assetWriterInput isReadyForMoreMediaData])
{
// Get the asset reader output's next sample buffer.
CMSampleBufferRef sampleBuffer = [self.assetReaderOutput
copyNextSampleBuffer];
if (sampleBuffer != NULL)
{
// If it exists, append this sample buffer to the output file.
BOOL success = [self.assetWriterInput
appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);
sampleBuffer = NULL;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
89
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
// Check for errors that may have occurred when appending the new
sample buffer.
if (!success && self.assetWriter.status == AVAssetWriterStatusFailed)
{
NSError *failureError = self.assetWriter.error;
//Handle the error.
}
}
else
{
// If the next sample buffer doesn't exist, find out why the asset
reader output couldn't vend another one.
if (self.assetReader.status == AVAssetReaderStatusFailed)
{
NSError *failureError = self.assetReader.error;
//Handle the error here.
}
else
{
// The asset reader output must have vended all of its samples.
Mark the input as finished.
[self.assetWriterInput markAsFinished];
break;
}
}
}
}];
すべてを組み合わせる:アセットリーダーとアセットライター
を組み合わせてアセットを再エンコードする
アセットリーダーおよびアセットライターを使って、アセットの第1トラック(ビデオおよびオーディ
オ)を再エンコードし、新しいファイルに書き出すコード例を示します。次に挙げる処理が含まれて
います。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
90
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
●
●
●
●
オーディオビジュアルデータの読み書きは非同期に発生するので、シリアル化キューを使って処
理する。
アセットリーダーを初期化し、アセットリーダー出力を2つ(オーディオ用、ビデオ用)設定す
る。
アセットライターを初期化し、アセットライター入力を2つ(オーディオ用、ビデオ用)設定す
る。
アセットリーダーを使い、出力/入力の組(オーディオ用/ビデオ用)を通して、非同期にメディ
アデータをアセットライターに供給する。
●
ディスパッチグループを使って、再エンコード処理が終了した旨の通知を受け取る。
●
再エンコード処理中、ユーザが取り消すための手段を与える。
注意: 以下の例では、本質的な部分に着目できるよう、いくつかの処理を省略しています。
AVFoundationを使用するには、欠落した部分を推測できるように、Cocoaに関する十分な経
験を積んでいることが期待されます。
初期設定を行う
アセットリーダーやアセットライターを生成し、その出力や入力を設定する前に、いくつか必要な初
期設定があります。まず、読み込み処理と書き出し処理がうまく連携できるよう、シリアル化キュー
を3つ、別々に生成します。
NSString *serializationQueueDescription = [NSString stringWithFormat:@"%@
serialization queue", self];
// Create the main serialization queue.
self.mainSerializationQueue = dispatch_queue_create([serializationQueueDescription
UTF8String], NULL);
NSString *rwAudioSerializationQueueDescription = [NSString stringWithFormat:@"%@
rw audio serialization queue", self];
// Create the serialization queue to use for reading and writing the audio data.
self.rwAudioSerializationQueue =
dispatch_queue_create([rwAudioSerializationQueueDescription UTF8String], NULL);
NSString *rwVideoSerializationQueueDescription = [NSString stringWithFormat:@"%@
rw video serialization queue", self];
// Create the serialization queue to use for reading and writing the video data.
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
91
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
self.rwVideoSerializationQueue =
dispatch_queue_create([rwVideoSerializationQueueDescription UTF8String], NULL);
主たるシリアル化キューは、アセットリーダー/ライターの起動処理や、(おそらくユーザが処理を
取り消したために起こる)停止処理を調整するために使います。残る2つのシリアル化キューは、そ
れぞれの出力/入力の組による読み書きの処理をシリアル化して、いつ取り消しが要求されても対処
できるようにするために使います。
シリアル化キューが準備できたので、アセットのトラックをロードし、再エンコード処理を開始しま
す。
self.asset = <#AVAsset that you want to reencode#>;
self.cancelled = NO;
self.outputURL = <#NSURL representing desired output URL for file generated by
asset writer#>;
// Asynchronously load the tracks of the asset you want to read.
[self.asset loadValuesAsynchronouslyForKeys:@[@"tracks"] completionHandler:^{
// Once the tracks have finished loading, dispatch the work to the main
serialization queue.
dispatch_async(self.mainSerializationQueue, ^{
// Due to asynchronous nature, check to see if user has already cancelled.
if (self.cancelled)
return;
BOOL success = YES;
NSError *localError = nil;
// Check for success of loading the assets tracks.
success = ([self.asset statusOfValueForKey:@"tracks" error:&localError]
== AVKeyValueStatusLoaded);
if (success)
{
// If the tracks loaded successfully, make sure that no file exists
at the output path for the asset writer.
NSFileManager *fm = [NSFileManager defaultManager];
NSString *localOutputPath = [self.outputURL path];
if ([fm fileExistsAtPath:localOutputPath])
success = [fm removeItemAtPath:localOutputPath
error:&localError];
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
92
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
if (success)
success = [self setupAssetReaderAndAssetWriter:&localError];
if (success)
success = [self startAssetReaderAndWriter:&localError];
if (!success)
[self readingAndWritingDidFinishSuccessfully:success
withError:localError];
});
}];
トラックのロード終了後は、成功か否かにかかわらず、それ以降の処理を主たるシリアル化キューに
ディスパッチして、ユーザがいつ処理を取り消しても対処できるようにします。次に、取り消し処理
と、先に挙げたコードの末尾に現れる3つのメソッドを実装します。
アセットリーダー/ライターを初期化する
ここで実装するsetupAssetReaderAndAssetWriter:メソッドでは、リーダーとライターを初期化
し、出力/入力の組(オーディオトラック用、ビデオトラック用)をそれぞれ設定します。この例で
は、アセットリーダーを使ってオーディオをLinear PCMに伸長し、アセットライターで再び128 kbps
AACに圧縮します。一方、ビデオはアセットリーダーでYUVに伸長し、アセットライターでH.264に圧
縮します。
- (BOOL)setupAssetReaderAndAssetWriter:(NSError **)outError
{
// Create and initialize the asset reader.
self.assetReader = [[AVAssetReader alloc] initWithAsset:self.asset
error:outError];
BOOL success = (self.assetReader != nil);
if (success)
{
// If the asset reader was successfully initialized, do the same for the
asset writer.
self.assetWriter = [[AVAssetWriter alloc] initWithURL:self.outputURL
fileType:AVFileTypeQuickTimeMovie error:outError];
success = (self.assetWriter != nil);
}
if (success)
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
93
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
{
// If the reader and writer were successfully initialized, grab the audio
and video asset tracks that will be used.
AVAssetTrack *assetAudioTrack = nil, *assetVideoTrack = nil;
NSArray *audioTracks = [self.asset tracksWithMediaType:AVMediaTypeAudio];
if ([audioTracks count] > 0)
assetAudioTrack = [audioTracks objectAtIndex:0];
NSArray *videoTracks = [self.asset tracksWithMediaType:AVMediaTypeVideo];
if ([videoTracks count] > 0)
assetVideoTrack = [videoTracks objectAtIndex:0];
if (assetAudioTrack)
{
// If there is an audio track to read, set the decompression settings
to Linear PCM and create the asset reader output.
NSDictionary *decompressionAudioSettings = @{ AVFormatIDKey :
[NSNumber numberWithUnsignedInt:kAudioFormatLinearPCM] };
self.assetReaderAudioOutput = [AVAssetReaderTrackOutput
assetReaderTrackOutputWithTrack:assetAudioTrack
outputSettings:decompressionAudioSettings];
[self.assetReader addOutput:self.assetReaderAudioOutput];
// Then, set the compression settings to 128kbps AAC and create the
asset writer input.
AudioChannelLayout stereoChannelLayout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo,
.mChannelBitmap = 0,
.mNumberChannelDescriptions = 0
};
NSData *channelLayoutAsData = [NSData
dataWithBytes:&stereoChannelLayout length:offsetof(AudioChannelLayout,
mChannelDescriptions)];
NSDictionary *compressionAudioSettings = @{
AVFormatIDKey
: [NSNumber
numberWithUnsignedInt:kAudioFormatMPEG4AAC],
AVEncoderBitRateKey
: [NSNumber numberWithInteger:128000],
AVSampleRateKey
: [NSNumber numberWithInteger:44100],
AVChannelLayoutKey
: channelLayoutAsData,
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
94
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:2]
};
self.assetWriterAudioInput = [AVAssetWriterInput
assetWriterInputWithMediaType:[assetAudioTrack mediaType]
outputSettings:compressionAudioSettings];
[self.assetWriter addInput:self.assetWriterAudioInput];
}
if (assetVideoTrack)
{
// If there is a video track to read, set the decompression settings
for YUV and create the asset reader output.
NSDictionary *decompressionVideoSettings = @{
(id)kCVPixelBufferPixelFormatTypeKey
numberWithUnsignedInt:kCVPixelFormatType_422YpCbCr8],
: [NSNumber
(id)kCVPixelBufferIOSurfacePropertiesKey : [NSDictionary
dictionary]
};
self.assetReaderVideoOutput = [AVAssetReaderTrackOutput
assetReaderTrackOutputWithTrack:assetVideoTrack
outputSettings:decompressionVideoSettings];
[self.assetReader addOutput:self.assetReaderVideoOutput];
CMFormatDescriptionRef formatDescription = NULL;
// Grab the video format descriptions from the video track and grab
the first one if it exists.
NSArray *videoFormatDescriptions = [assetVideoTrack
formatDescriptions];
if ([videoFormatDescriptions count] > 0)
formatDescription = (__bridge
CMFormatDescriptionRef)[formatDescriptions objectAtIndex:0];
CGSize trackDimensions = {
.width = 0.0,
.height = 0.0,
};
// If the video track had a format description, grab the track
dimensions from there. Otherwise, grab them direcly from the track itself.
if (formatDescription)
trackDimensions =
CMVideoFormatDescriptionGetPresentationDimensions(formatDescription, false, false);
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
95
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
else
trackDimensions = [assetVideoTrack naturalSize];
NSDictionary *compressionSettings = nil;
// If the video track had a format description, attempt to grab the
clean aperture settings and pixel aspect ratio used by the video.
if (formatDescription)
{
NSDictionary *cleanAperture = nil;
NSDictionary *pixelAspectRatio = nil;
CFDictionaryRef cleanApertureFromCMFormatDescription =
CMFormatDescriptionGetExtension(formatDescription,
kCMFormatDescriptionExtension_CleanAperture);
if (cleanApertureFromCMFormatDescription)
{
cleanAperture = @{
AVVideoCleanApertureWidthKey
(id)CFDictionaryGetValue(cleanApertureFromCMFormatDescription,
kCMFormatDescriptionKey_CleanApertureWidth),
:
AVVideoCleanApertureHeightKey
(id)CFDictionaryGetValue(cleanApertureFromCMFormatDescription,
kCMFormatDescriptionKey_CleanApertureHeight),
:
AVVideoCleanApertureHorizontalOffsetKey :
(id)CFDictionaryGetValue(cleanApertureFromCMFormatDescription,
kCMFormatDescriptionKey_CleanApertureHorizontalOffset),
AVVideoCleanApertureVerticalOffsetKey
(id)CFDictionaryGetValue(cleanApertureFromCMFormatDescription,
kCMFormatDescriptionKey_CleanApertureVerticalOffset)
:
};
}
CFDictionaryRef pixelAspectRatioFromCMFormatDescription =
CMFormatDescriptionGetExtension(formatDescription,
kCMFormatDescriptionExtension_PixelAspectRatio);
if (pixelAspectRatioFromCMFormatDescription)
{
pixelAspectRatio = @{
AVVideoPixelAspectRatioHorizontalSpacingKey :
(id)CFDictionaryGetValue(pixelAspectRatioFromCMFormatDescription,
kCMFormatDescriptionKey_PixelAspectRatioHorizontalSpacing),
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
96
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
AVVideoPixelAspectRatioVerticalSpacingKey
(id)CFDictionaryGetValue(pixelAspectRatioFromCMFormatDescription,
kCMFormatDescriptionKey_PixelAspectRatioVerticalSpacing)
:
};
}
// Add whichever settings we could grab from the format
description to the compression settings dictionary.
if (cleanAperture || pixelAspectRatio)
{
NSMutableDictionary *mutableCompressionSettings =
[NSMutableDictionary dictionary];
if (cleanAperture)
[mutableCompressionSettings setObject:cleanAperture
forKey:AVVideoCleanApertureKey];
if (pixelAspectRatio)
[mutableCompressionSettings setObject:pixelAspectRatio
forKey:AVVideoPixelAspectRatioKey];
compressionSettings = mutableCompressionSettings;
}
}
// Create the video settings dictionary for H.264.
NSMutableDictionary *videoSettings = (NSMutableDictionary *) @{
AVVideoCodecKey
: AVVideoCodecH264,
AVVideoWidthKey : [NSNumber
numberWithDouble:trackDimensions.width],
AVVideoHeightKey : [NSNumber
numberWithDouble:trackDimensions.height]
};
// Put the compression settings into the video settings dictionary
if we were able to grab them.
if (compressionSettings)
[videoSettings setObject:compressionSettings
forKey:AVVideoCompressionPropertiesKey];
// Create the asset writer input and add it to the asset writer.
self.assetWriterVideoInput = [AVAssetWriterInput
assetWriterInputWithMediaType:[videoTrack mediaType] outputSettings:videoSettings];
[self.assetWriter addInput:self.assetWriterVideoInput];
}
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
97
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
return success;
}
アセットを再エンコードする
アセットリーダー/ライターを正常に初期化、設定していれば、startAssetReaderAndWriter:メソッ
ド(“初期設定を行う” (91 ページ)を参照)が呼び出されます。このメソッド内で、実際にアセッ
トの読み書きを行います。
- (BOOL)startAssetReaderAndWriter:(NSError **)outError
{
BOOL success = YES;
// Attempt to start the asset reader.
success = [self.assetReader startReading];
if (!success)
*outError = [self.assetReader error];
if (success)
{
// If the reader started successfully, attempt to start the asset writer.
success = [self.assetWriter startWriting];
if (!success)
*outError = [self.assetWriter error];
}
if (success)
{
// If the asset reader and writer both started successfully, create the
dispatch group where the reencoding will take place and start a sample-writing
session.
self.dispatchGroup = dispatch_group_create();
[self.assetWriter startSessionAtSourceTime:kCMTimeZero];
self.audioFinished = NO;
self.videoFinished = NO;
if (self.assetWriterAudioInput)
{
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
98
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
// If there is audio to reencode, enter the dispatch group before
beginning the work.
dispatch_group_enter(self.dispatchGroup);
// Specify the block to execute when the asset writer is ready for
audio media data, and specify the queue to call it on.
[self.assetWriterAudioInput
requestMediaDataWhenReadyOnQueue:self.rwAudioSerializationQueue usingBlock:^{
// Because the block is called asynchronously, check to see
whether its task is complete.
if (self.audioFinished)
return;
BOOL completedOrFailed = NO;
// If the task isn't complete yet, make sure that the input is
actually ready for more media data.
while ([self.assetWriterAudioInput isReadyForMoreMediaData] &&
!completedOrFailed)
{
// Get the next audio sample buffer, and append it to the
output file.
CMSampleBufferRef sampleBuffer =
[self.assetReaderAudioOutput copyNextSampleBuffer];
if (sampleBuffer != NULL)
{
BOOL success = [self.assetWriterAudioInput
appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);
sampleBuffer = NULL;
completedOrFailed = !success;
}
else
{
completedOrFailed = YES;
}
}
if (completedOrFailed)
{
// Mark the input as finished, but only if we haven't
already done so, and then leave the dispatch group (since the audio work has
finished).
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
99
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
BOOL oldFinished = self.audioFinished;
self.audioFinished = YES;
if (oldFinished == NO)
{
[self.assetWriterAudioInput markAsFinished];
}
dispatch_group_leave(self.dispatchGroup);
}
}];
}
if (self.assetWriterVideoInput)
{
// If we had video to reencode, enter the dispatch group before
beginning the work.
dispatch_group_enter(self.dispatchGroup);
// Specify the block to execute when the asset writer is ready for
video media data, and specify the queue to call it on.
[self.assetWriterVideoInput
requestMediaDataWhenReadyOnQueue:self.rwVideoSerializationQueue usingBlock:^{
// Because the block is called asynchronously, check to see
whether its task is complete.
if (self.videoFinished)
return;
BOOL completedOrFailed = NO;
// If the task isn't complete yet, make sure that the input is
actually ready for more media data.
while ([self.assetWriterVideoInput isReadyForMoreMediaData] &&
!completedOrFailed)
{
// Get the next video sample buffer, and append it to the
output file.
CMSampleBufferRef sampleBuffer =
[self.assetReaderVideoOutput copyNextSampleBuffer];
if (sampleBuffer != NULL)
{
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
100
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
BOOL success = [self.assetWriterVideoInput
appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);
sampleBuffer = NULL;
completedOrFailed = !success;
}
else
{
completedOrFailed = YES;
}
}
if (completedOrFailed)
{
// Mark the input as finished, but only if we haven't
already done so, and then leave the dispatch group (since the video work has
finished).
BOOL oldFinished = self.videoFinished;
self.videoFinished = YES;
if (oldFinished == NO)
{
[self.assetWriterVideoInput markAsFinished];
}
dispatch_group_leave(self.dispatchGroup);
}
}];
}
// Set up the notification that the dispatch group will send when the
audio and video work have both finished.
dispatch_group_notify(self.dispatchGroup, self.mainSerializationQueue,
^{
BOOL finalSuccess = YES;
NSError *finalError = nil;
// Check to see if the work has finished due to cancellation.
if (self.cancelled)
{
// If so, cancel the reader and writer.
[self.assetReader cancelReading];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
101
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
[self.assetWriter cancelWriting];
}
else
{
// If cancellation didn't occur, first make sure that the asset
reader didn't fail.
if ([self.assetReader status] == AVAssetReaderStatusFailed)
{
finalSuccess = NO;
finalError = [self.assetReader error];
}
// If the asset reader didn't fail, attempt to stop the asset
writer and check for any errors.
if (finalSuccess)
{
finalSuccess = [self.assetWriter finishWriting];
if (!finalSuccess)
finalError = [self.assetWriter error];
}
}
// Call the method to handle completion, and pass in the appropriate
parameters to indicate whether reencoding was successful.
[self readingAndWritingDidFinishSuccessfully:finalSuccess
withError:finalError];
});
}
// Return success here to indicate whether the asset reader and writer were
started successfully.
return success;
}
音声/動画トラックの再エンコードは、個々のシリアル化キューを使って非同期的に行うことにより
全体の処理性能を改善していますが、いずれも同じディスパッチグループに属します。各トラックの
処理を同じディスパッチグループに置いているのは、処理がすべて終了したときに通知を送り、正常
終了であったかどうか判断できるようにするためです。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
102
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
終了時の処理
読み込み/書き出し処理が終了すると、readingAndWritingDidFinishSuccessfully:メソッドが呼
び出されます。その際、正常に再エンコードできたかどうかを表すパラメータが渡されます。正常終
了でなければ、アセットリーダーおよびアセットライターの処理を取り消し、UI関係のタスクを主た
るキューにディスパッチすることになります。
- (void)readingAndWritingDidFinishSuccessfully:(BOOL)success withError:(NSError
*)error
{
if (!success)
{
// If the reencoding process failed, we need to cancel the asset reader
and writer.
[self.assetReader cancelReading];
[self.assetWriter cancelWriting];
dispatch_async(dispatch_get_main_queue(), ^{
// Handle any UI tasks here related to failure.
});
}
else
{
// Reencoding was successful, reset booleans.
self.cancelled = NO;
self.videoFinished = NO;
self.audioFinished = NO;
dispatch_async(dispatch_get_main_queue(), ^{
// Handle any UI tasks here related to success.
});
}
}
取り消し時の処理
シリアル化キューを複数使うことにより、ユーザが簡単に、再エンコード処理を取り消せるようにな
ります。主たるシリアル化キューでは、メッセージを非同期に、アセット再エンコード処理用のシリ
アル化キュー(オーディオ、ビデオ)に送信して、読み込み/書き出し処理を取り消そうとします。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
103
エクスポート
すべてを組み合わせる:アセットリーダーとアセットライターを組み合わせてアセットを再エンコードする
この2つのシリアル化キューが取り消し処理を終えると、ディスパッチグループはその旨の通知を主
たるシリアル化キューに送信し、その結果、cancelledプロパティがYESになります。次のような
cancelメソッドを、画面上のボタンなどに対応づけるとよいでしょう。
- (void)cancel
{
// Handle cancellation asynchronously, but serialize it with the main queue.
dispatch_async(self.mainSerializationQueue, ^{
// If we had audio data to reencode, we need to cancel the audio work.
if (self.assetWriterAudioInput)
{
// Handle cancellation asynchronously again, but this time serialize
it with the audio queue.
dispatch_async(self.rwAudioSerializationQueue, ^{
// Update the Boolean property indicating the task is complete
and mark the input as finished if it hasn't already been marked as such.
BOOL oldFinished = self.audioFinished;
self.audioFinished = YES;
if (oldFinished == NO)
{
[self.assetWriterAudioInput markAsFinished];
}
// Leave the dispatch group since the audio work is finished
now.
dispatch_group_leave(self.dispatchGroup);
});
}
if (self.assetWriterVideoInput)
{
// Handle cancellation asynchronously again, but this time serialize
it with the video queue.
dispatch_async(self.rwVideoSerializationQueue, ^{
// Update the Boolean property indicating the task is complete
and mark the input as finished if it hasn't already been marked as such.
BOOL oldFinished = self.videoFinished;
self.videoFinished = YES;
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
104
エクスポート
アセット出力設定アシスタント
if (oldFinished == NO)
{
[self.assetWriterVideoInput markAsFinished];
}
// Leave the dispatch group, since the video work is finished
now.
dispatch_group_leave(self.dispatchGroup);
});
}
// Set the cancelled Boolean property to YES to cancel any work on the
main queue as well.
self.cancelled = YES;
});
}
アセット出力設定アシスタント
AVOutputSettingsAssistantクラスには、アセットリーダー/ライターの出力設定辞書作成を支援す
る働きがあります。セットアップ処理の記述が簡潔になりますが、特に高フレームレートのH264動画
で、プリセットがいくつもある場合に効果的です。リスト 5-1に、出力設定アシスタントを使って、
実際に設定を行うコード例を示します。
リスト 5-1
AVOutputSettingsAssistantの使用例
AVOutputSettingsAssistant *outputSettingsAssistant = [AVOutputSettingsAssistant
outputSettingsAssistantWithPreset:<some preset>];
CMFormatDescriptionRef audioFormat = [self getAudioFormat];
if (audioFormat != NULL)
[outputSettingsAssistant
setSourceAudioFormat:(CMAudioFormatDescriptionRef)audioFormat];
CMFormatDescriptionRef videoFormat = [self getVideoFormat];
if (videoFormat != NULL)
[outputSettingsAssistant
setSourceVideoFormat:(CMVideoFormatDescriptionRef)videoFormat];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
105
エクスポート
アセット出力設定アシスタント
CMTime assetMinVideoFrameDuration = [self getMinFrameDuration];
CMTime averageFrameDuration = [self getAvgFrameDuration]
[outputSettingsAssistant setSourceVideoAverageFrameDuration:averageFrameDuration];
[outputSettingsAssistant setSourceVideoMinFrameDuration:assetMinVideoFrameDuration];
AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:<some URL>
fileType:[outputSettingsAssistant outputFileType] error:NULL];
AVAssetWriterInput *audioInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:[outputSettingsAssistant audioSettings] sourceFormatHint:audioFormat];
AVAssetWriterInput *videoInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:[outputSettingsAssistant videoSettings] sourceFormatHint:videoFormat];
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
106
時間およびメディアの表現
AV Foundationフレームワークでは、時間ベースのオーディオビジュアルデータ(ムービーファイルや
ビデオストリームなど)をAVAssetで表します。フレームワークのほとんどの作業は、アセットの構
造によって規定されます。AV Foundationが時間とメディアの表現に使用するいくつかの低レベルデー
タ構造(サンプルバッファなど)は、Core Mediaフレームワークによって提供されます。
アセットの表現
AVAssetは、AV Foundationフレームワークのコアクラスです。時間ベースのオーディオビジュアル
データ(ムービーファイルやビデオストリーム)のフォーマットに依存しない抽象表現を提供しま
す。主要な関係を図 6-1に示します。多くの場合、サブクラスのいずれかを使用します。新しいアセッ
トを作成するときは、コンポジションサブクラスを使用します(“編集” (9 ページ)を参照)。ま
た、特定のURLにあるメディアから新しいアセットインスタンスを作成するには、AVURLAssetを使用
します(MPMediaフレームワークまたはAsset Libraryフレームワークのアセットを含む。“アセットの
使用” (12 ページ)を参照)。
図 6-1
AVAssetによる時間ベースのオーディオビジュアルデータの抽象化
アセットには、まとめて表示または処理するトラックのコレクションが含まれています。各トラック
のメディアタイプは同じであり、メディアタイプにはオーディオ、ビデオ、テキスト、クローズド
キャプション、サブタイトルなどがあります(ただし、これらに限定されません)。アセットオブ
ジェクトは、リソース全体に関する情報(再生時間やタイトルなど)とともに、表示に関するヒント
(固有のサイズなど)を提供します。アセットには、AVMetadataItemのインスタンスで表されるメ
タデータも格納できます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
107
時間およびメディアの表現
時間の表現
トラックはAVAssetTrackのインスタンスで表されます(図 6-2を参照)。通常の簡単なケースでは、
オーディオコンポーネントを表すトラックとビデオコンポーネントを表すトラックが別個に存在しま
すが、複雑なコンポジションでは、オーディオとビデオのトラックが複数重複して存在する場合もあ
ります。
図 6-2
AVAssetTrack
トラックは、トラックのタイプ(ビデオまたはオーディオ)、ビデオまたはオーディオの特性(該当
する場合)、メタデータ、タイムライン(親アセットに関して表される)などのいくつかのプロパ
ティと、フォーマット記述の配列を持っています。この配列には、トラックが参照するメディアサン
プルのフォーマットを記述するCMFormatDescriptionオブジェクトが格納されます
(CMFormatDescriptionRefを参照)。同じメディアを格納しているトラック(たとえば、すべて同
じ設定を使用してエンコードされた場合など)は、個数1の配列を提供します。
トラック自体がAVAssetTrackSegmentのインスタンスで表される複数のセグメントに分割されるこ
ともあります。セグメントは、ソースからアセットトラックのタイムラインにマッピングされる時間
です。
時間の表現
AV Foundationでは、時間をCore Mediaフレームワークの基本的な構造体で表します。
時間の長さを表すCMTime
CMTimeは、時間を分子(int64_tの値)と分母(int32_tの時間係数)からなる有理数で表すCの構
造体です。時間係数は、概念的には分子の各単位が1秒間に占める割合を示します。したがって、時
間係数が4であれば、各単位は1/4秒を表し、時間係数が10であれば、各単位は1/10秒を表します。時
間係数としては、よく使用されるフレームレートの公倍数である600が頻繁に使用されます。よく使
用されるフレームレートとは、映画の24fps(1秒あたりのフレーム数)、NTSCの30fps(北米および日
本のTVで使用される)、およびPALの25fps(ヨーロッパのTVで使用される)です。時間係数として600
を使用することで、これらのシステムのフレーム数を正確に表すことができます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
108
時間およびメディアの表現
時間の表現
CMTime構造体は、単純な時間値に加えて、非数値である正の無限値、負の無限値、および不定値を
表すこともできます。また、時間の丸め処理が行われたかどうかを示したり、エポック番号を保持す
ることもできす。
CMTimeの使用
時間を作成するには、CMTimeMakeまたは関連する関数のいずれか(たとえば、float型の値にして時
間を作成し、任意の時間スケールを指定できるCMTimeMakeWithSecondsなど)を使用します。次の
例に示すように、時間ベースの算術演算や時間の比較を行うための関数がいくつか用意されていま
す。
CMTime time1 = CMTimeMake(200, 2); // 200 half-seconds
CMTime time2 = CMTimeMake(400, 4); // 400 quarter-seconds
// time1 and time2 both represent 100 seconds, but using different timescales.
if (CMTimeCompare(time1, time2) == 0) {
NSLog(@"time1 and time2 are the same");
}
Float64 float64Seconds = 200.0 / 3;
CMTime time3 = CMTimeMakeWithSeconds(float64Seconds , 3); // 66.66... third-seconds
time3 = CMTimeMultiply(time3, 3);
// time3 now represents 200 seconds; next subtract time1 (100 seconds).
time3 = CMTimeSubtract(time3, time1);
CMTimeShow(time3);
if (CMTIME_COMPARE_INLINE(time2, ==, time3)) {
NSLog(@"time2 and time3 are the same");
}
利用可能なすべての関数の一覧については、『CMTime Reference 』を参照してください。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
109
時間およびメディアの表現
時間の表現
CMTimeの特殊な値
Core Mediaには、特殊な値を表すための定数として、kCMTimeZero、kCMTimeInvalid、
kCMTimePositiveInfinity、およびkCMTimeNegativeInfinityが用意されています。CMTime構造
体は、たとえば時間として無効な値をさまざまな方法で表すことができます。CMTimeが有効か否か、
あるいは非数値かを確認する必要がある場合は、適切なマクロ(CMTIME_IS_INVALID、
CMTIME_IS_POSITIVE_INFINITY、CMTIME_IS_INDEFINITEなど)を使用してください。
CMTime myTime = <#Get a CMTime#>;
if (CMTIME_IS_INVALID(myTime)) {
// Perhaps treat this as an error; display a suitable alert to the user.
}
任意のCMTime構造体の値をkCMTimeInvalidと比較しないでください。
オブジェクトとしてのCMTimeの表現
注釈またはCore FoundationコンテナでCMTime構造体を使用する必要がある場合は、CMTime構造体と
CFDictionary不透過型(CFDictionaryRefを参照)を相互に変換してください。これは、それぞれ
CMTimeCopyAsDictionary関数およびCMTimeMakeFromDictionary関数で行います。CMTime構造体
の文字列表現は、CMTimeCopyDescription関数で取得することも可能です。
エポック
CMTime構造体のエポック番号は通常0に設定されていますが、エポック番号を使用して関係のないタ
イムラインを区別することができます。たとえば、表示ループを1回通すたびにエポックをインクリ
メントすることにより、ループ0の時間Nとループ1の時間Nとを区別できます。
時間範囲を表すCMTimeRange
CMTimeRangeはCの構造体で、CMTime構造体で表される開始時刻と持続時間を保持します。時間範囲
には、開始時刻に持続時間を加えた時刻は含まれません。
時間範囲を作成するには、CMTimeRangeMakeまたはCMTimeRangeFromTimeToTimeを使用します。
CMTimeのエポックの値には次の制約があります。
●
●
異なるエポックにまたがるCMTimeRange構造体は作成できません。
タイムスタンプを表すCMTime構造体のエポックは、0以外にすることができますが、範囲操作
(CMTimeRangeGetUnionなど)の実行対象になるのは開始フィールドが同じエポックを持つ範囲
だけです。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
110
時間およびメディアの表現
時間の表現
持続時間を表すCMTime構造体は、エポックが0、値が非負でなければなりません。
●
時間範囲の操作
Core Mediaには、時間範囲に特定の時間や他の時間範囲が含まれているかどうかを判定したり、2つの
時間範囲が等しいかどうかを判定したり、時間範囲の論理和や論理積を計算したりする関数
(CMTimeRangeContainsTime、CMTimeRangeEqual、CMTimeRangeContainsTimeRange、
CMTimeRangeGetUnionなど)が用意されています。
開始時刻に持続時間を加えた時刻は時間範囲に含まれないため、次の式は常にfalseとして評価されま
す。
CMTimeRangeContainsTime(range, CMTimeRangeGetEnd(range))
利用可能なすべての関数の一覧については、『CMTimeRange Reference 』を参照してください。
CMTimeRangeの特殊な値
Core Mediaには、長さ0の範囲を表す定数(kCMTimeRangeZero)と無効な範囲を表す定数
(kCMTimeRangeInvalid)が用意されています。ただし、CMTimeRange構造体では、無効、ゼロ、
または不定(CMTime構造体のいずれかが不定の場合)をさまざまな方法で表すことができます。
CMTimeRange構造体が無効、ゼロ、または不定かを確認する必要がある場合は、適切なマクロ
(CMTIMERANGE_IS_VALID、CMTIMERANGE_IS_INVALID、CMTIMERANGE_IS_EMPTY、
CMTIMERANGE_IS_EMPTY)を使用してください。
CMTimeRange myTimeRange = <#Get a CMTimeRange#>;
if (CMTIMERANGE_IS_EMPTY(myTimeRange)) {
// The time range is zero.
}
任意のCMTimeRange構造体の値をkCMTimeRangeInvalidと比較しないでください。
オブジェクトとしてのCMTimeRange構造体の表現
注釈またはCore FoundationコンテナでCMTimeRange構造体を使用する必要がある場合は、CMTimeRange
構造体とCFDictionary不透過型(CFDictionaryRefを参照)を相互に変換してください。これは、そ
れぞれCMTimeRangeCopyAsDictionary関数およびCMTimeRangeMakeFromDictionary関数で行いま
す。CMTimeRangeCopyDescription関数を使用してCMTimeの文字列表現を取得することもできます。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
111
時間およびメディアの表現
メディアの表現
メディアの表現
AV Foundationでは、ビデオデータと関連するメタデータをCore Mediaフレームワークの不透過オブ
ジェクトで表します。Core Mediaでは、CMSampleBufferを使用してビデオデータが表されます
(CMSampleBufferRefを参照)。CMSampleBufferは、Core Foundationスタイルの不透過型です。イ
ンスタンスにはビデオデータのフレームのサンプルバッファがCore Videoのピクセルバッファ
(CVPixelBufferRefを参照)として格納されます。サンプルバッファからピクセルバッファにアク
セスするには、次のようにCMSampleBufferGetImageBufferを使用します。
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(<#A CMSampleBuffer#>);
ピクセルバッファから実際のビデオデータにアクセスできます。具体例については、“CMSampleBuffer
からUIImageオブジェクトへの変換” (113 ページ)を参照してください。
ビデオデータに加えて、次のようなビデオフレームのほかの側面も取得できます。
●
●
●
タイミング情報:元の表示時間およびデコード時間の正確なタイムスタンプを取得するには、
CMSampleBufferGetPresentationTimeStampおよびCMSampleBufferGetDecodeTimeStampをそ
れぞれ使用します。
フォーマット情報:フォーマット情報は、CMFormatDescriptionオブジェクトにカプセル化されて
います(CMFormatDescriptionRefを参照)。このフォーマット記述から、たとえば、
CMVideoFormatDescriptionGetCodecTypeを使用してピクセルタイプを取得したり、
CMVideoFormatDescriptionGetDimensionsを使用してビデオの寸法を取得したりできます。
メタデータ:メタデータは、ディクショナリにアタッチメントとして格納されています。この
ディクショナリを取得するには、次のようにCMGetAttachmentを使用します。
CMSampleBufferRef sampleBuffer = <#Get a sample buffer#>;
CFDictionaryRef metadataDictionary =
CMGetAttachment(sampleBuffer, CFSTR("MetadataDictionary", NULL);
if (metadataDictionary) {
// Do something with the metadata.
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
112
時間およびメディアの表現
CMSampleBufferからUIImageオブジェクトへの変換
CMSampleBufferからUIImageオブジェクトへの変換
次のコードは、CMSampleBufferをUIImageオブジェクトに変換する方法を示しています。この関数
を使用する前に、要件を慎重に検討してください。この変換は比較的負荷の高い操作です。たとえ
ば、1秒程度の間隔で取得したビデオデータのフレームから静止画像を作成するのが適切です。この
関数を、キャプチャデバイスから入力されるすべてのフレームをリアルタイムで操作する手段として
使用しないでください。
// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
// Get a CMSampleBuffer's Core Video image buffer for the media data
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// Get the number of bytes per row for the pixel buffer
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// Get the number of bytes per row for the pixel buffer
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// Get the pixel buffer width and height
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// Create a device-dependent RGB color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Create a bitmap graphics context with the sample buffer data
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,
bytesPerRow, colorSpace, kCGBitmapByteOrder32Little |
kCGImageAlphaPremultipliedFirst);
// Create a Quartz image from the pixel data in the bitmap graphics context
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// Unlock the pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
113
時間およびメディアの表現
CMSampleBufferからUIImageオブジェクトへの変換
// Free up the context and color space
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
// Create an image object from the Quartz image
UIImage *image = [UIImage imageWithCGImage:quartzImage];
// Release the Quartz image
CGImageRelease(quartzImage);
return (image);
}
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
114
書類の改訂履歴
この表は「AVFoundationプログラミングガイド 」の改訂履歴です。
日付
メモ
2015-06-30
編成を若干変更しました。
2015-07-03
「AV Foundationについて」を更新して、iOSおよびOS XでのAV
Foundationスタックに名前を追加しました。カメラの例のリンクを
追加しました。
2014-11-18
「AV Foundationについて」を更新して、iOSおよびOS XでのAV
Foundationスタックに名前を追加しました。カメラの例のリンクを
追加しました。
2014-03-10
iOS 7.0およびOS X v10.9に合わせて改訂しました。
2013-10-22
CMSampleBufferGetImageBufferの使い方を明確にしました。
2013-08-08
AV Foundationフレームワークが提供する、編集用APIに関する章を
新たに追加しました。
2011-10-12
iOS 5用に更新し、リリースノートへの参照を含めました。
2011-04-28
OS X 10.7向けの初版。
2010-09-08
再生およびメタデータの章を拡充しました。
2010-08-16
メディアアセットの再生、検査、作成、編集、キャプチャ、変換を
行う下位レベルのフレームワークについて説明した文書の初版。
2015-06-30 | Copyright © 2015 Apple Inc. All Rights Reserved.
115
Apple Inc.
Copyright © 2015 Apple Inc.
All rights reserved.
本書の一部あるいは全部を Apple Inc. から書
面による事前の許諾を得ることなく複写複製
(コピー)することを禁じます。また、製品
に付属のソフトウェアは同梱のソフトウェア
使用許諾契約書に記載の条件のもとでお使い
ください。書類を個人で使用する場合に限り
1 台のコンピュータに保管すること、またそ
の書類にアップルの著作権表示が含まれる限
り、個人的な利用を目的に書類を複製するこ
とを認めます。
Apple ロゴは、米国その他の国で登録された
Apple Inc. の商標です。
キーボードから入力可能な Apple ロゴについ
ても、これを Apple Inc. からの書面による事
前の許諾なしに商業的な目的で使用すると、
連邦および州の商標法および不正競争防止法
違反となる場合があります。
本書に記載されているテクノロジーに関して
は、明示または黙示を問わず、使用を許諾し
ません。 本書に記載されているテクノロジー
に関するすべての知的財産権は、Apple Inc.
が保有しています。 本書は、Apple ブランド
のコンピュータ用のアプリケーション開発に
使用を限定します。
本書には正確な情報を記載するように努めま
した。 ただし、誤植や制作上の誤記がないこ
とを保証するものではありません。
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
U.S.A.
Apple Japan
〒106-6140 東京都港区六本木 6
丁目10番1号 六本木ヒルズ
http://www.apple.com/jp
Offline copy. Trademarks go here.
Apple Inc. は本書の内容を確認しておりますが、本
書に関して、明示的であるか黙示的であるかを問わ
ず、その品質、正確さ、市場性、または特定の目的
に対する適合性に関して何らかの保証または表明を
行うものではありません。その結果、本書は「現状
有姿のまま」提供され、本書の品質または正確さに
関連して発生するすべての損害は、購入者であるお
客様が負うものとします。
いかなる場合も、Apple Inc. は、本書の内容に含ま
れる瑕疵または不正確さによって生じる直接的、間
接的、特殊的、偶発的、または結果的損害に対する
賠償請求には一切応じません。そのような損害の可
能性があらかじめ指摘されている場合においても同
様です。
上記の損害に対する保証および救済は、口頭や書面
によるか、または明示的や黙示的であるかを問わ
ず、唯一のものであり、その他一切の保証にかわる
ものです。 Apple Inc. の販売店、代理店、または従
業員には、この保証に関する規定に何らかの変更、
拡張、または追加を加える権限は与えられていませ
ん。
一部の国や地域では、黙示あるいは偶発的または結
果的損害に対する賠償の免責または制限が認められ
ていないため、上記の制限や免責がお客様に適用さ
れない場合があります。 この保証はお客様に特定
の法的権利を与え、地域によってはその他の権利が
お客様に与えられる場合もあります。