您的位置:首页 > 移动开发 > IOS开发

多媒体编程——ios视频图像绘制(1)

2014-08-11 16:18 316 查看
IOS上视频级的图像绘制

ios上的图像绘制常规的是 UIView的drawRect函数,但是这个函数是异步触发,并且由主线程执行。虽然可以通过一定技巧达到主动绘制的效果:

1、传递图像给UIView缓存着。

2、然后调用UIView的setNeedDisplay 改写重绘标志。

(以上两步是讲图像丢给UIView,让它自己进行绘制,但是绘制的时机不可控,有时候我们需要它马上绘制,甚至有时候我们需要知道它什么时候绘制完成了,就需要下面两步)

3、在播放线程中调用UIView的 perfromOnMainThread 最后一个参数 waitUtilDone = true, 执行某个自定义函数比如叫 mydrawImage

4、mydrawImage中 调用 【NSRunloop mainLoop】run 。。。。 (执行一次消息泵抽送)

(这样调用perfromOnMainThread的地方就会阻塞,知道真正的绘制完成。)

但是这种方式的模拟同步方式绘制 知识等待主线程绘制完成,并且如果扩展到多帧缓存 就比较麻烦了,并且UIView必须自己继承然后重写。

下面附上代码 基于类似思想,但是是基于CALayer 完成的渲染工具类,同步和异步切换只需要改一下 waitUtilDone的参数即可。

实际测试 帧率可以达到25左右 (ipad mini1),如果机器好点 速度应该更快。

头文件

#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
#import <stdint.h>

/*
渲染视频,只支持RGB RGB RGB 32bit格式。
*/

@interface TKVideoPlayer : NSObject

- (bool) create:(UIView*)target width:(uint16_t)width height:(uint16_t)height frate:(float)frate ;
- (bool) destory ;
- (bool) update:(uint8_t*)buf len:(uint32_t) len ;
- (bool) start ;
- (bool) stop ;
@end


实现文件

//
//  TKVideoPlayer.m
//  FLVPlayer
//
//  Created by administrator on 14-7-11.
//  Copyright (c) 2014年 trustsky. All rights reserved.
//

#import "TKVideoPlayer.h"
#import "TKTimer.h"
#import "TKTimer2.h"
#import "TKLock.h"
#import "TKTicker.h"

#include <queue>

#define TKVIDEO_FRAME_CACHE_COUNT 8

@interface TKVideoPlayer ()
{
UIView*         _view    ;
float           _frate   ;

uint16_t        _width   ;
uint16_t        _height  ;

uint8_t*        _buffer  ;
uint32_t        _length  ;

TKTimer2*       _timer   ;

bool            _state   ;

TKLock*         _lockEmptyQueue ;
TKLock*         _lockFilledQueue ;

std::queue<uint8_t*> _fmEmptyQueue ;
std::queue<uint8_t*> _fmFiledQueue ;

uint8_t*             _fmbuffr[TKVIDEO_FRAME_CACHE_COUNT];

dispatch_semaphore_t _sgEmpty ;
dispatch_semaphore_t _sgfilled ;
}

@end

@implementation TKVideoPlayer

- (bool) create:(UIView*)target width:(uint16_t)width height:(uint16_t)height frate:(float)frate;
{
self->_view    = target ;
self->_width   = width  ;
self->_height  = height ;
self->_frate   = frate  ;
self->_length  = width * height * 4 ;

self->_view.layer.delegate = self ;

self->_sgfilled = dispatch_semaphore_create(TKVIDEO_FRAME_CACHE_COUNT);
self->_sgEmpty  = dispatch_semaphore_create(TKVIDEO_FRAME_CACHE_COUNT);

for(int idx=0; idx<TKVIDEO_FRAME_CACHE_COUNT; idx++)
{
_fmbuffr[idx] = (uint8_t*)malloc(_length) ;
_fmEmptyQueue.push(_fmbuffr[idx]);
dispatch_semaphore_wait(_sgfilled, DISPATCH_TIME_FOREVER);
}

self->_lockFilledQueue = [[TKLock alloc] init];
[self->_lockFilledQueue open];

self->_lockEmptyQueue = [[TKLock alloc] init];
[self->_lockEmptyQueue open];

return true ;
}

