Как обрабатывать буферы изображений GPUImage, чтобы они могли использоваться с такими вещами, как Tokbox?

Я использую OpenTok и заменяю свой Publisher своей собственной подклассовой версией, которая включает GPUImage. Моя цель – добавить фильтры.

Приложение строит и запускает, но сбой здесь:

func willOutputSampleBuffer(sampleBuffer: CMSampleBuffer!) { let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) CVPixelBufferLockBaseAddress(imageBuffer!, 0) videoFrame?.clearPlanes() for var i = 0 ; i < CVPixelBufferGetPlaneCount(imageBuffer!); i++ { print(i) videoFrame?.planes.addPointer(CVPixelBufferGetBaseAddressOfPlane(imageBuffer!, i)) } videoFrame?.orientation = OTVideoOrientation.Left videoCaptureConsumer.consumeFrame(videoFrame) //comment this out to stop app from crashing. Otherwise, it crashes here. CVPixelBufferUnlockBaseAddress(imageBuffer!, 0) } 

Если я прокомментирую эту строку, я могу запустить приложение без сбоев. Фактически, я вижу, что фильтр применяется правильно, но он мерцает. Публикации публикуются в Opentok.

Вся моя база кода может быть загружена. Нажмите здесь, чтобы просмотреть конкретный файл: Это конкретный файл для класса . На самом деле это довольно легко запустить – просто установите pod перед запуском.

После проверки может быть, что videoCaptureConsumer не инициализирован. Ссылка на протокол

