2015-10-12 10 views
5

Sto tentando di utilizzare una combinazione di GPUImage e AVVideoCompositing per implementare un filtro chroma-key live tra due video. Questa operazione in modo ingenuo utilizzando CIImageimageFromCVPixelBuffer in CGImage in GPUImage su CGImage a CIImage a CVPixelBuffer, è altamente inefficiente e causa problemi di memoria.Utilizzo di GPUImage con AVVideoCompositing

Ho notato che ci sono oggetti texture, destinazioni di rendering e frame buffer nel framework GPUImage. Speravo di essere in grado di sfruttare CVOpenGLESTextureCacheCreateTextureFromImage in iOS per mantenere tutto sulla GPU.

Non credo che sto abbastanza comprensione funzionamento interno del quadro, perché ho pensato che potrei creare una catena di filtro su un oggetto GPUImageTextureInput e quindi ottenere il filtro del renderTarget, che è un CVPixelBufferRef. Lo renderTarget di seguito è sempre nullo e chiamare imageFromCurrentFrameBuffer mi darà una casella grigia, che non è la mia immagine.

Si prega di notare che l'esempio seguente non è chroma-key, ma un semplice filtro di luminosità su un singolo video per provare e dimostrare il concetto.

@implementation MyCustomCompositor : NSObject <AVVideoCompositing> 

- (instancetype)init 
{ 
    self = [super init]; 
    if (self) { 
     CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, [GPUImageContext sharedImageProcessingContext].context, NULL, &_textureCache); 
    } 
    return self; 
} 

- (NSDictionary<NSString *,id> *)requiredPixelBufferAttributesForRenderContext 
{ 
    return @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @[@(kCVPixelFormatType_32BGRA)], 
      (NSString *)kCVPixelBufferOpenGLCompatibilityKey : @YES}; 
} 

- (NSDictionary<NSString *,id> *)sourcePixelBufferAttributes 
{ 
    return @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @[@(kCVPixelFormatType_32BGRA)], 
      (NSString *)kCVPixelBufferOpenGLCompatibilityKey : @YES}; 
} 

- (void)startVideoCompositionRequest:(AVAsynchronousVideoCompositionRequest *)asyncVideoCompositionRequest 
{ 
    @autoreleasepool { 
     CVPixelBufferRef mePixelBuffer = [asyncVideoCompositionRequest sourceFrameByTrackID:200]; 
     CVPixelBufferLockBaseAddress(mePixelBuffer, kCVPixelBufferLock_ReadOnly); 

     CVOpenGLESTextureRef meTextureRef = NULL; 
     size_t width = CVPixelBufferGetWidth(mePixelBuffer); 
     size_t height = CVPixelBufferGetHeight(mePixelBuffer); 
     CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCache, mePixelBuffer, NULL, GL_TEXTURE_2D, GL_BGRA, (int)width, (int)height, GL_BGRA, GL_UNSIGNED_BYTE, 0, &meTextureRef); 

     GPUImageTextureInput *meTextureInput = [[GPUImageTextureInput alloc] initWithTexture:CVOpenGLESTextureGetName(meTextureRef) size:CGSizeMake(width, height)]; 

     GPUImageBrightnessFilter *filter = [[GPUImageBrightnessFilter alloc] init]; 
     filter.brightness = 0.5; 
     [meTextureInput addTarget:filter]; 

     [filter setFrameProcessingCompletionBlock:^(GPUImageOutput *imageOutput, CMTime time) { 
      [asyncVideoCompositionRequest finishWithComposedVideoFrame:((GPUImageBrightnessFilter *)imageOutput).renderTarget]; 
     }]; 

     [meTextureInput processTextureWithFrameTime:kCMTimeZero]; 

     CFRelease(meTextureRef); 
     CVOpenGLESTextureCacheFlush(_textureCache, 0); 

     CVPixelBufferUnlockBaseAddress(mePixelBuffer, kCVPixelBufferLock_ReadOnly); 
    } 
} 

non sto usando i GPUMovieWriter o video di API in GPUImage perché ho bisogno di un controllo più granulare di mia composizione. La composizione può essere composta da più istruzioni chroma-key che fanno riferimento a un diverso overlay video verde in diversi intervalli di tempo e mi sembra che le API del film in GPUImage siano limitate al filtraggio di un intero file video. Ho anche bisogno delle capacità della composizione per manipolare tracce e mix audio.

Ho provato a capovolgerlo facendo tutto questo in GL con shader personalizzati, ma ho pensato di sfruttare le strutture esistenti che sembrano fare ciò che sto cercando di fare.

risposta

0

