你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> 多媒體編程——ios視頻圖像繪制工具類。

多媒體編程——ios視頻圖像繪制工具類。

編輯: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 
#import 
#import 

/*
  渲染視頻,只支持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 

#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 _fmEmptyQueue ;
    std::queue _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_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_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 

@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



  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved