给你的视频打上个人标签
编辑前 编辑后话不多说,先上代码:
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请留言,大家一起探讨。
网友评论