iOS 信号量在GPUImage中的使用


//
//  video_capture_external_demo.cpp
//  ve_demo
//
//  Created by robotding on 16/5/30.
//  Copyright © 2016年 jjams. All rights reserved.
//

#import "video_capture_external_demo.h"

#import "RosyWriterCPURenderer.h"

@interface VideoCaptureDeviceDemo ()
- (int)start;
- (int)stop;
- (int)restart;

- (int)updateFrameRate;
- (int)updateVideoSize;
- (int)updateRotation;
- (int)updateTorch;
- (int)findDevice;
- (NSString*)findPreset;

- (int)createCam;
- (int)releaseCam;

- (AVCaptureVideoOrientation)findOrientation;

- (CGImageRef)createCGImageFromCVPixelBuffer:(CVPixelBufferRef)pixels ;
@end

static void *openGLESContextQueueKey = &openGLESContextQueueKey;

@implementation VideoCaptureDeviceDemo {
    dispatch_queue_t m_oQueue;
    
    AVCaptureSession *m_rCapSession;
    AVCaptureDevice *m_rCapDevice;
    AVCaptureDeviceInput *m_rDeviceInput;
    AVCaptureVideoDataOutput *m_rDataOutput;
    
    struct {
        int fps;
        int width;
        int height;
        bool front;
        int rotation;
        int torch;
    } m_oSettings;
    
    struct RunningState {
        bool preview;
        bool capture;
    } m_oState;
    
    id<ZegoVideoCaptureClientDelegate> client_;
    
    bool is_take_photo_;
    
    
    id<RosyWriterRenderer> _renderer;
    
    dispatch_queue_t _serialQueue;
    
    dispatch_queue_t _contextQueue;
    
    dispatch_semaphore_t frameRenderingSemaphore;
}

-(id)init{
    self = [super init];
    if(nil != self){
        m_oQueue = dispatch_queue_create("com.zego.ave.vcap.queue", DISPATCH_QUEUE_SERIAL);
        _renderer = [[RosyWriterCPURenderer alloc] init];
        _serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
        
//        openGLESContextQueueKey = &openGLESContextQueueKey;
        _contextQueue = dispatch_queue_create("com.sunsetlakesoftware.GPUImage.openGLESContextQueue", NULL);
        
#if OS_OBJECT_USE_OBJC
        dispatch_queue_set_specific(_contextQueue, openGLESContextQueueKey, &openGLESContextQueueKey, NULL);
#endif
        
        frameRenderingSemaphore = dispatch_semaphore_create(1);
    }
    return self;  
}

- (void)allocateAndStart:(id<ZegoVideoCaptureClientDelegate>) client {
    client_ = client;
    is_take_photo_ = false;
}

- (void)stopAndDeAllocate {
    [client_ destroy];
    client_ = nil;
    
    dispatch_sync(m_oQueue, ^ {
        return ;
    });
}

- (int)startCapture {
    if(m_oState.capture) {
        // * already started
        return 0;
    }
    
    m_oState.capture = true;
    
    if(!m_oState.preview) {
        dispatch_async(m_oQueue, ^{
            [self start];
        });
    }
    
    return 0;
}

- (int)stopCapture {
    if(!m_oState.capture) {
        // * capture is not started
        return 0;
    }
    
    m_oState.capture = false;
    
    if(!m_oState.preview) {
        // * stop the cam
        dispatch_async(m_oQueue, ^{
            [self stop];
        });
    }
    
    return 0;
}

- (int)setFrameRate:(int)framerate {
    // * no change
    if(m_oSettings.fps == framerate) {
        return 0;
    }
    
    m_oSettings.fps = framerate;
    
    dispatch_async(m_oQueue, ^{
        if(m_rCapSession) {
            [self updateFrameRate];
        }
    });
    
    return 0;
}

