差分

ナビゲーションに移動 検索に移動

Xamarin.Mac/動画から静止画を抽出する

5,066 バイト追加, 2015年10月1日 (木) 08:46
ページの作成:「'''Xamarin.Mac'''上で動画から静止画を抽出したいこともあると思う。 動画のサムネイルを作ったりするのに使える。 動画編集...」
'''Xamarin.Mac'''上で動画から静止画を抽出したいこともあると思う。

動画のサムネイルを作ったりするのに使える。

動画編集ソフトを実現するのにも使える。

本当は動画からアニメーションGIFを作りたかったが、まだ上手くいっていない。
これを見た人は大至急アニメGIFの作り方を書いてください。
*[[Xamarin.Mac/アニメーションGIFを生成する]]

需要を考えると[[サムネイル]]の生成が圧倒的多数を占めると思うので、CocoaのAPIに依存せず、[[PCL]]100%な[[ピュアマネージドコード]]で実装して[[ASP.NET]]で使えた方が遥かに嬉しいかもしれない。この手の実装は汎用的なはずなのに[[.NET Framework]]と[[Mono]]の両方で動くポータブルな実装って少ないよね。

==実装1:AVAssetImageGenerator.CopyCGImageAtTime==
実装の基本形となる方法だと思う。
AVAssetImageGeneratorというイカすクラスがあるようだ。

ただしCopyCGImageAtTimeメソッドはアホみたいに遅い。
変換頻度が高い場合は後述するGenerateCGImagesAsynchronouslyメソッドを使った方がよい。
<source lang="csharp">
// 動画を読み込む
var asset = new AVUrlAsset(videoFilePath, new AVUrlAssetOptions{} );

// 増分
var increment = asset.Duration.TimeScale / framerate;
// 終端
var framemax = asset.Duration.Value / increment;

// イメージジェネレーター
var generator = new AVAssetImageGenerator(asset);
generator.RequestedTimeToleranceBefore = CMTime.Zero;
generator.RequestedTimeToleranceAfter = CMTime.Zero;

// 動画のど真ん中の画像(CGImage)を取得する。
CMTime requestedTime = new CMTime(asset.Duration.Value / 2, asset.Duration.TimeScale);
CMTime actualTime;
NSError error;
var cgImage = generator.CopyCGImageAtTime(requestedTime, out actualTime, out error);
</source>

なお、上記のCGImageの保存はC#の拡張メソッドで独自に実装しているものであり、標準では存在しない。
このCGImageをファイルに保存する拡張メソッドの詳細は下記を参照。
*[[Xamarin.Mac/CGImageをファイルに保存する]]

==実装2: AVAssetImageGenerator.GenerateCGImagesAsynchronously==
GenerateCGImagesAsynchronouslyはかなり速い。
名称からもわかるように非同期で実行される。

今回はUIを持たないコマンドラインで実験したので非同期実行の終了を待たないと一瞬でアプリが終わってしまう。そのためC#では定番のManualResetEventを使っている。UIを持つアプリであれば処理を一時停止させる必要などないと思われ、インジケーター表示関連の処理に差し替えるなどすると捗ると思われる。

<source lang="csharp">
var framerate = 5f;

// 動画を読み込む
var asset = new AVUrlAsset(videoFilePath, new AVUrlAssetOptions{} );

// 増分
var increment = asset.Duration.TimeScale / framerate;
// 終端
var framemax = asset.Duration.Value / increment;

// イメージジェネレーター
var generator = new AVAssetImageGenerator(asset);
generator.RequestedTimeToleranceBefore = CMTime.Zero;
generator.RequestedTimeToleranceAfter = CMTime.Zero;

// 画像を取得する間隔
var requests = new List<NSValue>();
for (var count = 0f; count < asset.Duration.Value; count += increment)
{
var time = new CMTime((long)count, asset.Duration.TimeScale);
var val = NSValue.FromCMTime(time);
requests.Add(val);
}

// 待機用
var wait = new ManualResetEvent(false);
wait.Reset();

long framecur = 0;

// イメージを取得して保存
generator.GenerateCGImagesAsynchronously(
requests.ToArray(),
new AVAssetImageGeneratorCompletionHandler((requestedTime, imageRef, actualTime, result, error) => {
var cgImage = new CGImage(imageRef);

var savePath = string.Format("/tmp/img_{0:D8}.png", requestedTime.Value);
cgImage.Save(savePath, MobileCoreServices.UTType.PNG);

Console.WriteLine(requestedTime + "/" + actualTime);

framecur++;

if (framemax <= framecur)
{
// おわり
wait.Set();
}
})
);

// 終わるのを待つ
wait.WaitOne();
</source>

==関連項目==

==参考文献==
{{reflist}}

{{stub}}

[[category:Xamarin.Mac]]
匿名利用者

案内メニュー