Iphone 如何在视频中添加叠加文本,然后对其重新编码?

Iphone 如何在视频中添加叠加文本,然后对其重新编码?,iphone,objective-c,ios,ipad,video-editing,Iphone,Objective C,Ios,Ipad,Video Editing,我想编辑iOS应用程序中的视频。我想要一些文字的源视频语言字幕。然后我想保存覆盖该文本的视频。文字不仅仅是显示的目的。但当我打开编辑过的视频时,它会显示更新过的视频 这在iOS应用程序中可能吗?如果是,如何创建?一种方法是将文本覆盖创建为CoreAnimation CATextLayer,将其附加到AVAssetExportSession的视频合成,然后导出视频。生成的视频将在其上渲染叠加 这带来了一些好处: 你不必停留在CATextLayer——你可以构建包含CAGradientLayer、C

我想编辑iOS应用程序中的视频。我想要一些文字的源视频语言字幕。然后我想保存覆盖该文本的视频。文字不仅仅是显示的目的。但当我打开编辑过的视频时,它会显示更新过的视频


这在iOS应用程序中可能吗?如果是,如何创建?

一种方法是将文本覆盖创建为CoreAnimation CATextLayer,将其附加到AVAssetExportSession的视频合成,然后导出视频。生成的视频将在其上渲染叠加

这带来了一些好处:

  • 你不必停留在CATextLayer——你可以构建包含CAGradientLayer、CAShapeLayer等的CALayer树
  • 作为核心动画层,它们的许多属性都是可设置动画的,因此您可以在视频中免费获得平滑的iOS风格动画
  • 听起来不错,对吧?有一个小的副作用:根据您使用的导出预设,您的视频将不可避免地以恒定的帧速率重新编码-对我来说是30fps。为了保持文件的小尺寸,我故意通过省略冗余帧来降低帧速率,所以为了静态横幅,这对我来说是一个破坏者


    有一些名为AVEditDemo的苹果示例代码演示了此功能。有找到它的说明。

    使用Chaitali Jain代码,新视频将在没有音频的情况下保存。
    有没有人对这个问题有想法?谢谢

    不要要求我们在你的博客上回复你。这不是这个网站的工作方式。我已经删除了你问题的这一部分。我们可以在视频中添加动态文本吗?比如电影中演员的名字。非常有用。谢谢。@Chaitali Jain我在您的代码中遇到问题,例如由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:'***-[AVAssetExportSession setVideoComposition:]视频合成必须有一个积极的渲染“我们可以使用视频中不断变化的文本进行编辑吗?就像电影中演员的名字一个接一个出现一样。你可以使用此方法向电影添加字幕,但此方法会将文本烧录到视频帧上。你以后不能更改它们。当我在图像上写入文本,然后图像与视频合并时,我发现文本模糊。你能帮我吗?所以。。。如果CoreAnimation是一个破坏交易的人,你是如何获得“静态横幅”的?我将横幅渲染到YUV帧上,然后将其传递到编码器(AVAssetWriter)。我们是否可以在演员名字一个接一个出现的视频类电影中使用不断变化的文本进行编辑。@RhythmicFistman您能否建议我如何设置CATextlayer的精确帧查看我的问题@Imran您是否找到了在演员名字一个接一个出现的电影类电影中添加文本的解决方案?她没有添加音频曲目
    AVMutableCompositionTrack*compositionAudioTrack=[mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_无效]添加此项并保存音频
    
    - (void)addAnimation
    {       
        NSString *filePath = [[NSBundle mainBundle] pathForResource:videoName ofType:ext];
    
        AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:[NSURL fileURLWithPath:filePath]  options:nil];
    
        AVMutableComposition* mixComposition = [AVMutableComposition composition];
    
        AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    
        AVAssetTrack *clipVideoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    
        [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:clipVideoTrack atTime:kCMTimeZero error:nil];
    
        [compositionVideoTrack setPreferredTransform:[[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] preferredTransform]];
    
        CGSize videoSize = [clipVideoTrack naturalSize];
    
        UIImage *myImage = [UIImage imageNamed:@"29.png"];
        CALayer *aLayer = [CALayer layer];
        aLayer.contents = (id)myImage.CGImage;
        aLayer.frame = CGRectMake(videoSize.width - 65, videoSize.height - 75, 57, 57);
        aLayer.opacity = 0.65;
        CALayer *parentLayer = [CALayer layer];
        CALayer *videoLayer = [CALayer layer];
        parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
        videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
        [parentLayer addSublayer:videoLayer];
        [parentLayer addSublayer:aLayer];
    
        CATextLayer *titleLayer = [CATextLayer layer];
        titleLayer.string = @"Text goes here";
        titleLayer.font = CFBridgingRetain(@"Helvetica");
        titleLayer.fontSize = videoSize.height / 6;
        //?? titleLayer.shadowOpacity = 0.5;
        titleLayer.alignmentMode = kCAAlignmentCenter;
        titleLayer.bounds = CGRectMake(0, 0, videoSize.width, videoSize.height / 6); //You may need to adjust this for proper display
        [parentLayer addSublayer:titleLayer]; //ONLY IF WE ADDED TEXT
    
        AVMutableVideoComposition* videoComp = [AVMutableVideoComposition videoComposition];
        videoComp.renderSize = videoSize;
        videoComp.frameDuration = CMTimeMake(1, 30);
        videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
    
        AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [mixComposition duration]);
        AVAssetTrack *videoTrack = [[mixComposition tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
        AVMutableVideoCompositionLayerInstruction* layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
        instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];
        videoComp.instructions = [NSArray arrayWithObject: instruction];
    
        AVAssetExportSession *assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];//AVAssetExportPresetPassthrough
        assetExport.videoComposition = videoComp;
    
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString* VideoName = [NSString stringWithFormat:@"%@/mynewwatermarkedvideo.mp4",documentsDirectory];
    
    
        //NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:VideoName];
        NSURL *exportUrl = [NSURL fileURLWithPath:VideoName];
    
        if ([[NSFileManager defaultManager] fileExistsAtPath:VideoName])
        {
            [[NSFileManager defaultManager] removeItemAtPath:VideoName error:nil];
        }
    
        assetExport.outputFileType = AVFileTypeQuickTimeMovie;
        assetExport.outputURL = exportUrl;
        assetExport.shouldOptimizeForNetworkUse = YES;
    
        //[strRecordedFilename setString: exportPath];
    
        [assetExport exportAsynchronouslyWithCompletionHandler:
         ^(void ) {
             dispatch_async(dispatch_get_main_queue(), ^{
                 [self exportDidFinish:assetExport];
             });
         }
         ];
    }
    
    -(void)exportDidFinish:(AVAssetExportSession*)session
    {
        NSURL *exportUrl = session.outputURL;
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    
        if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:exportUrl])
        {
            [library writeVideoAtPathToSavedPhotosAlbum:exportUrl completionBlock:^(NSURL *assetURL, NSError *error)
             {
                 dispatch_async(dispatch_get_main_queue(), ^{
                     if (error) {
                         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Video Saving Failed"
                                                                        delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
                         [alert show];
                     } else {
                         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Video Saved" message:@"Saved To Photo Album"
                                                                        delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
                         [alert show];
                     }
                 });
             }];
    
        }
        NSLog(@"Completed");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"AlertView" message:@"Video is edited successfully." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
    }