- (bool) destory
{
self->_view.layer.delegate = nil ;
self->_view = nil ;

self->_buffer = NULL ;

for(int idx=0; idx<TKVIDEO_FRAME_CACHE_COUNT; idx++)
{
free(_fmbuffr[idx]) ;
_fmbuffr[idx] = NULL ;
}

[self->_lockFilledQueue close];
[self->_lockFilledQueue release];
self->_lockFilledQueue = nil ;

[self->_lockEmptyQueue close];
[self->_lockEmptyQueue release];
self->_lockEmptyQueue = nil ;

int lastCount = TKVIDEO_FRAME_CACHE_COUNT - _fmEmptyQueue.size() - _fmFiledQueue.size() ;
for(int idx=0; idx<_fmEmptyQueue.size()+lastCount; idx++)
dispatch_semaphore_signal(self->_sgfilled);
for(int idx=0; idx<_fmFiledQueue.size()+lastCount; idx++)
dispatch_semaphore_signal(self->_sgEmpty);

dispatch_release(self->_sgfilled);
self->_sgfilled = nil ;

dispatch_release(self->_sgEmpty);
self->_sgEmpty = nil ;

return true ;
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
//计算图像居中应该的尺寸
CGRect frame = [layer bounds];

float scalew = frame.size.width/_width ;
float scaleh = frame.size.height/_height;

float scale = scalew < scaleh ? scalew : scaleh ;

float image_width  = _width * scale ;
float image_height = _height * scale ;

CGRect rect = CGRectMake((frame.size.width - image_width)/2, (frame.size.height - image_height)/2, image_width, image_height);

if(_state && _buffer)
{
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, _buffer, _length, NULL);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB() ;
CGImageRef imageRef = CGImageCreate(_width, _height, 8, 32, 4 * _width, colorSpaceRef, kCGBitmapByteOrder32Little|kCGImageAlphaFirst, provider, NULL, NO, kCGRenderingIntentDefault);

CGContextTranslateCTM(context, 0.0, frame.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, rect, imageRef);

CGImageRelease(imageRef);
CGColorSpaceRelease(colorSpaceRef);
CGDataProviderRelease(provider);

//NSLog(@"drawLayer Time Tick = %u.", get_tick32());
}
else
{
CGContextSetRGBFillColor(context, 0, 0, 0, 1);
CGContextFillRect(context, frame);
}
}

- (bool) update:(uint8_t*)buf len:(uint32_t) len
{
if(_state)
{
dispatch_semaphore_wait(_sgEmpty, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC* 100));

[_lockEmptyQueue lock];
if(_fmEmptyQueue.size() == 0)
{
[_lockEmptyQueue unlock];
return true;
}
uint8_t* cachebuf = _fmEmptyQueue.front();
_fmEmptyQueue.pop();
[_lockEmptyQueue unlock];

memcpy(cachebuf, buf, len);

[_lockFilledQueue lock];
_fmFiledQueue.push(cachebuf);
[_lockFilledQueue unlock];

dispatch_semaphore_signal(self->_sgfilled);
}
return true ;
}

- (void) timer_call
{
if(_state)
{
dispatch_semaphore_wait(self->_sgfilled, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC*100)); //等待100毫秒

[_lockFilledQueue lock];
if(_fmFiledQueue.size() == 0)
{
[_lockFilledQueue unlock];
return ;
}
uint8_t* cachebuf = _fmFiledQueue.front();
_fmFiledQueue.pop();
[_lockFilledQueue unlock];

[self performSelectorOnMainThread:@selector(timerDrawFrame:)
withObject:[NSNumber numberWithUnsignedLongLong:(uint64_t)cachebuf]
waitUntilDone:false];
}
}

- (void) timerDrawFrame:(NSNumber*)bufNumber
{
self->_buffer = (uint8_t*)bufNumber.unsignedLongLongValue ;

if(_state && _buffer)
{
[self->_view.layer setNeedsDisplay];
[self->_view.layer display];

[_lockEmptyQueue lock];
_fmEmptyQueue.push(self->_buffer);
[_lockEmptyQueue unlock];

dispatch_semaphore_signal(self->_sgEmpty);
}
else
{
[self->_view.layer setNeedsDisplay];
[self->_view.layer display];
}
}

- (bool) clear
{
[self performSelectorOnMainThread:@selector(timerDrawFrame:)
withObject:[NSNumber numberWithUnsignedLongLong:NULL]
waitUntilDone:true];

return true ;
}

- (bool) start
{
if(_timer == nil)
{
_timer = [[TKTimer2 alloc] init];
_timer.delay   = 1000/_frate ;
_timer.objcall = self ;
_timer.selcall = @selector(timer_call);
[_timer start];
_state = true ;
}
return true ;
}

- (bool) stop
{
if(_timer)
{
_state = false ;
[_timer stop];
[self clear];
}
return true ;
}

@end


//里面用到了一个 TKTimer计时器

计时器的头文件是这样的

@interface TKTimer2 : NSObject

@property (assign, nonatomic) id        objcall ;
@property (assign, nonatomic) SEL       selcall ;
@property (assign, nonatomic) uint32_t  delay ;

- (void) start ;
- (void) stop ;

@end


设置回调的id + SEL 然后设置延迟 毫秒单位,调用start之后该id+sel会被重复执行。本人还在调研那种计时效果准确,所以就不发上来误导大家了,大家自己实现吧

还用到了一个TKLock

#import <Foundation/Foundation.h>

@interface TKLock : NSObject

- (void)open ;
- (void)close ;

- (void)lock ;
- (void)unlock ;

- (bool)trylock:(uint32_t)tick ;
@end


实现如下:

#import "TKLock.h"

@interface TKLock ()
{
dispatch_semaphore_t    _sglock ; //是否缓存为空
}
@end

@implementation TKLock

- (void)open
{
_sglock  = dispatch_semaphore_create(1);
}
- (void)close
{
[self trylock:1];
dispatch_semaphore_signal(_sglock);
dispatch_release(_sglock);
_sglock = nil ;
}

- (void)lock
{
dispatch_semaphore_wait(_sglock, DISPATCH_TIME_FOREVER);
}

- (void)unlock
{
dispatch_semaphore_signal(_sglock);
}

- (bool)trylock:(uint32_t)tick
{
long retcode = dispatch_semaphore_wait(_sglock, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC*tick));
return (retcode == 0) ;
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: