Skip to content

Commit

Permalink
Support Gif WaterMark
Browse files Browse the repository at this point in the history
  • Loading branch information
黄锐灏 committed Jan 23, 2019
1 parent 73270dc commit 664a9bd
Show file tree
Hide file tree
Showing 15 changed files with 283 additions and 64 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Apache License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# WAVideoBox
秒级! 三行代码实现iOS视频压缩、变速、混音、合并、水印、旋转、换音、裁剪 ! 支持不同分辩率,支持你能想到的各种混合操作!
秒级! 三行代码实现iOS视频压缩、变速、混音、合并、GIF水印、旋转、换音、裁剪 ! 支持不同分辩率,支持你能想到的各种混合操作!

======================

Expand Down
4 changes: 4 additions & 0 deletions WAVideoBox.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
3B222FF921E863DC0056CE83 /* WAAVSEExtractSoundCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B222FF821E863DC0056CE83 /* WAAVSEExtractSoundCommand.m */; };
3B791E4521F8A43E00D58F16 /* gifTest.gif in Resources */ = {isa = PBXBuildFile; fileRef = 3B791E4421F8A43E00D58F16 /* gifTest.gif */; };
3B9027CD21E1F2E7001B1497 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B9027CC21E1F2E7001B1497 /* AppDelegate.m */; };
3B9027D021E1F2E7001B1497 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B9027CF21E1F2E7001B1497 /* ViewController.m */; };
3B9027D321E1F2E7001B1497 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3B9027D121E1F2E7001B1497 /* Main.storyboard */; };
Expand Down Expand Up @@ -55,6 +56,7 @@
/* Begin PBXFileReference section */
3B222FF721E863DC0056CE83 /* WAAVSEExtractSoundCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WAAVSEExtractSoundCommand.h; sourceTree = "<group>"; };
3B222FF821E863DC0056CE83 /* WAAVSEExtractSoundCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WAAVSEExtractSoundCommand.m; sourceTree = "<group>"; };
3B791E4421F8A43E00D58F16 /* gifTest.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = gifTest.gif; sourceTree = "<group>"; };
3B9027C821E1F2E7001B1497 /* WAVideoBox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WAVideoBox.app; sourceTree = BUILT_PRODUCTS_DIR; };
3B9027CB21E1F2E7001B1497 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
3B9027CC21E1F2E7001B1497 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -230,6 +232,7 @@
3B90282E21E1FF16001B1497 /* Sources */ = {
isa = PBXGroup;
children = (
3B791E4421F8A43E00D58F16 /* gifTest.gif */,
3BD923A921E44D8F007D85B8 /* test1.mp4 */,
3BCAF55521E72B8000663097 /* test2.mp4 */,
3B90282F21E1FF21001B1497 /* test3.mp4 */,
Expand Down Expand Up @@ -346,6 +349,7 @@
3B9027D321E1F2E7001B1497 /* Main.storyboard in Resources */,
3B90283421E1FF22001B1497 /* test3.mp4 in Resources */,
3BD923AA21E44D8F007D85B8 /* test1.mp4 in Resources */,
3B791E4521F8A43E00D58F16 /* gifTest.gif in Resources */,
3B90283821E1FF22001B1497 /* nature.mp4 in Resources */,
3BCAF55621E72B8000663097 /* test2.mp4 in Resources */,
);
Expand Down
Binary file not shown.
Binary file added WAVideoBox/Sources/gifTest.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions WAVideoBox/ViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ - (IBAction)addWaterMark:(id)sender {
__weak typeof(self) wself = self;

[_videoBox appendVideoByPath:_videoPath];
[_videoBox appendWaterMark:[UIImage imageNamed:@"waterMark"] relativeRect:CGRectMake(0.7, 0.2, 0.2, 0)];
[_videoBox appendImages:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"gifTest" ofType:@"gif"]] relativeRect:CGRectMake(0.6, 0.2, 0.3, 0)];

[_videoBox asyncFinishEditByFilePath:filePath complete:^(NSError *error) {
if (!error) {
Expand Down Expand Up @@ -206,7 +206,6 @@ - (IBAction)composeEdit:(id)sender {
}
}];


}


Expand Down
144 changes: 144 additions & 0 deletions WAVideoBox/WAVideoBox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# WAVideoBox

https://github.com/CoderHenry66/WAVideoBox

秒级! 三行代码实现iOS视频压缩、变速、混音、合并、水印、旋转、换音、裁剪 ! 支持不同分辩率,支持你能想到的各种混合操作!

======================

WAVideoBox是一款基于AVFoundation视频操作框架,用短短几行代码就可完成各种简单及至复杂的视频操作命令。使用简单,性能高超~