- (int)setWidth:(int)width andHeight:(int)height {
    // * not changed
    if ((m_oSettings.width == width) && (m_oSettings.height == height)) {
        return 0;
    }
    
    m_oSettings.width = width;
    m_oSettings.height = height;
    
    dispatch_async(m_oQueue, ^{
        if(m_rCapSession) {
            [self updateVideoSize];
            [self updateFrameRate];
        }
    });
    
    return 0;
}

- (int)setFrontCam:(int)bFront {
    if(m_oSettings.front == bFront) {
        return 0;
    }
    
    m_oSettings.front = bFront;
    
    dispatch_async(m_oQueue, ^{
        // * just restart capture
        if(m_rCapSession) {
            [self stop];
            [self start];
        }
    });
    
    return 0;
}

- (int)setView:(UIView*)view {
    dispatch_async(m_oQueue, ^{
        // * restart cam
        [self restart];
    });
    
    return 0;
}

- (int)setViewMode:(int)mode {
    return 0;
}

- (int)setViewRotation:(int)rotation {
    return 0;
}

- (int)setCaptureRotation:(int)rotaion {
    if(m_oSettings.rotation == rotaion) {
        return 0;
    }
    
    m_oSettings.rotation = rotaion;
    
    dispatch_async(m_oQueue, ^{
        if(m_rCapSession) {
            [self updateRotation];
        }
    });
    
    return 0;
}

- (int)startPreview {
    if(m_oState.preview) {
        // * preview already started
        return 0;
    }
    
    m_oState.preview = true;
    
    if(!m_oState.capture) {
        // * let's start the cam
        dispatch_async(m_oQueue, ^{
            [self start];
        });
    }
    
    return 0;
}

- (int)stopPreview {
    if(!m_oState.preview) {
        // * preview not started
        return 0;
    }
    
    m_oState.preview = false;
    
    if(!m_oState.capture) {
        dispatch_async(m_oQueue, ^{
            [self stop];
        });
    }
    
    return 0;
}

- (int)enableTorch:(bool)enable {
    if (m_oSettings.torch == enable) {
        return 0;
    } else {
        m_oSettings.torch = enable;
    }
    
    dispatch_async(m_oQueue, ^{
        if (m_rCapSession) {
            [self updateTorch];
        }
    });
    
    return 0;
}

- (int)takeSnapshot {
    is_take_photo_ = true;
    return 0;
}

- (int)setPowerlineFreq:(unsigned int)freq {
    return 0;
}


- (int)createCam {
    [self findDevice];
    
    m_rCapSession = [[AVCaptureSession alloc] init];
    
    [m_rCapSession beginConfiguration];
    
    NSError *error = nil;
    m_rDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:m_rCapDevice error:&error];
    
    if([m_rCapSession canAddInput:m_rDeviceInput]) {
        [m_rCapSession addInput:m_rDeviceInput];
    }
    
    m_rDataOutput = [[AVCaptureVideoDataOutput alloc] init];
    // * [m_rDataOutput setAlwaysDiscardsLateVideoFrames:NO];
    [m_rDataOutput setAlwaysDiscardsLateVideoFrames:YES];
    // * [m_rDataOutput setVideoSettings:@{(NSString*)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32BGRA)}];
    
    // * set output to 640x480
    double dWidth = m_oSettings.width;
    double dHeight = m_oSettings.height;
    
    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                   [NSNumber numberWithDouble:dWidth], (id)kCVPixelBufferWidthKey,
                                   [NSNumber numberWithDouble:dHeight], (id)kCVPixelBufferHeightKey,
                                   [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
                                   nil
                                   ];
    
    // * [datOutput setVideoSettings: [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
    [m_rDataOutput setVideoSettings:videoSettings];

    [m_rDataOutput setSampleBufferDelegate:self queue:m_oQueue];
    
    if([m_rCapSession canAddOutput:m_rDataOutput]) {
        [m_rCapSession addOutput:m_rDataOutput];
    }
    
    // * [capSession setSessionPreset:AVCaptureSessionPresetMedium];
    NSString *strPreset = [self findPreset];
    
    if([m_rCapSession canSetSessionPreset:strPreset]) {
        [m_rCapSession setSessionPreset:strPreset];
    }
    
    // * set video orientation
    AVCaptureConnection *vconn = [m_rDataOutput connectionWithMediaType:AVMediaTypeVideo];
    if([vconn isVideoOrientationSupported]) {
        [vconn setVideoOrientation:[self findOrientation]];
    }
    
    [m_rCapSession commitConfiguration];
    
    // * set framerate
    [self updateFrameRate];
    
    // * torch
    [self updateTorch];
    
    return 0;
}

