iOS使用AVFoundation在视频上添加字幕以及控制字幕时间

释放双眼,带上耳机,听听看~!

IOS在视频上添加字幕效果的基本思路是:

  1. 使用自定义的CATextLayer文字图层或者CAShapeLayer文字图层,添加到视频的Layer上创建用户自定义的字幕效果。这两者的区别是:CATextLayer支持设置简单的文字效果,包括文字的内容、字体、字号大小、对其方式、文字颜色、背景颜色等基本的属性;CAShapeLayer功能更强大,提供了CATextLayer没有的边框大小、边框颜色等设置,如果需要更高级的文字内容展示,需要使用CATextLayer配合UIBezierPath来定制自定义的文字内容。
  2. 通过设置Layer图层的动画来控制字幕的时间点和时间长度,这里有一个坑如果单独设置CATextLayer或者CAShapeLayer的动画不能控制开始的时间,需要额外添加一个CALayer图层,把文字图层CATextLayer或者CAShapeLayer添加到父CALayer图层中,在文字图层CATextLayer或者CAShapeLayer上设置开始的动画
    NSTimeInterval animatedInStartTime = startTime + initAnimationDuration;
CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeInAnimation.fromValue = @0.0f;
fadeInAnimation.toValue = @1.0f;
fadeInAnimation.additive = NO;
fadeInAnimation.removedOnCompletion = NO;
fadeInAnimation.beginTime = animatedInStartTime;
fadeInAnimation.duration = animationDuration;
fadeInAnimation.autoreverses = NO;
fadeInAnimation.fillMode = kCAFillModeBoth;
[textLayer addAnimation:fadeInAnimation forKey:@"opacity"];

在父CALayer图层上设置结束的动画,这样设置才能实现用户自定义的时间点和时间长度

NSTimeInterval animatedOutStartTime = startTime + duration - animationDuration;
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOutAnimation.fromValue = @1.0f;
fadeOutAnimation.toValue = @0.0f;
fadeOutAnimation.additive = NO;
fadeOutAnimation.removedOnCompletion = NO;
fadeOutAnimation.beginTime = animatedOutStartTime;
fadeOutAnimation.duration = animationDuration;
fadeOutAnimation.autoreverses = NO;
fadeOutAnimation.fillMode = kCAFillModeBoth;
[animatedTitleLayer addAnimation:fadeOutAnimation forKey:@"opacity"];

完整的代码