尤其是不同分辩率视频的组合操作,如,给A视频变速,给B视频加水印,把C视频旋转...把ABC..视频合并,再操作合并视频...循环...
用WAVideoBox能快速高效实现上述功能。

PS :支持多线程处理

iOS 8.0 ++

使用指导
=====================

常规操作: append一个视频 + 操作命令 + finish

组合操作:将所有视频append,操作命令 * n,finish

骚操作:参照文尾

下列代码均跑于6s 12.0系统

常规操作: 三行代码
=====================

// 压缩:将19秒的视频进行压缩, 耗时<1秒, 成果 : 6.7M -> 335KB (有损压缩,高清压缩可设置ratio videoQuality)

// 第一种:自动压缩,分low ,medium , high 三档
[_videoBox appendVideoByPath:_videoPath];
_videoBox.ratio = WAVideoExportRatioLowQuality;//有损压缩
[_videoBox asyncFinishEditByFilePath:filePath complete:^(NSError *error) {
// do it
}];

// 第二种:通过自行设定分辩率,实现高清压缩
[_videoBox appendVideoByPath:_videoPath];
_videoBox.ratio = WAVideoExportRatio640x480;
// _videoBox.vidoQuality = 6;还可以通过设置videoQuality进行精准压缩
[_videoBox asyncFinishEditByFilePath:filePath complete:^(NSError *error) {
// do it
}];

![压缩](http://g.recordit.co/FLVh4VqcrI.gif)

// 拼接:将两个不同分辨率视频拼接(17秒的视频), 耗时<3秒 ,如果是相同分辩率的视频耗时<1秒

[_videoBox appendVideoByPath:_testThreePath];
[_videoBox appendVideoByPath:_testTwoPath];
[_videoBox asyncFinishEditByFilePath:filePath complete:^(NSError *error) {
// do it
}];

// 混音:给视频混上其他视频/音乐的声音 (19秒视频), 耗时 < 1秒

[_videoBox appendVideoByPath:_videoPath];
[_videoBox dubbedSoundBySoundPath:_testThreePath];
[_videoBox asyncFinishEditByFilePath:filePath complete:^(NSError *error) {
// do it
}];

// 旋转、裁剪、换音、变速、水印....更多操作见demo

组合操作
=====================

// 将1号拼接到video,用2号的音频替换,给视频加一个水印,旋转180度,混上3号的音,速度加快两倍
// 把生好的视频裁6-12秒,压缩
// 耗时 < 2秒

[_videoBox appendVideoByPath:_videoPath];
[_videoBox appendVideoByPath:_testThreePath];
[_videoBox replaceSoundBySoundPath:_testTwoPath];
[_videoBox appendWaterMark:[UIImage imageNamed:@"waterMark"] relativeRect:CGRectMake(0.7, 0.7, 0.2, 0.1)];

[_videoBox rotateVideoByDegress:180];
[_videoBox dubbedSoundBySoundPath:_testOnePath];
[_videoBox gearBoxWithScale:2];

[_videoBox rangeVideoByTimeRange:CMTimeRangeMake(CMTimeMake(2400, 600), CMTimeMake(3600, 600))];

[_videoBox asyncFinishEditByFilePath:filePath complete:^(NSError *error) {
// do it
}];

骚操作
=====================

// 放入原视频,换成1号的音,再把3号视频放入混音,剪其中8秒
// 拼1号视频,给1号水印,剪其中8秒
// 拼2号视频,给2号变速
// 拼3号视频,旋转180,剪其中8秒
// 把最后的视频再做一个变速
// 耗时<3秒,如果都是分辩率一致的视频,将更快

[_videoBox appendVideoByPath:_videoPath];
[_videoBox replaceSoundBySoundPath:_testOnePath];
[_videoBox dubbedSoundBySoundPath:_testThreePath];
[_videoBox rangeVideoByTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeMake(3600, 600))];

[_videoBox appendVideoByPath:_testOnePath];
[_videoBox appendWaterMark:[UIImage imageNamed:@"waterMark"] relativeRect:CGRectMake(0.7, 0.7, 0.2, 0.12)];
[_videoBox rangeVideoByTimeRange:CMTimeRangeMake(CMTimeMake(3600, 600), CMTimeMake(3600, 600))];

[_videoBox appendVideoByPath:_testTwoPath];
[_videoBox gearBoxWithScale:2];

[_videoBox appendVideoByPath:_testThreePath];
[_videoBox rotateVideoByDegress:180];
[_videoBox rangeVideoByTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeMake(3600, 600))];

[_videoBox commit];
[_videoBox gearBoxWithScale:2];