- (int)releaseCam {
    if(m_rDataOutput) {
        [m_rDataOutput setSampleBufferDelegate:nil queue:dispatch_get_main_queue()];
    }
    
    if(m_rCapSession) {
        if(m_rDeviceInput) {
            [m_rCapSession removeInput:m_rDeviceInput];
        }
        
        if(m_rDataOutput) {
            [m_rCapSession removeOutput:m_rDataOutput];
        }
    }
    
    m_rDataOutput = nil;
    m_rDeviceInput = nil;
    m_rCapDevice = nil;
    m_rCapSession = nil;
    
    return 0;
}

- (int)start {
    // * make sure we are stopped
    [self stop];
    
    [self createCam];
    
    // * [m_rCapSession startRunning];
    // * queue a async start
    
    // * lock again
    if(m_rCapSession)
    {
        [m_rCapSession startRunning];
    }
    
    return 0;
}

- (int)stop {
    if (m_rCapSession) {
        if([m_rCapSession isRunning]) {
            [m_rCapSession stopRunning];
        }
    }
    
    [self releaseCam];
    
    return 0;
}

- (int)restart {
    // * stop capture (without releasing capture)
    if (m_rCapSession) {
        if([m_rCapSession isRunning]) {
            [m_rCapSession stopRunning];
        }
    }
    
    // * queue a async start
    if(m_rCapSession) {
        [m_rCapSession startRunning];
    }
    
    return 0;
}

- (int)updateFrameRate {
    // * set framerate
    int framerate = m_oSettings.fps;
    CMTime frameDuration;
    if(framerate > 0) {
        frameDuration = CMTimeMake(1, framerate);
    } else {
        frameDuration = kCMTimeInvalid;
    }
    NSError *err = nil;
    [m_rCapDevice lockForConfiguration:&err];
    m_rCapDevice.activeVideoMaxFrameDuration = frameDuration;
    m_rCapDevice.activeVideoMinFrameDuration = frameDuration;
    [m_rCapDevice unlockForConfiguration];
    // * for(AVCaptureConnection *conn in datOutput.connections)
    // * {
    // * 	if([conn respondsToSelector:@selector(setVideoMinFrameDuration:)])
    // * 	{
    // * 		[conn setVideoMinFrameDuration:frameDuration];
    // * 	}
    // * }
    
    return 0;
}

- (int)updateVideoSize {
    [m_rCapSession beginConfiguration];
    
    // * set output to 640x480
    double dWidth = m_oSettings.width;
    double dHeight = m_oSettings.height;
    
    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                   [NSNumber numberWithDouble:dWidth], (id)kCVPixelBufferWidthKey,
                                   [NSNumber numberWithDouble:dHeight], (id)kCVPixelBufferHeightKey,
                                   [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
                                   nil
                                   ];
    
    // * [datOutput setVideoSettings: [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
    [m_rDataOutput setVideoSettings:videoSettings];
    
    NSString *strPreset = [self findPreset];
    
    if([m_rCapSession canSetSessionPreset:strPreset])
    {
        [m_rCapSession setSessionPreset:strPreset];
    }
    
    [m_rCapSession commitConfiguration];
    
    return 0;
}

- (int)updateRotation {
    [m_rCapSession beginConfiguration];
    
    AVCaptureConnection *vconn = [m_rDataOutput connectionWithMediaType:AVMediaTypeVideo];
    if([vconn isVideoOrientationSupported]) {
        [vconn setVideoOrientation:[self findOrientation]];
    }
    
    [m_rCapSession commitConfiguration];
    
    return 0;
}