Я понятия не имею, что означает мой код. Я перевел его непосредственно из этого объектного файла C: пример проекта Tokbox

    Я проанализировал как ваш, Swift проект, так и Objective-C project. Я понял, что не работает.

    С этой записью я хочу дать первое обновление и показать действительно полезную демонстрацию того, как использовать фильтры изображений GPU с OpenTok.

    Причины, по которым ваша реализация фильтра GPUImage не беспокоит OpenTok

    # 1 Несколько целевых спецификаций

     let sepia = GPUImageSepiaFilter() videoCamera?.addTarget(sepia) sepia.addTarget(self.view) videoCamera?.addTarget(self.view) // <-- This is wrong and produces the flickering videoCamera?.startCameraCapture() 

    Два источника пытаются отобразить одну и ту же точку зрения. Делает вещи мерцающими …

    Часть первая решена. Далее: Почему в OpenTok ничего не пропало? Чтобы найти причину этого, я решил начать с «рабочей» версии Objective-C .

    # 2 – исходная кодовая база Objective-C

    Версия orignal Objective-C не имеет ожидаемой функциональности. Публикация GPUImageVideoCamera для подписчика OpenTok работает, но фильтрация не задействована. И это ваше основное требование. Дело в том, что добавление фильтров не так тривиально, как ожидалось, из-за различий в форматах изображений и различных механизмах, как делать асинхронное программирование.

    Итак, причина №2 , почему ваш код не работает должным образом: ваша ссылочная база кода для вашей работы портирования неверна. Он не позволяет размещать фильтры графического процессора между конвейером Publish-Subscriber.

    Работающая реализация Objective-C

    Я изменил версию Objective-C. Текущие результаты выглядят так:

    [! [введите описание изображения здесь] [1]] [1]

    Он работает плавно.

    Окончательные шаги

    Это полный код для пользовательского издателя Tok . В основном это исходный код ( TokBoxGPUImagePublisher ) из [ https://github.com/JayTokBox/TokBoxGPUImage/blob/master/TokBoxGPUImage/ViewController.m%5D%5B2%5D со следующими заметными изменениями:

    OTVideoFrame получает экземпляр с новым форматом

      ... format = [[OTVideoFormat alloc] init]; format.pixelFormat = OTPixelFormatARGB; format.bytesPerRow = [@[@(imageWidth * 4)] mutableCopy]; format.imageWidth = imageWidth; format.imageHeight = imageHeight; videoFrame = [[OTVideoFrame alloc] initWithFormat: format]; ... 

    Заменить механизм обратного вызова WillOutputSampleBuffer

    Этот обратный вызов срабатывает только тогда, когда буферы выборок, поступающие непосредственно из GPUImageVideoCamera , готовы и НЕ от ваших пользовательских фильтров. GPUImageFilters не предоставляют такой механизм обратного вызова / делегирования. Вот почему мы GPUImageRawDataOutput между ними и запрашиваем готовые изображения. Этот конвейер реализован в методе initCapture и выглядит так:

      videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack]; videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; sepiaImageFilter = [[GPUImageSepiaFilter alloc] init]; [videoCamera addTarget:sepiaImageFilter]; // Create rawOut CGSize size = CGSizeMake(imageWidth, imageHeight); rawOut = [[GPUImageRawDataOutput alloc] initWithImageSize:size resultsInBGRAFormat:YES]; // Filter into rawOut [sepiaImageFilter addTarget:rawOut]; // Handle filtered images // We need a weak reference here to avoid a strong reference cycle. __weak GPUImageRawDataOutput* weakRawOut = self->rawOut; __weak OTVideoFrame* weakVideoFrame = self->videoFrame; __weak id<OTVideoCaptureConsumer> weakVideoCaptureConsumer = self.videoCaptureConsumer; // [rawOut setNewFrameAvailableBlock:^{ [weakRawOut lockFramebufferForReading]; // GLubyte is an uint8_t GLubyte* outputBytes = [weakRawOut rawBytesForImage]; // About the video formats used by OTVideoFrame // -------------------------------------------- // Both YUV video formats (i420, NV12) have the (for us) following important properties: // // - Two planes // - 8 bit Y plane // - 8 bit 2x2 subsampled U and V planes (1/4 the pixels of the Y plane) // --> 12 bits per pixel // // Further reading: www.fourcc.org/yuv.php // [weakVideoFrame clearPlanes]; [weakVideoFrame.planes addPointer: outputBytes]; [weakVideoCaptureConsumer consumeFrame: weakVideoFrame]; [weakRawOut unlockFramebufferAfterReading]; }]; [videoCamera addTarget:self.view]; [videoCamera startCameraCapture]; ,  videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack]; videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; sepiaImageFilter = [[GPUImageSepiaFilter alloc] init]; [videoCamera addTarget:sepiaImageFilter]; // Create rawOut CGSize size = CGSizeMake(imageWidth, imageHeight); rawOut = [[GPUImageRawDataOutput alloc] initWithImageSize:size resultsInBGRAFormat:YES]; // Filter into rawOut [sepiaImageFilter addTarget:rawOut]; // Handle filtered images // We need a weak reference here to avoid a strong reference cycle. __weak GPUImageRawDataOutput* weakRawOut = self->rawOut; __weak OTVideoFrame* weakVideoFrame = self->videoFrame; __weak id<OTVideoCaptureConsumer> weakVideoCaptureConsumer = self.videoCaptureConsumer; // [rawOut setNewFrameAvailableBlock:^{ [weakRawOut lockFramebufferForReading]; // GLubyte is an uint8_t GLubyte* outputBytes = [weakRawOut rawBytesForImage]; // About the video formats used by OTVideoFrame // -------------------------------------------- // Both YUV video formats (i420, NV12) have the (for us) following important properties: // // - Two planes // - 8 bit Y plane // - 8 bit 2x2 subsampled U and V planes (1/4 the pixels of the Y plane) // --> 12 bits per pixel // // Further reading: www.fourcc.org/yuv.php // [weakVideoFrame clearPlanes]; [weakVideoFrame.planes addPointer: outputBytes]; [weakVideoCaptureConsumer consumeFrame: weakVideoFrame]; [weakRawOut unlockFramebufferAfterReading]; }]; [videoCamera addTarget:self.view]; [videoCamera startCameraCapture]; 

    Весь код (действительно важная вещь – initCapture)

     // // TokBoxGPUImagePublisher.m // TokBoxGPUImage // // Created by Jaideep Shah on 9/5/14. // Copyright (c) 2014 Jaideep Shah. All rights reserved. // #import "TokBoxGPUImagePublisher.h" #import "GPUImage.h" static size_t imageHeight = 480; static size_t imageWidth = 640; @interface TokBoxGPUImagePublisher() <GPUImageVideoCameraDelegate, OTVideoCapture> { GPUImageVideoCamera *videoCamera; GPUImageSepiaFilter *sepiaImageFilter; OTVideoFrame* videoFrame; GPUImageRawDataOutput* rawOut; OTVideoFormat* format; } @end @implementation TokBoxGPUImagePublisher @synthesize videoCaptureConsumer ; // In OTVideoCapture protocol - (id)initWithDelegate:(id<OTPublisherDelegate>)delegate name:(NSString*)name { self = [super initWithDelegate:delegate name:name]; if (self) { self.view = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; [self setVideoCapture:self]; format = [[OTVideoFormat alloc] init]; format.pixelFormat = OTPixelFormatARGB; format.bytesPerRow = [@[@(imageWidth * 4)] mutableCopy]; format.imageWidth = imageWidth; format.imageHeight = imageHeight; videoFrame = [[OTVideoFrame alloc] initWithFormat: format]; } return self; } #pragma mark GPUImageVideoCameraDelegate - (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer { CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer, 0); [videoFrame clearPlanes]; for (int i = 0; i < CVPixelBufferGetPlaneCount(imageBuffer); i++) { [videoFrame.planes addPointer:CVPixelBufferGetBaseAddressOfPlane(imageBuffer, i)]; } videoFrame.orientation = OTVideoOrientationLeft; [self.videoCaptureConsumer consumeFrame:videoFrame]; CVPixelBufferUnlockBaseAddress(imageBuffer, 0); } #pragma mark OTVideoCapture - (void) initCapture { videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack]; videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; sepiaImageFilter = [[GPUImageSepiaFilter alloc] init]; [videoCamera addTarget:sepiaImageFilter]; // Create rawOut CGSize size = CGSizeMake(imageWidth, imageHeight); rawOut = [[GPUImageRawDataOutput alloc] initWithImageSize:size resultsInBGRAFormat:YES]; // Filter into rawOut [sepiaImageFilter addTarget:rawOut]; // Handle filtered images // We need a weak reference here to avoid a strong reference cycle. __weak GPUImageRawDataOutput* weakRawOut = self->rawOut; __weak OTVideoFrame* weakVideoFrame = self->videoFrame; __weak id<OTVideoCaptureConsumer> weakVideoCaptureConsumer = self.videoCaptureConsumer; // [rawOut setNewFrameAvailableBlock:^{ [weakRawOut lockFramebufferForReading]; // GLubyte is an uint8_t GLubyte* outputBytes = [weakRawOut rawBytesForImage]; // About the video formats used by OTVideoFrame // -------------------------------------------- // Both YUV video formats (i420, NV12) have the (for us) following important properties: // // - Two planes // - 8 bit Y plane // - 8 bit 2x2 subsampled U and V planes (1/4 the pixels of the Y plane) // --> 12 bits per pixel // // Further reading: www.fourcc.org/yuv.php // [weakVideoFrame clearPlanes]; [weakVideoFrame.planes addPointer: outputBytes]; [weakVideoCaptureConsumer consumeFrame: weakVideoFrame]; [weakRawOut unlockFramebufferAfterReading]; }]; [videoCamera addTarget:self.view]; [videoCamera startCameraCapture]; } - (void)releaseCapture { videoCamera.delegate = nil; videoCamera = nil; } - (int32_t) startCapture { return 0; } - (int32_t) stopCapture { return 0; } - (BOOL) isCaptureStarted { return YES; } - (int32_t)captureSettings:(OTVideoFormat*)videoFormat { videoFormat.pixelFormat = OTPixelFormatNV12; videoFormat.imageWidth = imageWidth; videoFormat.imageHeight = imageHeight; return 0; } @end , // // TokBoxGPUImagePublisher.m // TokBoxGPUImage // // Created by Jaideep Shah on 9/5/14. // Copyright (c) 2014 Jaideep Shah. All rights reserved. // #import "TokBoxGPUImagePublisher.h" #import "GPUImage.h" static size_t imageHeight = 480; static size_t imageWidth = 640; @interface TokBoxGPUImagePublisher() <GPUImageVideoCameraDelegate, OTVideoCapture> { GPUImageVideoCamera *videoCamera; GPUImageSepiaFilter *sepiaImageFilter; OTVideoFrame* videoFrame; GPUImageRawDataOutput* rawOut; OTVideoFormat* format; } @end @implementation TokBoxGPUImagePublisher @synthesize videoCaptureConsumer ; // In OTVideoCapture protocol - (id)initWithDelegate:(id<OTPublisherDelegate>)delegate name:(NSString*)name { self = [super initWithDelegate:delegate name:name]; if (self) { self.view = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; [self setVideoCapture:self]; format = [[OTVideoFormat alloc] init]; format.pixelFormat = OTPixelFormatARGB; format.bytesPerRow = [@[@(imageWidth * 4)] mutableCopy]; format.imageWidth = imageWidth; format.imageHeight = imageHeight; videoFrame = [[OTVideoFrame alloc] initWithFormat: format]; } return self; } #pragma mark GPUImageVideoCameraDelegate - (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer { CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer, 0); [videoFrame clearPlanes]; for (int i = 0; i < CVPixelBufferGetPlaneCount(imageBuffer); i++) { [videoFrame.planes addPointer:CVPixelBufferGetBaseAddressOfPlane(imageBuffer, i)]; } videoFrame.orientation = OTVideoOrientationLeft; [self.videoCaptureConsumer consumeFrame:videoFrame]; CVPixelBufferUnlockBaseAddress(imageBuffer, 0); } #pragma mark OTVideoCapture - (void) initCapture { videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack]; videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; sepiaImageFilter = [[GPUImageSepiaFilter alloc] init]; [videoCamera addTarget:sepiaImageFilter]; // Create rawOut CGSize size = CGSizeMake(imageWidth, imageHeight); rawOut = [[GPUImageRawDataOutput alloc] initWithImageSize:size resultsInBGRAFormat:YES]; // Filter into rawOut [sepiaImageFilter addTarget:rawOut]; // Handle filtered images // We need a weak reference here to avoid a strong reference cycle. __weak GPUImageRawDataOutput* weakRawOut = self->rawOut; __weak OTVideoFrame* weakVideoFrame = self->videoFrame; __weak id<OTVideoCaptureConsumer> weakVideoCaptureConsumer = self.videoCaptureConsumer; // [rawOut setNewFrameAvailableBlock:^{ [weakRawOut lockFramebufferForReading]; // GLubyte is an uint8_t GLubyte* outputBytes = [weakRawOut rawBytesForImage]; // About the video formats used by OTVideoFrame // -------------------------------------------- // Both YUV video formats (i420, NV12) have the (for us) following important properties: // // - Two planes // - 8 bit Y plane // - 8 bit 2x2 subsampled U and V planes (1/4 the pixels of the Y plane) // --> 12 bits per pixel // // Further reading: www.fourcc.org/yuv.php // [weakVideoFrame clearPlanes]; [weakVideoFrame.planes addPointer: outputBytes]; [weakVideoCaptureConsumer consumeFrame: weakVideoFrame]; [weakRawOut unlockFramebufferAfterReading]; }]; [videoCamera addTarget:self.view]; [videoCamera startCameraCapture]; } - (void)releaseCapture { videoCamera.delegate = nil; videoCamera = nil; } - (int32_t) startCapture { return 0; } - (int32_t) stopCapture { return 0; } - (BOOL) isCaptureStarted { return YES; } - (int32_t)captureSettings:(OTVideoFormat*)videoFormat { videoFormat.pixelFormat = OTPixelFormatNV12; videoFormat.imageWidth = imageWidth; videoFormat.imageHeight = imageHeight; return 0; } @end 
    Давайте будем гением компьютера.