多媒体编程——ios视频图像绘制工具类。

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

多媒体编程——ios视频图像绘制工具类。

时间: 2024-10-05 04:58:20

多媒体编程——ios视频图像绘制工具类。的相关文章

多媒体编程——ios摄像头图像抓取工具类

工具类提供预览图像画面,自动处理旋转,并且以主动方式抓取图像(这样帧率可以无限大) 系统的接口多是异步接收图像,像我这种强迫症怎么受得了,必须吧被动接收图像的方式改成主动抓取. 头文件 #import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> //这些比例都是4:3的比例. typedef enum TKVideoFrameSize { tkVideoFrame480x360 = 480 <

java获取视频第一帧工具类

java获取视频文件第一帧,为了防止黑画面,此例取第五帧,本例子采用maven形式 1. pom.xml 添加引用jar包 jdk采用1.8,日志自行添加即可,这里使用的是log4j2 <!-- log4j2 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.

ios Base64编解码工具类及使用

为了避免明码传递http内容,可以用base64编码后传输,收到方再解码,也方便了2进制数据的字符串式传输. 对于ios来说,google给提供了一个很好的工具类,方便进行base64编解码,当然也可以用openssl来进行,但这东西相对来 说比较麻烦.google给提供了3个文件就够了. 官网地址是: http://code.google.com/p/google-toolbox-for-mac/ 这里面有很多宝贝,如果自己找很难找到这3个文件,所以我加到附件里.解压后放到ios工程中即可使用

Java线程与并发编程实践----额外的并发工具类

一.并发集合 java.util包下提供了很多的集合类,如ArrayList.TreeSet.HashMap,但是这些 集合都是非线程安全的,并且对于单列集合的迭代器,采用的是快速失败机制,当正在迭代 遍历的集合被其它线程修改时,便会抛出 java.util.ConcurrentModificationException. 这显然对于多线程操作的集合是十分不方便的,但早Colections这个工具类中有方法可以返回 线程安全的集合,然而这种集合对于高并发环境,性能十分低下. 于是,java.ut

JKXY的视频内容下载工具类

package cn.jsonlu.make.license; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.junit.Test; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Li

iOS 图像绘制

最近看到iOS的图像绘制,大致两部分. 一部分是openGL:(参考1~3) 一部分的图像绘制(④). 参考 ① http://www.cocoachina.com/game/20141127/10335.html ② http://mississi.blog.163.com/blog/static/1024892012012727104953605/ ③ http://blog.csdn.net/wangyuchun_799/article/details/7736928 ④ http://a

java并发编程中常用的工具类 Executor

/***************************************************  * TODO: description .  * @author: gao_chun  * @since:  2015-4-17  * @version: 1.0.0  * @remark: 转载请注明出处  **************************************************/ java.util.concurrent.Executor 使用 Execut

IOS 视频分解图片、图片合成视频

在IOS视频处理中,视频分解图片和图片合成视频是IOS视频处理中经常遇到的问题,这篇博客就这两个部分对IOS视频图像的相互转换做一下分析. (1)视频分解图片 这里视频分解图片使用的是AVAssetImageGenerator,利用这个class可以很方便的实现不同时间戳下,视频帧的抓取.注意一般这种视频分解图片帧的方法都是放在子线程中的,而UI更新操作都是放在主线程中的.下面来看看核心代码: _imageGenerator = [[AVAssetImageGenerator alloc] in

Java学习笔记—多线程(并发工具类,java.util.concurrent.atomic包)

在JDK的并发包里提供了几个非常有用的并发工具类.CountDownLatch.CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线程间交换数据的一种手段.本章会配合一些应用场景来介绍如何使用这些工具类. CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作.假如有这样一个需求:我们需要解析一个Excel里多个sheet的数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数据,