iOSでストリーミング再生中の動画キャプチャを撮りたいが方法がない
やりたいこと
サーバー上にあるm3u8ファイルをiPhoneアプリでストリーミング再生してる。
ボタンを押すと今映ってる映像の写真を撮ってローカルに保存したい。
一見、以下のような処理で簡単に取得できそうである。
let rect = view.bounds UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0) let context: CGContextRef = UIGraphicsGetCurrentContext() view.layer.renderInContext(context) let capturedImage : UIImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext()
が、真っ黒な画像が出力されるだけ。
色々調べたけど
色々試したけどできないっぽいよという回答。
A) UIGetScreenImage() という非公開メソッドで撮れるけど、リジェクトの可能性もあるよ
B) AVPlayerLayerで再生して、AVAssetImageGenerator の copyCGImageAtTime で撮れるよ
-> ただしこれは、サーバー上の動画ファイルには使えないみたい
stackoverflow.com
ここに載ってる方法でやってみたけど、imageRefが取得できない。
「できた!」って言ってる人は多分ローカルの動画ファイルでやってる気がする。
var url = NSURL(string: Constants.streamUrls[0]) if var asset = AVAsset.assetWithURL(url) as? AVAsset { var imageGenerator:AVAssetImageGenerator = AVAssetImageGenerator(asset: asset) var time = CMTimeMake(1, 1) var imageRef = imageGenerator.copyCGImageAtTime(time, actualTime: nil, error: nil) var thumbnail = UIImage(CGImage: imageRef) return thumbnail! }
以下のようなエラーを吐いてしまう。
Optional(Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo=0x17046c140 {NSUnderlyingError=0x174243840 "The operation couldn’t be completed. (OSStatus error -12782.)", NSLocalizedFailureReason=An unknown error occurred (-12782), NSLocalizedDescription=The operation could not be completed})
stackoverflow.com こちらもうまくいかない。
AVAssetImageGeneratorを使っての取得も試みたが、ダメだった。
恐らくこちらも有効なのはローカルのビデオのみで、リモートの動画はだめくさい?
func captureFromPlayer(player: AVPlayer, maxSize: CGSize) -> UIImage? { var actualTime: CMTime = CMTimeMake(0, 0) var error: NSError? var generator: AVAssetImageGenerator = AVAssetImageGenerator(asset: player.currentItem.asset) generator.maximumSize = maxSize println("player.currentTime() :\(player.currentTime())") var cgIm: CGImageRef = generator.copyCGImageAtTime(player.currentTime(), actualTime: &actualTime, error: &error) var image = UIImage(CGImage: cgIm) if error != nil { println("erorr : \(error?.localizedDescription)") println("CMTimeGetSeconds : \(CMTimeGetSeconds(actualTime))") if let current = streamView.player?.currentTime() { CMTimeGetSeconds(current) } return nil } return image }
こっちもやってみたけど、そもそもtracksが空。
func setupReader() { let url = NSURL(string: urlString) let asset:AVURLAsset = AVURLAsset(URL: url, options: nil) asset.loadValuesAsynchronouslyForKeys(["tracks"], completionHandler: { dispatch_sync(dispatch_get_main_queue(), { () -> () in var videoTrack: AVAssetTrack? = nil var tracks = asset.tracksWithMediaType(AVMediaTypeVideo) if tracks.count == 1 { if let track = tracks[0] as? AVAssetTrack { var error: NSError? self.movieReader = AVAssetReader(asset: asset, error: &error) if error != nil { println("error: \(error)") } var key:String = kCVPixelBufferPixelFormatTypeKey as String var value:Int = kCVPixelFormatType_4444AYpCbCr16 as Int var videoSettings = [key: value] self.movieReader?.addOutput(AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoSettings)) self.movieReader?.startReading() } } }) }) }
iOS7から実装されたスナップショットを撮るメソッドを試す
こんなんあったんですね。
let capture: UIView = view.snapshotViewAfterScreenUpdates(true)
スクリーン自体のスナップショットを撮影するメソッドも、UIScreenに実装されている。
let capture = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(true)
だがこれも、UIViewをUIImageに変換するこちらのお決まりのメソッドに投げると、
AVPlayerLayerの所だけが真っ黒になって返ってくる。
func viewToImage(capturedView:UIView) -> UIImage { UIGraphicsBeginImageContextWithOptions(capturedView.bounds.size, capturedView.opaque, 0.0) capturedView.layer.renderInContext(UIGraphicsGetCurrentContext()) var img = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return img }
ちなみに、キャプチャのUIView自体は、ビデオの動画もバッチリ表示されている。
UIImageにする段階で、ビデオ動画がすっぽり抜け落ちる。
なんでや!なんでなんや!
うーむ。。
こんなのもあった
これもだめだった
func snapByOpenGL() -> UIImage { if let time = streamView.playerItem?.currentTime() { if var pixelBuffer: CVPixelBufferRef = streamView.output?.copyPixelBufferForItemTime(time, itemTimeForDisplay: nil) { var ciImage = CIImage(CVPixelBuffer: pixelBuffer) var temporaryContext: CIContext = CIContext(options: nil) var rect = CGRectMake(0, 0, CGFloat(CVPixelBufferGetWidth(pixelBuffer)), CGFloat(CVPixelBufferGetHeight(pixelBuffer))) var videoImage: CGImageRef = temporaryContext.createCGImage(ciImage, fromRect: rect) var image = UIImage(CGImage: videoImage) return image! } } return UIImage() }