Skip to content

logomark

pengbin edited this page Jul 31, 2017 · 6 revisions

水印(静态/动态图片或文字)

所谓水印其实就是一张半透明的图片叠加在预览视图的指定位置上. 这里介绍的添加水印的方法是在视频编码前直接叠加到视频画面中去, 这样主播和观众看到的内容是一致的, 主要用到接口是图层混合.

静态图片水印

最常见的图片水印主要是将公司的logo等内容放到视频的某个角上.

  • 导入图片 (任意方式导入, 这里用GPUImagePicture来载入, url 是对应图片的地址)
_kit.logoPic  = [[GPUImagePicture alloc] initWithURL: url];
  • 设置水印的位置和透明度
_kit.logoRect = CGRectMake(0.05, 0.05, 0, hgt);
_kit.logoAlpha= 0.5;

其中 水印的位置信息, 包括左上角的位置, 和宽高信息, 都采用相对预览输出尺寸,归一化为0~1.0的比例值

  • 左上角位置: 水印图片的左上角相对预览输出的左上角的偏移
  • 宽高信息: 将原始图片缩放到指定宽高对应的矩形框中, 提供如下简易设置方法:
    • (0, hgt): 宽为0, 仅指定高度, 则根据高度和图片的宽高比计算宽度.
    • (wdt, 0): 高为0, 仅指定宽度, 则根据宽度和图片的宽高比计算宽度.
    • (0, 0): 保持宽高比 填充预览视图

文字水印

文字水印可以用于添加时间信息. kit类中借用UILabel来指定文字的格式,颜色,透明度等信息. 从UILabel中可以得到对应字符串渲染后的UIImage, 将UIImage载入GPUImagePicture之后,就可以通过picMixer叠加到图层上了.

  • 设置label的文字内容, 格式和位置
NSDateFormatter *_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateFormat = @"HH:mm:ss";
NSDate *now = [[NSDate alloc] init];
_kit.textLable.text = [_dateFormatter stringFromDate:now];
_kit.textLable.textColor = [UIColor whiteColor];
_kit.textLable.alpha = 0.9;
_kit.textRect = CGRectMake(0.05, yPos, 0, hgt);
  • 在timer中定时刷新文字的内容
_kit.textLable.text = [_dateFormatter stringFromDate:now];
[_kit updateTextLable];

动态图片水印

有时需要引入动画版的水印, 比如gif/apng等格式的小动画. 其实实现方法,跟静态图片水印一样, 只是需要加上特定格式图片解码和定时刷新. 大家可以自己找需要支持的格式的解码库, 我们demo中使用了YYImage, 解码gif和apng

// 载入动画图片到解码器, 并启动timer,按图片的帧率进行刷新
- (void) setupAnimateLogo:(NSString*)path {
    NSData *data = [NSData dataWithContentsOfFile:path];
    [_dlLock lock];
    _animateDecoder = [YYImageDecoder decoderWithData:data scale:2.0];
    [_dlLock unlock];
    _animateIdx = 0;
    _dlTime = 0;
    if(!_displayLink){
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkCallBack:)];
        [_displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                           forMode:NSRunLoopCommonModes];
    }
}
// 更新画面内容, 让画面动起来
- (void) updateAnimateLogo {
    if (_animateDecoder==nil) {
        return;
    }
    [_dlLock lock];
    YYImageFrame* frame = [_animateDecoder frameAtIndex:_animateIdx
                                       decodeForDisplay:NO];
    if (frame.image) {
        _kit.logoPic = [[GPUImagePicture alloc] initWithImage:frame.image];
    }
    _animateIdx = (_animateIdx+1)%_animateDecoder.frameCount;
    [_dlLock unlock];
}
// timer 回调, 这里主要需要注意的是帧率的设定
- (void)displayLinkCallBack:(CADisplayLink *)link {
    dispatch_async( dispatch_get_global_queue(0, 0), ^(){
        if (_animateDecoder) {
            _dlTime += link.duration;
            // 读取 图像的 duration 来决定下一帧的刷新时间
            // 也可以固定设置为一个值来调整动画的快慢程度
            NSTimeInterval delay = [_animateDecoder frameDurationAtIndex:_animateIdx];
            if (delay < 0.04) { // 太高的帧率(比采集帧率高)最后观众端可能看不到所有的画面, 会丢帧
                delay = 0.04;
            }
            if (_dlTime < delay) return;
            _dlTime -= delay;
            [self updateAnimateLogo];
        }
    });
}

1. 推流环节说明

2. 特色功能说明

2.1 采集

2.2 音频处理

2.3 视频处理

2.4 编码

2.5 推流

2.6 输入多样化

2.7 集成

3. 第三方功能

4. 技术专栏

5. 已知问题

8. FAQ

金山云计算

Clone this wiki locally