- (int)updateTorch {
    AVCaptureTorchMode mode = m_oSettings.torch ? AVCaptureTorchModeOn : AVCaptureTorchModeOff;
    
    if(m_rCapDevice && [m_rCapDevice hasTorch] && [m_rCapDevice isTorchModeSupported:mode]) {
        [m_rCapDevice lockForConfiguration:nil];
        [m_rCapDevice setTorchMode:mode];
        [m_rCapDevice unlockForConfiguration];
    }
    
    return 0;
}

- (int)findDevice {
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for(AVCaptureDevice *device in devices) {
        if(m_oSettings.front) {
            if(device.position == AVCaptureDevicePositionFront) {
                m_rCapDevice = device;
            }
        } else {
            if(device.position == AVCaptureDevicePositionBack) {
                m_rCapDevice = device;
            }
        }
    }
    
    return 0;
}

- (NSString*)findPreset {
    int width = m_oSettings.width;
    int height = m_oSettings.height;
    
    if((width >= 1920) || (height >= 1080))  {
        return AVCaptureSessionPreset1920x1080;
    } else if((width >= 1280) || (height >= 720)) {
        return AVCaptureSessionPreset1280x720;
    }
    // * else if((width >= 960) || (height >= 540))
    // * {
    // * 	return AVCaptureSessionPreset960x540;
    // * }
    else if((width >= 640) || (height >= 480))
    {
        return AVCaptureSessionPreset640x480;
    } else if((width >= 352) || (height >= 288)) {
        return AVCaptureSessionPreset352x288;
    }
    // * else if((width >= 320) || (height >= 240))
    // * {
    // * 	return AVCaptureSessionPreset320x240;
    // * }
    
    // * ??????????
    return AVCaptureSessionPreset640x480;
}

- (AVCaptureVideoOrientation)findOrientation {
    AVCaptureVideoOrientation nOrientation = AVCaptureVideoOrientationPortrait;
    
    if(m_oSettings.rotation == 0) {
        nOrientation = AVCaptureVideoOrientationPortrait;
    } else if(m_oSettings.rotation == 90) {
        nOrientation = AVCaptureVideoOrientationLandscapeLeft;
    } else if(m_oSettings.rotation == 180) {
        nOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
    } else if(m_oSettings.rotation == 270) {
        nOrientation = AVCaptureVideoOrientationLandscapeRight;
    }
    
    return nOrientation;
}

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    
    [self processPixelBufferdidWithOutputSampleBuffer:sampleBuffer];
    
}

- (void)processPixelBufferdidWithOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer{
    
    CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);

    BOOL isWaitingTimeOut = dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW);
    if (isWaitingTimeOut == YES) {
        NSLog(@"等待超时");
        return;
    }
    
    dispatch_async(_serialQueue, ^{
        CVPixelBufferRef renderedPixelBuffer = NULL;
        CVPixelBufferRef sourcePixelBuffer = CMSampleBufferGetImageBuffer( sampleBuffer );
        renderedPixelBuffer = [_renderer copyRenderedPixelBuffer:sourcePixelBuffer];
        [client_ onIncomingCapturedData:renderedPixelBuffer withPresentationTimeStamp:pts];
        CFRelease( renderedPixelBuffer );
    });
    
    //方法1
//    //等待超时-返回非零
//    if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
//    {
//        //等待超时-则返回
//        return;
//    }
//    
//    CFRetain(sampleBuffer);
//    [self runAsynchronouslyOnVideoProcessingQueue:^{
//        //Feature Detection Hook.
//        
//        //[self processVideoSampleBuffer:sampleBuffer];
//        
//        CVPixelBufferRef renderedPixelBuffer = NULL;
//        CVPixelBufferRef sourcePixelBuffer = CMSampleBufferGetImageBuffer( sampleBuffer );
//        renderedPixelBuffer = [_renderer copyRenderedPixelBuffer:sourcePixelBuffer];
//        [client_ onIncomingCapturedData:renderedPixelBuffer withPresentationTimeStamp:pts];
//        CFRelease( renderedPixelBuffer );
//        CFRelease(sampleBuffer);
//        dispatch_semaphore_signal(frameRenderingSemaphore);
//    }];
    
    /*方法二
    @synchronized (_renderer) {
        CVPixelBufferRef renderedPixelBuffer = NULL;
        CVPixelBufferRef sourcePixelBuffer = CMSampleBufferGetImageBuffer( sampleBuffer );
        renderedPixelBuffer = [_renderer copyRenderedPixelBuffer:sourcePixelBuffer];
        [client_ onIncomingCapturedData:renderedPixelBuffer withPresentationTimeStamp:pts];
        CFRelease( renderedPixelBuffer );
    };*/
}

- (void) runAsynchronouslyOnVideoProcessingQueue:(void (^)(void))block
{
    dispatch_queue_t videoProcessingQueue = _contextQueue;
    
    if (dispatch_get_specific(openGLESContextQueueKey))
    {
        block();
    }else
    {
        dispatch_async(videoProcessingQueue, block);
    }
}

//void runAsynchronouslyOnVideoProcessingQueue(void (^block)(void))
//{
//    dispatch_queue_t videoProcessingQueue = [GPUImageContext sharedContextQueue];
//    
//#if !OS_OBJECT_USE_OBJC
//#pragma clang diagnostic push
//#pragma clang diagnostic ignored "-Wdeprecated-declarations"
//    if (dispatch_get_current_queue() == videoProcessingQueue)
//#pragma clang diagnostic pop
//#else
//        if (dispatch_get_specific([GPUImageContext contextKey]))
//#endif
//        {
//            block();
//        }else
//        {
//            dispatch_async(videoProcessingQueue, block);
//        }
//}

- (CGImageRef)createCGImageFromCVPixelBuffer:(CVPixelBufferRef)pixels {
    int width = (int)CVPixelBufferGetWidth(pixels);
    int height = (int)CVPixelBufferGetHeight(pixels);
    int stride = (int)CVPixelBufferGetBytesPerRow(pixels);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    CVPixelBufferLockBaseAddress(pixels, kCVPixelBufferLock_ReadOnly);
    unsigned char *bgra = (unsigned char*)CVPixelBufferGetBaseAddress(pixels);
    CFDataRef data = CFDataCreate(kCFAllocatorDefault, bgra, CVPixelBufferGetBytesPerRow(pixels)*height);
    
    unsigned char *rgba = (unsigned char*)CFDataGetBytePtr(data);
    for(int h=0; h<height; h++) {
        unsigned char *line = rgba + (h * stride);
        for(int w=0; w<width; w++) {
            unsigned char tmp = line[4*w+0];
            line[4*w+0] = line[4*w+2];
            line[4*w+2] = tmp;
        }
    }
    
    CGDataProviderRef dp = CGDataProviderCreateWithCFData(data);
    CGImageRef image = CGImageCreate(width, height, 8, 32, CVPixelBufferGetBytesPerRow(pixels), colorSpace, kCGImageAlphaPremultipliedLast, dp, NULL, NO, kCGRenderingIntentDefault);
    
    CGDataProviderRelease(dp);
    CFRelease(data);
    CGColorSpaceRelease(colorSpace);
    CVPixelBufferUnlockBaseAddress(pixels, kCVPixelBufferLock_ReadOnly);
    
    return image;
}

@end

@implementation VideoCaptureFactoryDemo {
    VideoCaptureDeviceDemo* g_device_;
}

- (id<ZegoVideoCaptureDevice>)create:(NSString*)deviceId {
    if (g_device_ == nil) {
        g_device_ = [[VideoCaptureDeviceDemo alloc]init];
    }
    return g_device_;
}

- (void)destroy:(id<ZegoVideoCaptureDevice>)device {
    
}

@end


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值