美文网首页
给你的视频打上个人标签

给你的视频打上个人标签

作者: 小唐朝 | 来源:发表于2016-09-25 14:21 被阅读524次

给你的视频打上个人标签

编辑前 编辑后

话不多说,先上代码:

CommonEditVideo.h

//
//  CommonEditVideo.h
//  MediumEdit
//
//  Created by Input on 2016/9/23.
//  Copyright © 2016年 Input. All rights reserved.
//
/**
 * 使用时请在info.plist文件加入key: NSPhotoLibraryUsageDescription value: 打开相册
 * 水印格式: 自定义字符 + 时间 + 机型
 *
 */

#import <UIKit/UIColor.h>
#import <AVFoundation/AVFoundation.h>

@protocol  CommonEditVideoDelegate <NSObject>

@required

//导出到文件结束, 暂时没有加状态处理
- (void)didExport:(nullable NSURL *) url error:(nullable NSError *) error;

//返回是否导出到文件, 不希望文件加水印只是临时添加请返回NO
- (BOOL)willExport:(nullable AVAssetExportSession *) exporter;

//开始编辑
- (void)willEdit;

@end

/**
 * 水印位置
 */

typedef enum : NSUInteger {
    AlignmentUp,        //居上
    AlignmentCenter,    //居中
    AlignmentDown,      //居下
} Alignment;


@interface CommonEditVideo: NSObject

@property (nonatomic, nullable, weak)   id<CommonEditVideoDelegate>     delegate;           //视频编辑相关代理

@property (nonatomic, nullable, strong) NSString                        *watermarkTitle;    //水印内容
@property (nonatomic, nullable, strong) NSURL                           *url;               //图片输出位置(默认为相册)

@property Alignment     watermarkPlace;     //水印位置
@property BOOL          isShowTime;         //是否显示时间(YES)
@property BOOL          isShowModel;        //是否显示拍摄机型(YES)


- (nullable instancetype)init;
- (void)startEditVideo:(nonnull NSURL *) assetURL;
- (void)videoOutput;

@end

CommonEditVideo.m

//
//  CommonEditVideo.m
//  MediumEdit
//
//  Created by Input on 2016/9/23.
//  Copyright © 2016年 Input. All rights reserved.
//

#import "CommonEditVideo.h"
#import <MobileCoreServices/UTCoreTypes.h>
#import <AssetsLibrary/ALAssetsLibrary.h>

@interface CommonEditVideo ()

@property (nonatomic, nullable, strong) AVAssetExportSession            *exporter;          //视频导出
@property (nonatomic, nullable, strong) AVAsset                         *videoAsset;        //视频资源

@end

@implementation CommonEditVideo

- (nullable instancetype)init{
    self = [super init];
    if (self){
        self.isShowTime     = YES;
        self.isShowModel    = YES;
        self.watermarkPlace = AlignmentCenter;
    }
    return self;
}

- (void)startEditVideo:(nonnull NSURL *) assetURL{
    self.videoAsset = [[AVURLAsset alloc] initWithURL: assetURL options: nil];
    
    if (self.delegate){
        [self.delegate willEdit];
    }
    
    //初始化及构造新视频
    [self exporterInitialize];
}

//导出结果回调
- (void)exportDidFinish{
    
    if (self.exporter.status == AVAssetExportSessionStatusCompleted) {
        NSURL *outputURL = self.exporter.outputURL;
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL]) {
            
            [library writeVideoAtPathToSavedPhotosAlbum:outputURL completionBlock:^(NSURL *assetURL, NSError *error){
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    if (self.delegate){
                        [self.delegate didExport: self.exporter.outputURL error: error];
                    }
                    
                });
            }];
        }
    }
   
}

- (void)editWatermarkTitle{
    //水印相关设定
    NSString *modelStr = nil;
    
    for (int i = 0;  i < self.videoAsset.commonMetadata.count; i++ ){
        AVMetadataItem *data = self.videoAsset.commonMetadata[i];
        
//        NSLog(@"%@",data);
        
        if ([data.commonKey isEqual: @"creationDate"]){
            if (self.isShowTime){
                NSDateFormatter *timeFormatter = [NSDateFormatter new];
                timeFormatter.dateFormat = @"' ' yyyy-MM-dd/HH:mm";
                self.watermarkTitle = [self.watermarkTitle stringByAppendingString:[timeFormatter stringFromDate:data.dateValue]];
            }
        };
        
        if ([data.commonKey isEqual: @"model"]){
            if (self.isShowModel){
                modelStr =  @" ";
                modelStr = [modelStr stringByAppendingString: data.stringValue];
            }
        };
        
    };
    
    self.watermarkTitle = [self.watermarkTitle stringByAppendingString:modelStr];

}

- (void)applyVideoEffectsToComposition:(nonnull AVMutableVideoComposition *)composition size:(CGSize)size{
    
    
    [self editWatermarkTitle];
    
    CATextLayer *subtitle1Text = [[CATextLayer alloc] init];
    
    [subtitle1Text setFontSize:36];
    
    subtitle1Text.frame = [self CGRectFromWatermarkPlaceAndVideoSzie:size];
    [subtitle1Text setString:self.watermarkTitle];
    [subtitle1Text setAlignmentMode:kCAAlignmentCenter];
    [subtitle1Text setForegroundColor:[[UIColor redColor] CGColor]];
    
    CALayer *overlayLayer = [CALayer layer];
    [overlayLayer addSublayer:subtitle1Text];
    
    overlayLayer.frame = CGRectMake(0, 0, size.width, size.height);
    [overlayLayer setMasksToBounds:YES];
    
    CALayer *parentLayer = [CALayer layer];
    CALayer *videoLayer = [CALayer layer];
    parentLayer.frame = CGRectMake(0, 0, size.width, size.height);
    videoLayer.frame = CGRectMake(0, 0, size.width, size.height);
    [parentLayer addSublayer:videoLayer];
    [parentLayer addSublayer:overlayLayer];
    
    composition.animationTool = [AVVideoCompositionCoreAnimationTool
                                 videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];

}

//返回水印位置
- (CGRect)CGRectFromWatermarkPlaceAndVideoSzie:(CGSize)size{
    
    switch (self.watermarkPlace) {
        case AlignmentUp:
            return CGRectMake(0, 0, size.width, size.height);
        case AlignmentDown:
            return CGRectMake(0, 0, size.width, 80);
        default:
            return CGRectMake(0, 0, size.width, size.height / 2);
    }
}

- (void)exporterInitialize{

    AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
    
    AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                        preferredTrackID:kCMPersistentTrackID_Invalid];
    [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.videoAsset.duration)
                        ofTrack:[[self.videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
                         atTime:kCMTimeZero error:nil];
    AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
                                                                        preferredTrackID:kCMPersistentTrackID_Invalid];
    [audioTrack insertTimeRange: CMTimeRangeMake(kCMTimeZero, self.videoAsset.duration)
                        ofTrack:[[self.videoAsset tracksWithMediaType:AVMediaTypeAudio]
                                 objectAtIndex:0]
                         atTime:kCMTimeZero error:nil];
    
    self.exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
                                                 presetName:AVAssetExportPresetHighestQuality];
    AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, self.videoAsset.duration);
    
    AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
    AVAssetTrack *videoAssetTrack = [[self.videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    
    mainInstruction.layerInstructions = [NSArray arrayWithObjects:videolayerInstruction,nil];
    AVMutableVideoComposition *mainCompositionInst = [AVMutableVideoComposition videoComposition];
    CGSize naturalSize;
    
    naturalSize = videoAssetTrack.naturalSize;
    
    float renderWidth, renderHeight;
    renderWidth = naturalSize.width;
    renderHeight = naturalSize.height;
    mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight);
    mainCompositionInst.instructions = [NSArray arrayWithObject:mainInstruction];
    mainCompositionInst.frameDuration = CMTimeMake(1, 30);
    
    [self applyVideoEffectsToComposition:mainCompositionInst size:naturalSize];
    
    self.exporter.videoComposition = mainCompositionInst;
    self.exporter.outputFileType = AVFileTypeQuickTimeMovie;
    self.exporter.shouldOptimizeForNetworkUse = YES;
    self.exporter.metadata = self.videoAsset.metadata;
    
}

- (void)videoOutput{
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *myPathDocs =  [documentsDirectory stringByAppendingPathComponent:
                            [NSString stringWithFormat:@"(Input)FinalVideo-%d.mov",arc4random() % 1000]];
    NSURL *url = [NSURL fileURLWithPath:myPathDocs];
    
    BOOL flag;
    
    if (self.delegate){
        flag = [self.delegate willExport:self.exporter];
    }else{
        flag = YES;
    }
    
    //视频输出url设置
    if (!self.url){
        self.exporter.outputURL = url;
    }else{
        self.exporter.outputURL = self.url;
    }
    //导出
    if (flag){
        [self.exporter exportAsynchronouslyWithCompletionHandler:^{
            
            dispatch_async(dispatch_get_main_queue(), ^{
                [self exportDidFinish];
            });
        }];
    }
}

@end

解说

​ 首先创建一个 CommonEditVideo对象,使用这个对像得实现CommonEditVideoDelegate协议;

CommonEditVideoDelegate协议有三个方法,分别在开始编辑视频及保存到文件前后调用,其中实现willExport返回一个真假值,如果是想在放器界面添加水印在这里返回一个NO即可,参数就是已经处理好的AVAssetExportSession,这个时候你可以直接拿AVAssetExportSession对象的asset播放了;如果是要将视频永久保存则返回一个YES,当视频导出到文件结束将会调用didExport,这个函数会给你一个导出状态及地址,默认保存到相册,你也可以设置CommonEditVideo对象的url值。

​ 水印可以放在三个位置:上中下;默认居中。可以设置是否显示时间及拍摄机型。自定义字符放在水印最前端。

下面的代码就是我测试用的视图控制器的实现部分

ViewController.m

//
//  ViewController.m
//  MediumEdit
//
//  Created by Input on 2016/9/23.
//  Copyright © 2016年 Input. All rights reserved.
//

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <ImageIO/ImageIO.h>
#import <MediaPlayer/MPMoviePlayerViewController.h>
#import <MobileCoreServices/UTCoreTypes.h>


@interface ViewController ()

@end

@implementation ViewController 

- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *btn = [UIButton buttonWithType: UIButtonTypeSystem];
    
    btn.bounds = CGRectMake(0, 0, 100, 40);
    btn.center = self.view.center;
    btn.backgroundColor = [UIColor cyanColor];
    btn.layer.cornerRadius = 5;
    [btn addTarget:self action:@selector(didBtn:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}


- (void)didBtn:(UIButton *)sender{
    
    UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
    mediaUI.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    mediaUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];
    
    mediaUI.allowsEditing = YES;
    mediaUI.delegate = self;
    
    [self presentViewController:mediaUI animated:YES completion:nil];
}

#pragma mark - UIImagePickerControllerDelegate

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    
    [self dismissViewControllerAnimated:YES completion:nil];
        
    CommonEditVideo *videoEdit = [[CommonEditVideo alloc]init];
    videoEdit.delegate = self;
    videoEdit.watermarkTitle = @"input";
    videoEdit.watermarkPlace = AlignmentDown;

    //开始编辑
    [videoEdit startEditVideo: [info objectForKey:UIImagePickerControllerMediaURL]];
    
    //输出视频
    [videoEdit videoOutput];

}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - CommonEditVideoDelegate

- (void)willEdit{
    NSLog(@"%@", @"开始编辑");
}
- (BOOL)willExport:(AVAssetExportSession *)exporter{
    NSLog(@"%@", @"编辑完成\n开始导出到文件");

//    NSLog(@"%lld",exporter.asset.duration.value);
    return YES;
}
- (void)didExport:(NSURL *)url error:(NSError *)error{
    
    if (!error){
        NSLog(@"%@", url);
    
        MPMoviePlayerViewController *playerCtr = [[MPMoviePlayerViewController alloc]initWithContentURL:url];
        [self presentViewController:playerCtr animated:YES completion:nil];
    }
}

@end

测试时间不长,也只试过系统自带的相机拍摄的视频处理,如果使用途中遇到BUG请留言,大家一起探讨。

相关文章

  • 给你的视频打上个人标签

    给你的视频打上个人标签 话不多说,先上代码: CommonEditVideo.h CommonEditVideo....

  • 给自己的抖音帐号,打上标签的方法

    给自己的抖音帐号打上标签,为什么要给自己的抖音帐号打上标签呢?因为一旦你的帐号打上了标签,抖音平台给你推荐的人流就...

  • 给你的ABAP对象打上标签(Tag)

    标签(Tag)几乎是信息管理软件的一个必备功能,目的是帮助用户更迅速地检索出自己需要的数据,以及对海量数据进行更有...

  • 不要过早给你的人生打上标签

    闺密秀儿打来电话,说这段时间被她女儿小雨气死了。小雨14岁,一直是个品学兼优的孩子,成绩从小学一年级就一直是班里的...

  • 直播基础知识分享--入门篇

    什么是视频直播? 直播就是将每一帧数据(Vide/Audio/Data Frame),打上时序标签(Timesta...

  • 打上失败的标签以后

    早上打开手机一看,日更宣布挑战失败!意料之中的事。每天日更的时间在晚上十点之后。昨天晚上跟孩子因为不早点睡的问题争...

  • 你所不知道的“标签”

    现在是一个个人标签泛滥的时代。有的人为了品牌效应会给自己打上个人标签,比如蔡康永“高情商”的标签。当然我们也热衷于...

  • 我亲爱的偏执狂

    01旅行的意义 我们经常喜欢在社交软件上给自己打上爱好旅行这样的标签,可是,我们为什么要给自己打上如此标签?有多少...

  • 给语音打上标签

  • 家中哪些地方可以使用香薰蜡烛?香味怎么选?

    家是个私密空间,而室内香氛则是一个只为取悦自己的存在。鼻息间的香气润物于无声。如果说香水于一个人是外界给你打上标签...

网友评论

      本文标题:给你的视频打上个人标签

      本文链接:https://www.haomeiwen.com/subject/ukjfyttx.html