[_videoBox asyncFinishEditByFilePath:filePath complete:^(NSError * error) {
// do it
}];

![骚操作](http://recordit.co/euSAMGzK0P.gif)

Box分析
=====================

PS:为了灵活的调用、任意使用,效果与代码的调用顺序有关
比如先变速,再换音轨,明显后面才换的音轨不会有变速效果

WAVideoBox的工作区域:
----缓存区,appedVideo后缓存区域
----工作区,视频指令区域,只有在这区域的视频才是有效操作
----合成区,完成视频指令后待合成区域

1、appendVideo:会将视频加入到缓存区,将工作区内容合成一个视频(无法再拆散),并移到合成区,清空工作区
2、视频操作指令:缓存区视频放到工作区,视频操作只对工作区视频有效
3、commit:合成区域,将缓存区,合成区的视频移到工作区,视频操作对所有视频有效

tip:线程安全,适用于短视频处理



6 changes: 0 additions & 6 deletions WAVideoBox/WAVideoBox/WAAVSeCommand/WAAVSECommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@
*/
- (NSUInteger)degressFromTransform:(CGAffineTransform)transForm;

/**
画布旋转

@param asset asset
@param degress 角度
*/
- (void)performWithAsset:(AVAsset *)asset degress:(NSUInteger)degress;

@end

Expand Down
52 changes: 1 addition & 51 deletions WAVideoBox/WAVideoBox/WAAVSeCommand/WAAVSECommand.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ - (void)performWithAsset:(AVAsset *)asset

self.trackDegress = [self degressFromTransform:self.assetVideoTrack.preferredTransform];

self.composition.mutableComposition.naturalSize = compostionVideoTrack.naturalSize;
if (self.trackDegress % 360) {
[self performVideoCompopsition];
}else{
self.composition.mutableComposition.naturalSize = compostionVideoTrack.naturalSize;
}

}
Expand Down Expand Up @@ -137,55 +136,6 @@ - (void)performAudioCompopsition{
}


- (void)performWithAsset:(AVAsset *)asset degress:(NSUInteger)degress{

[self performVideoCompopsition];

AVMutableVideoCompositionInstruction *instruction = nil;
AVMutableVideoCompositionLayerInstruction *layerInstruction = nil;
CGAffineTransform t1;
CGAffineTransform t2;
CGSize renderSize;

// 角度调整
degress -= degress % 360 % 90;

if (degress == 90) {
t1 = CGAffineTransformMakeTranslation(self.composition.mutableVideoComposition.renderSize.height, 0.0);
renderSize = CGSizeMake(self.composition.mutableVideoComposition.renderSize.height, self.composition.mutableVideoComposition.renderSize.width);
}else if (degress == 180){
t1 = CGAffineTransformMakeTranslation(self.composition.mutableVideoComposition.renderSize.width, self.composition.mutableVideoComposition.renderSize.height);
renderSize = CGSizeMake(self.composition.mutableVideoComposition.renderSize.width, self.composition.mutableVideoComposition.renderSize.height);
}else if (degress == 270){
t1 = CGAffineTransformMakeTranslation(0.0, self.composition.mutableVideoComposition.renderSize.width);
renderSize = CGSizeMake(self.composition.mutableVideoComposition.renderSize.height, self.composition.mutableVideoComposition.renderSize.width);
}else{
t1 = CGAffineTransformMakeTranslation(0.0, 0.0);
renderSize = CGSizeMake(self.composition.mutableVideoComposition.renderSize.width, self.composition.mutableVideoComposition.renderSize.height);
}

// Rotate transformation
t2 = CGAffineTransformRotate(t1, (degress / 180.0) * M_PI );

self.composition.mutableComposition.naturalSize = self.composition.mutableVideoComposition.renderSize = renderSize;

instruction = (AVMutableVideoCompositionInstruction *)(self.composition.mutableVideoComposition.instructions)[0];
layerInstruction = (AVMutableVideoCompositionLayerInstruction *)(instruction.layerInstructions)[0];

CGAffineTransform existingTransform;

if (![layerInstruction getTransformRampForTime:[self.composition.mutableComposition duration] startTransform:&existingTransform endTransform:NULL timeRange:NULL]) {
[layerInstruction setTransform:t2 atTime:kCMTimeZero];
} else {
CGAffineTransform newTransform = CGAffineTransformConcat(existingTransform, t2);
[layerInstruction setTransform:newTransform atTime:kCMTimeZero];
}

instruction.layerInstructions = @[layerInstruction];
self.composition.mutableVideoComposition.instructions = @[instruction];

}

