【问题标题】:iOS modify video frame by frame on iPhone (to add CC)iOS在iPhone上逐帧修改视频(添加CC)
【发布时间】:2013-03-18 15:04:02
【问题描述】:

我有兴趣在 iPhone 上的现有视频中添加文本(以隐藏式字幕样式)。 stackoverflow 上的一些解决方案建议添加一个透明的 UIView 来覆盖视频。这一切都很好,但是,我无法将其保存为“新的和修改过的视频”。

我看到的唯一解决方案是获取文本,从视频中获取单个帧,将文本添加到帧中,然后将带有文本的修改帧推回视频中,替换视频中的原始帧。

有谁知道如何从视频中取出一帧(我想我可以弄清楚添加文本),然后如何将帧推回视频中?如果您有一些想法,或者知道教程,我将不胜感激..

【问题讨论】:

  • 你浏览过 AVFoundation 框架文档吗?
  • 为什么要将它保存为修改后的视频,只要每次播放时它的顶部都有相同的透明视图?编辑视频占用大量 CPU 资源,因此会消耗移动设备的电池电量。
  • iPhone 已经具备视频编辑功能,以及适用于 iPhone 的完整 iMovie 套件,所以我看不出在帧中添加文本是多么费力......
  • 我没有说这更费力,我质疑你的应用为什么需要这样做。已经可用的视频编辑软件旨在满足大量编辑需求,但主要用于小序列编辑、剪切、segues 等。您正在寻求系统地更改视频的每一帧,而您的原始问题似乎不需要,因此我要求澄清,您提供的信息越多,其他人可以得到的帮助越多,如果您不想要别人的帮助,我建议不要问。
  • 我也在寻找相同的。但是得到任何好处

标签: iphone ios video frame


【解决方案1】:

您无需逐帧执行此操作。 AVFoundation 从 iOS 4.0 开始支持字幕。

例如,您可以创建一个AVMutableComposition,然后在您的视频顶部添加一个字幕轨道。 AVMediaTypeSubtitle 是字幕的类型(或隐藏字幕的 AVMediaTypeClosedCaption)。然后,您可以将合成提供给玩家或AVAssetWriter。为您省去所有麻烦。

【讨论】:

  • 我对您的解决方案很感兴趣,但我没有找到如何使用“AVMediaTypeSubtitle”设置“字幕”的文本。你能举个例子吗?谢谢!
【解决方案2】:

根据你说的,这里有一个例子你可以学习一下。

如上图所示,您可以为视频添加边框、叠加层、字幕等任何您想要的内容。

http://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos

【讨论】:

    【解决方案3】:

    您可以通过 AvFoundation 框架编辑视频...

    这是一个例子..

    var assetTrack = AVAsset.assetWithURL(filePath1) as! AVURL资产 var mutableComposition = AVMutableComposition()

        // Step - 1  pass url to  avasset
    
        // video
    
        var compositionVideoTrack = AVMutableCompositionTrack()
        compositionVideoTrack = mutableComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
    
    
        var assetVideoTrack = assetTrack.tracksWithMediaType(AVMediaTypeVideo)[0] as! AVAssetTrack
        compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero , assetTrack.duration), ofTrack: assetVideoTrack, atTime: kCMTimeZero, error: nil)
    
        // audio
    
        var compositionAudioTrack = AVMutableCompositionTrack()
        compositionAudioTrack = mutableComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
    
        var assetAudioTrack = assetTrack.tracksWithMediaType(AVMediaTypeAudio)[0] as! AVAssetTrack
        compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero , assetTrack.duration), ofTrack: assetAudioTrack, atTime: kCMTimeZero, error: nil)
    
        var videoAssetOrientation_: UIImageOrientation = .Up
        var isVideoAssetPortrait_: Bool = false
    
        var videoTransform: CGAffineTransform = assetTrack.preferredTransform
    
    
    
        var videosize = CGSize()
        videosize = assetVideoTrack.naturalSize
    
        var parentLayer = CALayer()
        var videoLayer = CALayer()
        var textLayer = CALayer()
    
        parentLayer.frame = CGRectMake(0, 0, videosize.width, videosize.height)
        videoLayer.frame = CGRectMake(0, 0, videosize.width, videosize.height)
        textLayer.frame = CGRectMake(0, 0, videosize.width, videosize.height)
    
        parentLayer.addSublayer(videoLayer)
    
        if drawingView.image != nil
        {
            var drawingLayer = CALayer()
            drawingLayer.frame = CGRectMake(0, 0, videosize.width, videosize.height)
            drawingLayer.contents = drawingView.image.CGImage
    
            var image = UIImage()
            parentLayer.addSublayer(drawingLayer)
    
        }
    
    
        var textV = UIView()
        textV.backgroundColor = UIColor.clearColor()
        textV.layer.backgroundColor = UIColor.clearColor().CGColor
        textV.frame = CGRectMake(self.captureView.frame.size.width, 0, self.captureView.frame.size.width, self.captureView.frame.size.height)
    
        var textL = UILabel()
        textL = textShowOnPreview
        textV.addSubview(textL)
    
        if textL != ""
        {
            UIGraphicsBeginImageContext(textV.bounds.size)
            textV.layer.renderInContext(UIGraphicsGetCurrentContext())
            var image1: UIImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            if ( TextAnimation == "")
            {
                textLayer.contents = image1.CGImage
                parentLayer.addSublayer(textLayer)
            }
    
            else if  ( TextAnimation == "flip")
            {
    
                var overlayer1 = CALayer()
                overlayer1.backgroundColor = UIColor.clearColor().CGColor
                let screenSize: CGRect = UIScreen.mainScreen().bounds
                overlayer1.contents = image1.CGImage
                overlayer1.masksToBounds = true
                overlayer1.frame = CGRectMake(videosize.width/2-300, videosize.height/2 - 400, videosize.width,videosize.width);
                var   animation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation")
                animation.duration=5.0;
                animation.repeatCount=5;
                animation.autoreverses = true;
                // rotate from 0 to 360
                animation.fromValue = 0
                animation.toValue = (2.0 * M_PI);
                animation.beginTime = AVCoreAnimationBeginTimeAtZero;
                overlayer1.addAnimation(animation, forKey:"rotation")
                [parentLayer .addSublayer(overlayer1)]
    
            }
            else if ( TextAnimation == "fade")
            {
    
                           //opacity
                var overlayer1 = CALayer()
                overlayer1.backgroundColor = UIColor.clearColor().CGColor
                overlayer1.contents = image1.CGImage
                overlayer1.masksToBounds = true
    
                overlayer1.frame = CGRectMake(videosize.width/2 - 300,  videosize.height/2 - 100 , videosize.width+20, videosize.width);
                var   animation : CABasicAnimation = CABasicAnimation(keyPath: "transform.scale")
                animation.duration = 2.0;
                animation.repeatCount = 3;
                animation.autoreverses = true;
                // rotate from 0 to 360
    
                animation.fromValue = 0.5;
                animation.toValue = 1.0;
                animation.beginTime = AVCoreAnimationBeginTimeAtZero;
    
                overlayer1.addAnimation(animation, forKey:"scale")
                [parentLayer .addSublayer(overlayer1)]
    
            }
            else if ( TextAnimation == "bounce")
            {
                var overlayer1 = CALayer()
    
                var bounce : CABasicAnimation = CABasicAnimation (keyPath:"position.y");
                overlayer1.backgroundColor = UIColor.clearColor().CGColor
                overlayer1.contents = image1.CGImage
                overlayer1.masksToBounds = true
                overlayer1.frame = CGRectMake(videosize.width/2 - 300,  videosize.height/2 - 100 , videosize.width, videosize.width);
                bounce.duration = 1.0;
                bounce.fromValue =  overlayer1.frame.origin.y
    
                bounce.toValue =  overlayer1.frame.origin.y - 100
                bounce.repeatCount = 10
                bounce.autoreverses = true;
                overlayer1.addAnimation(bounce, forKey: "y")
    
                var animation = CABasicAnimation(keyPath: "transform.scale")
                animation.toValue = NSNumber(float: 0.9)
                animation.duration = 1.0
                animation.repeatCount = 10;
                animation.autoreverses = true
                overlayer1.addAnimation(animation, forKey: nil)
                 [parentLayer .addSublayer(overlayer1)]
    
            }
    
        }
    
        var mutableVideoComposition = AVMutableVideoComposition()
        mutableVideoComposition.frameDuration = CMTimeMake(1, 30)
        mutableVideoComposition.renderSize = videosize
    
        mutableVideoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)
    
    
        var passThroughInstruction = AVMutableVideoCompositionInstruction()
        passThroughInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, mutableComposition.duration)
    
    
        var passThroughLayerInstruction = AVMutableVideoCompositionLayerInstruction()
    
    
        // video
    
        var assestVideoMutableCompositionVideo = mutableComposition.tracksWithMediaType(AVMediaTypeVideo)[0] as! AVAssetTrack
        passThroughLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: assestVideoMutableCompositionVideo)
    
    
    
        if isVideoAssetPortrait_ == false
        {
    
            var FirstAssetScaleFactor: CGAffineTransform = CGAffineTransformMakeScale(1, 1)
    
            passThroughLayerInstruction.setTransform(CGAffineTransformConcat(assetVideoTrack.preferredTransform, FirstAssetScaleFactor), atTime: kCMTimeZero)
    
        }
    
    
        passThroughInstruction.layerInstructions = [passThroughLayerInstruction]
        mutableVideoComposition.instructions = [passThroughInstruction]
    
        let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as! NSURL
        let filePath = documentsURL.URLByAppendingPathComponent("NewWatermarkedVideo.mov") as NSURL
    
    
        var fileManager:NSFileManager = NSFileManager.defaultManager()
        fileManager.removeItemAtURL(filePath, error: nil)
    
       var exporter: AVAssetExportSession = AVAssetExportSession(asset: mutableComposition, presetName: AVAssetExportPresetMediumQuality)
        exporter.videoComposition = mutableVideoComposition
        exporter.outputFileType = AVFileTypeQuickTimeMovie
        exporter.outputURL = filePath
        exporter.shouldOptimizeForNetworkUse = true
        self.captureView.addSubview(textShowOnPreview)
    
        exporter.exportAsynchronouslyWithCompletionHandler({() -> Void in
    
            println(exporter.status)
    
            if  exporter.status == AVAssetExportSessionStatus.Completed
            {
    
    
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
    
    
    
    
                    MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
    
                    self.topicSelectedImage.highlighted = false
                    self.timelineSelectedImage.highlighted = false
                    self.selectCat = ""
                    self.postView.hidden = false
    
                })
    
                println("Completed")
                self.mediaData =  NSData(contentsOfURL:filePath, options: nil, error: nil)!
    
    
                var err: NSError? = nil
                var asset = AVURLAsset(URL: filePath, options: nil)
                var imgGenerator = AVAssetImageGenerator(asset: asset)
                var cgImage = imgGenerator.copyCGImageAtTime(CMTimeMake(0, 30), actualTime: nil, error: &err)
                var uiImage = UIImage(CGImage: cgImage)!
                self.videoThumbData = UIImageJPEGRepresentation(uiImage, 0.1)
    
                var assetTrack = AVAsset.assetWithURL(filePath) as! AVURLAsset
                self.videoTime = Int(CMTimeGetSeconds(assetTrack.duration)) + 3
    
                println(self.videoTime)
    
    
            }
            else if exporter.status == AVAssetExportSessionStatus.Cancelled
            {
    
    
            }
            else if exporter.status == AVAssetExportSessionStatus.Failed
            {
    
    
            }
        })
    

    【讨论】:

      【解决方案4】:

      如果您确实想逐帧编辑影片,请使用AVReaderWriter。虽然它是一个 OS X Apple 示例代码,但 AVFoundation 在两个平台上都可用,几乎没有什么变化。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-16
        • 1970-01-01
        • 2012-05-29
        • 2011-01-13
        相关资源
        最近更新 更多