- (CALayer *)buildLayerbuildTxt:(NSString*)text
textSize:(CGFloat)textSize
textColor:(UIColor*)textColor
strokeColor:(UIColor*)strokeColor
opacity:(CGFloat)opacity
textRect:(CGRect)textRect
fontPath:(NSString*)fontPath
viewBounds:(CGSize)viewBounds
startTime:(NSTimeInterval)startTime
duration:(NSTimeInterval)duration
{
if (!text || [text isEqualToString:@""])
{
return nil;
}
// Create a layer for the overall title animation.
CALayer *animatedTitleLayer = [CALayer layer];
// 1. Create a layer for the text of the title.
CATextLayer *titleLayer = [CATextLayer layer];
titleLayer.string = text;
titleLayer.font = (__bridge CFTypeRef)(@"Helvetica");
titleLayer.fontSize = textSize;
titleLayer.alignmentMode = kCAAlignmentCenter;
titleLayer.bounds = CGRectMake(0, 0, textRect.size.width, textRect.size.height);
titleLayer.foregroundColor = textColor.CGColor;
titleLayer.backgroundColor = [UIColor clearColor].CGColor;
// [animatedTitleLayer addSublayer:titleLayer];
// 添加文字以及边框效果
UIFont *font = nil;
if ((fontPath != nil) && (fontPath.length > 0)) {
font = [[FLVideoEditFontManager sharedFLVideoEditFontManager] fontWithPath:fontPath size:textSize];
titleLayer.font = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName);
}
if (font == nil) {
titleLayer.font = (__bridge CFTypeRef)(@"Helvetica");
}
UIBezierPath *path = nil;
if (font) {
path = [FLLayerBuilderTool createPathForText:text fontHeight:textSize fontName:(__bridge CFStringRef)(font.fontName)];
}
else
{
path = [FLLayerBuilderTool createPathForText:text fontHeight:textSize fontName:CFSTR("Helvetica")];
}
CGRect rectPath = CGPathGetBoundingBox(path.CGPath);
CAShapeLayer *textLayer = [CAShapeLayer layer];
textLayer.path = path.CGPath;
textLayer.lineWidth = 1;
if (strokeColor != nil) {
textLayer.strokeColor = strokeColor.CGColor;
}
if (textColor != nil) {
textLayer.fillColor = textColor.CGColor;
}
textLayer.lineJoin = kCALineJoinRound;
textLayer.lineCap = kCALineCapRound;
textLayer.geometryFlipped = NO;
textLayer.opacity = opacity;
textLayer.bounds = CGRectMake(0, 0, rectPath.size.width, textSize+10);
[animatedTitleLayer addSublayer:textLayer];
// 动画图层位置
animatedTitleLayer.position = CGPointMake(textRect.origin.x+textRect.size.width/2, viewBounds.height - textRect.size.height/2 - textRect.origin.y);
NSTimeInterval initAnimationDuration = 0.1f;
NSTimeInterval animationDuration = 0.1f;
// 3.显示动画
NSTimeInterval animatedInStartTime = startTime + initAnimationDuration;
CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeInAnimation.fromValue = @0.0f;
fadeInAnimation.toValue = @1.0f;
fadeInAnimation.additive = NO;
fadeInAnimation.removedOnCompletion = NO;
fadeInAnimation.beginTime = animatedInStartTime;
fadeInAnimation.duration = animationDuration;
fadeInAnimation.autoreverses = NO;
fadeInAnimation.fillMode = kCAFillModeBoth;
[textLayer addAnimation:fadeInAnimation forKey:@"opacity"];
NSTimeInterval animatedOutStartTime = startTime + duration - animationDuration;
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOutAnimation.fromValue = @1.0f;
fadeOutAnimation.toValue = @0.0f;
fadeOutAnimation.additive = NO;
fadeOutAnimation.removedOnCompletion = NO;
fadeOutAnimation.beginTime = animatedOutStartTime;
fadeOutAnimation.duration = animationDuration;
fadeOutAnimation.autoreverses = NO;
fadeOutAnimation.fillMode = kCAFillModeBoth;
[animatedTitleLayer addAnimation:fadeOutAnimation forKey:@"opacity"];
return animatedTitleLayer;
}

依赖的工具类FLLayerBuilderTool.m文件:

#import "FLLayerBuilderTool.h"
#import <CoreText/CoreText.h>
@implementation FLLayerBuilderTool
+ (UIBezierPath*) createPathForText:(NSString*)string fontHeight:(CGFloat)height fontName:(CFStringRef)fontName
{
if ([string length] < 1)
return nil;
UIBezierPath *combinedGlyphsPath = nil;
CGMutablePathRef letters = CGPathCreateMutable();
CTFontRef font = CTFontCreateWithName(fontName, height, NULL);
if (font == nil) {
font = (__bridge CFTypeRef)(@"Helvetica");
}
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)font, kCTFontAttributeName,
nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string
attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
CFArrayRef runArray = CTLineGetGlyphRuns(line);
// for each RUN
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
// Get FONT for this run
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
// for each GLYPH in run
for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
{
// get Glyph & Glyph-data
CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, thisGlyphRange, &glyph);
CTRunGetPositions(run, thisGlyphRange, &position);
// Get PATH of outline
{
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
CGPathAddPath(letters, &t, letter);
CGPathRelease(letter);
}
}
}
CFRelease(line);
combinedGlyphsPath = [UIBezierPath bezierPath];
[combinedGlyphsPath moveToPoint:CGPointZero];
[combinedGlyphsPath appendPath:[UIBezierPath bezierPathWithCGPath:letters]];
CGPathRelease(letters);
CFRelease(font);
if (attrString)
{
attrString = nil;
}
return combinedGlyphsPath;
}
@end

参考资料:
视频特效制作:如何给视频添加边框、水印、动画以及3D效果

AVFoundation Tutorial: Adding Overlays and Animations to Videos

人已赞赏
iOS文章

IOS XCTest使用异步测试

2020-4-27 2:32:37

iOS文章

iOS静态链接

2020-4-27 3:46:59

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索