- (NSUInteger)degressFromTransform:(CGAffineTransform)transForm
{
NSUInteger degress = 0;
Expand Down
1 change: 1 addition & 0 deletions WAVideoBox/WAVideoBox/WAAVSeCommand/WAAVSEExportCommand.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ - (void)performSaveAsset:(AVAsset *)asset byPath:(NSString *)path{
}

self.exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:self.composition.presetName];

self.exportSession.shouldOptimizeForNetworkUse = YES;
self.exportSession.videoComposition = self.composition.mutableVideoComposition;
self.exportSession.audioMix = self.composition.mutableAudioMix;
Expand Down
3 changes: 3 additions & 0 deletions WAVideoBox/WAVideoBox/WAAVSeCommand/WAAVSEImageMixCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@

@interface WAAVSEImageMixCommand : WAAVSECommand


@property (nonatomic , assign) BOOL imageBg;

@property (nonatomic , strong) UIImage *image;

@property (nonatomic , strong) NSURL *fileUrl;

// 传回要放的图片位置
- (void)imageLayerRectWithVideoSize:(CGRect (^) (CGSize videoSize))imageLayerRect;

Expand Down
61 changes: 60 additions & 1 deletion WAVideoBox/WAVideoBox/WAAVSeCommand/WAAVSEImageMixCommand.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

@interface WAAVSEImageMixCommand ()


@property (nonatomic , copy) CGRect (^imageLayerRect)(CGSize);

@end
Expand All @@ -34,6 +35,10 @@ - (void)performWithAsset:(AVAsset *)asset{
CALayer *imageLayer;
if (self.imageLayerRect) {
imageLayer = [self buildImageLayerWithRect:self.imageLayerRect(videoSize)];

if (self.fileUrl) {
[imageLayer addAnimation:[self buildAnimationForGif] forKey:@"gif"];
}
}

if (!self.composition.videoLayer || !self.composition.parentLayer) {
Expand Down Expand Up @@ -70,8 +75,62 @@ - (void)imageLayerRectWithVideoSize:(CGRect (^)(CGSize))imageLayerRect{
- (CALayer *)buildImageLayerWithRect:(CGRect)rect{

CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (__bridge id) (self.image.CGImage);
if (self.image) {
imageLayer.contents = (__bridge id) (self.image.CGImage);
}
imageLayer.frame = rect;
return imageLayer;
}

- (CAKeyframeAnimation *)buildAnimationForGif{

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
animation.beginTime = AVCoreAnimationBeginTimeAtZero;
animation.removedOnCompletion = YES;

NSMutableArray * frames = [NSMutableArray new]; NSMutableArray *delayTimes = [NSMutableArray new];
CGFloat totalTime = 0.0;
CGFloat gifWidth;
CGFloat gifHeight;
CGImageSourceRef gifSource = CGImageSourceCreateWithURL((CFURLRef)self.fileUrl, NULL);

size_t frameCount = CGImageSourceGetCount(gifSource);

for (size_t i = 0; i < frameCount; ++i) {
CGImageRef frame = CGImageSourceCreateImageAtIndex(gifSource, i, NULL);
[frames addObject:(__bridge id)frame]; CGImageRelease(frame);

NSDictionary *dict = (NSDictionary*)CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(gifSource, i, NULL));
gifWidth = [[dict valueForKey:(NSString*)kCGImagePropertyPixelWidth] floatValue];
gifHeight = [[dict valueForKey:(NSString*)kCGImagePropertyPixelHeight] floatValue];

NSDictionary *gifDict = [dict valueForKey:(NSString*)kCGImagePropertyGIFDictionary];
[delayTimes addObject:[gifDict valueForKey:(NSString*)kCGImagePropertyGIFUnclampedDelayTime]];

totalTime = totalTime + [[gifDict valueForKey:(NSString*)kCGImagePropertyGIFUnclampedDelayTime] floatValue];
}

if (gifSource) CFRelease(gifSource);

NSMutableArray *times = [NSMutableArray arrayWithCapacity:3];
CGFloat currentTime = 0;
NSInteger count = delayTimes.count;
for (int i = 0; i < count; ++i) {

[times addObject:[NSNumber numberWithFloat:(currentTime / totalTime)]];
currentTime += [[delayTimes objectAtIndex:i] floatValue];
}

NSMutableArray *images = [NSMutableArray arrayWithCapacity:3];
for (int i = 0; i < count; ++i) {
[images addObject:[frames objectAtIndex:i]];
}

animation.keyTimes = times;
animation.values = images;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.duration = totalTime;
animation.repeatCount = HUGE_VALF;
return animation;
}
@end
Loading

0 comments on commit 664a9bd

Please sign in to comment.