Ho scritto una classe denominata GPUImageFrameInput modificata da GPUImageMovie. CVPixelBufferRef è il suo input. Si va così:

  1. involucro sourcePixelBuffer a GPUImageFrameOutput
  2. creare GPUImageFrameInput avvolto il destinationPixelBuffer
  3. completare la catena dei filtri
  4. fatto

Ecco il codice della chiave.

// output 
// wrap the sourcePixelBuffer from the request 
// it's modified from GPUImageVideoCamera 
@interface GPUImageFrameOutput() { 

} 
-(void)processSourcePixelBuffer:(CVPixelBufferRef)pixelBuffer withSampleTime:(CMTime)currentSampleTime; 
@end 

@implement GPUImageFrameOutput() 

-(void)processSourcePixelBuffer:(CVPixelBufferRef)pixelBuffer withSampleTime:(CMTime)currentSampleTime { 
    runSynchronouslyOnVideoProcessingQueue(^{ 
     [GPUImageContext useImageProcessingContext]; 

     int bufferHeight = (int) CVPixelBufferGetHeight(movieFrame); 
     int bufferWidth = (int) CVPixelBufferGetWidth(movieFrame); 

     if (bufferHeight == 0 || bufferWidth == 0) { 
      return; 
     } 

     // almost same as 
     // [GPUImageVideoCamera processVideoSampleBuffer:] 
     // 
} 

@end 

// input 
// wrap the destinationPixelBuffer 
@interface GPUImageFrameInput() { 
    CVPixelBufferRef targetBuffer; 
    // ... others 
} 
@end 

- (void)setPixelBuffer:(CVPixelBufferRef)buffer{ 
    targetBuffer = buffer; 
} 

- (CVOpenGLESTextureRef)createDataFBO { 
    if (!movieFramebuffer) { 
     glActiveTexture(GL_TEXTURE1); 
     glGenFramebuffers(1, &movieFramebuffer); 
     glBindFramebuffer(GL_FRAMEBUFFER, movieFramebuffer); 
    } 

    glBindFramebuffer(GL_FRAMEBUFFER, movieFramebuffer); 
    glViewport(0, 0, (int)_videoSize.width, (int)_videoSize.height); 

    CVOpenGLESTextureRef renderTexture = nil; 

    if ([GPUImageContext supportsFastTextureUpload]) { 
     CVBufferSetAttachment(targetBuffer, kCVImageBufferColorPrimariesKey, kCVImageBufferColorPrimaries_ITU_R_709_2, kCVAttachmentMode_ShouldPropagate); 
     CVBufferSetAttachment(targetBuffer, kCVImageBufferYCbCrMatrixKey, kCVImageBufferYCbCrMatrix_ITU_R_601_4, kCVAttachmentMode_ShouldPropagate); 
     CVBufferSetAttachment(targetBuffer, kCVImageBufferTransferFunctionKey, kCVImageBufferTransferFunction_ITU_R_709_2, kCVAttachmentMode_ShouldPropagate); 

     CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault, [[GPUImageContext sharedImageProcessingContext] coreVideoTextureCache], 
                targetBuffer, 
                NULL, // texture attributes 
                GL_TEXTURE_2D, 
                GL_RGBA, // opengl format 
                (int)CVPixelBufferGetWidth(targetBuffer), 
                (int)CVPixelBufferGetHeight(targetBuffer), 
                GL_BGRA, // native iOS format 
                GL_UNSIGNED_BYTE, 
                0, 
                &renderTexture); 

     glBindTexture(CVOpenGLESTextureGetTarget(renderTexture), CVOpenGLESTextureGetName(renderTexture)); 
     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 

     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture), 0); 
    } 
    else 
    { 
    //... 
    } 
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 
    NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @"Incomplete filter FBO: %d", status); 

    return renderTexture; 
} 

quindi è possibile creare la catena gpuimage come altri

[frameInput setPixelBuffer:destinationPixelBuffer]; 

for (...) { 
    GPUImageMovieFrameOutput *output ... 
    [output addTarget:filter atTextureLocation:index]; 
} 
[filter addTarget:frameInput]; 

spero utile!

+0

Darò uno scatto! Grazie! – dhenke

+0

Sono un po 'confuso da questo. Sembra che il codice sia quasi uscito da GPUImageMovie, che va bene, ma come adattarlo a una sottoclasse GPUImageOutput? Come si verifica la chiamata a createDataFBO? O mi mancano alcuni pezzi su come organizzare questa sottoclasse? Presumo che finirei per chiamare createDataFBO e assegnando quel riferimento a qualcosa? – dhenke

+0

scusate. Ho dimenticato di dare l'output. : P Lasciami aggiustarlo. – econi