Самый эффективный способ изменить содержимое CMSampleBuffer

Я хочу изменить содержимое CMSampleBuffer, а затем записать его в файл с помощью AVAssetWriter/AVAssetWriterInput.

Я делаю это, создавая контекст растрового изображения Core Graphics и затем рисуя в нем, но это слишком медленно. В частности, мне нужно нарисовать изображение в буфере.

Итак, можно ли дать какой-то намек или предложение о том, как сделать это более эффективно?

Я думал об использовании OpenGL для достижения этой цели, то есть сначала создать текстуру A из CMSampleBuffer. Затем визуализируйте текстуру B, созданную из изображения, которое я хочу нарисовать, в текстуру A, затем извлеките данные, поддерживающие текстуру A, из OpenGL и, наконец, передайте эти данные в AVAssetWriter/AVAssetWriterInput. Но в документах говорится, что передача текстурных данных с графического процессора обратно на процессор довольно дорогая.

Итак, какие-либо предложения о том, как подойти к этому?

заранее спасибо


person Russian    schedule 11.01.2011    source источник


Ответы (1)


OpenGL, пожалуй, лучший вариант. Однако может быть немного эффективнее рендерить во внеэкранный буфер кадра, а не в текстуру.

Чтобы извлечь текстуру из буфера образцов:

// Note the caller is responsible for calling glDeleteTextures on the return value.
- (GLuint)textureFromSampleBuffer:(CMSampleBufferRef)sampleBuffer {
    GLuint texture = 0;

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    int width = CVPixelBufferGetWidth(pixelBuffer);
    int height = CVPixelBufferGetHeight(pixelBuffer);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, CVPixelBufferGetBaseAddress(pixelBuffer));
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

    return texture;
}

Чтобы обработать текстуру через OpenGL, вы можете сделать что-то вроде этого:

// This function exists to free the malloced data when the CGDataProviderRef is
// eventually freed.
void dataProviderFreeData(void *info, const void *data, size_t size){
    free((void *)data);
}

// Returns an autoreleased CGImageRef.
- (CGImageRef)processTexture:(GLuint)texture width:(int)width height:(int)height {
    CGImageRef newImage = NULL;

    // Set up framebuffer and renderbuffer.
    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    GLuint colorRenderbuffer;
    glGenRenderbuffers(1, &colorRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, width, height);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);

    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"Failed to create OpenGL frame buffer: %x", status);
    } else {
        glViewport(0, 0, width, height);
        glClearColor(0.0,0.0,0.0,1.0);
        glClear(GL_COLOR_BUFFER_BIT);

        // Do whatever is necessary to actually draw the texture to the framebuffer
        [self renderTextureToCurrentFrameBuffer:texture];

        // Read the pixels out of the framebuffer
        void *data = malloc(width * height * 4);
        glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);

        // Convert the data to a CGImageRef. Note that CGDataProviderRef takes
        // ownership of our malloced data buffer, and the CGImageRef internally
        // retains the CGDataProviderRef. Hence the callback above, to free the data
        // buffer when the provider is finally released.
        CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, data, width * height * 4, dataProviderFreeData);
        CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
        newImage = CGImageCreate(width, height, 8, 32, width*4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast, dataProvider, NULL, true, kCGRenderingIntentDefault);
        CFRelease(dataProvider);
        CGColorSpaceRelease(colorspace);

        // Autorelease the CGImageRef
        newImage = (CGImageRef)[NSMakeCollectable(newImage) autorelease];
    }

    // Clean up the framebuffer and renderbuffer.
    glDeleteRenderbuffers(1, &colorRenderbuffer);
    glDeleteFramebuffers(1, &framebuffer);

    return newImage;
}
person Anomie    schedule 19.04.2011
comment
Здравствуйте, Anomie, Можете ли вы обновить это с помощью Metal, поскольку OpenGL устарел. - person AsifHabib; 20.03.2020
comment
@AsifHabib: Извините, я ничего не делал с iOS с 2012 года. Возможно, вам лучше опубликовать новый вопрос. - person Anomie; 20.03.2020