2f4bb5d545
This code needs a FBO to bind the texture. Fixes invalid results when running under simulator. PiperOrigin-RevId: 499241867
177 lines
6.8 KiB
C++
177 lines
6.8 KiB
C++
#include "mediapipe/gpu/gpu_buffer_storage_cv_pixel_buffer.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "mediapipe/gpu/gl_context.h"
|
|
#include "mediapipe/gpu/gpu_buffer_storage_image_frame.h"
|
|
#include "mediapipe/objc/util.h"
|
|
|
|
namespace mediapipe {
|
|
|
|
#if TARGET_OS_OSX
|
|
typedef CVOpenGLTextureRef CVTextureType;
|
|
#else
|
|
typedef CVOpenGLESTextureRef CVTextureType;
|
|
#endif // TARGET_OS_OSX
|
|
|
|
GpuBufferStorageCvPixelBuffer::GpuBufferStorageCvPixelBuffer(
|
|
int width, int height, GpuBufferFormat format) {
|
|
OSType cv_format = CVPixelFormatForGpuBufferFormat(format);
|
|
CHECK_NE(cv_format, -1) << "unsupported pixel format";
|
|
CVPixelBufferRef buffer;
|
|
CVReturn err =
|
|
CreateCVPixelBufferWithoutPool(width, height, cv_format, &buffer);
|
|
CHECK(!err) << "Error creating pixel buffer: " << err;
|
|
adopt(buffer);
|
|
}
|
|
|
|
GlTextureView GpuBufferStorageCvPixelBuffer::GetTexture(
|
|
int plane, GlTextureView::DoneWritingFn done_writing) const {
|
|
CVReturn err;
|
|
auto gl_context = GlContext::GetCurrent();
|
|
CHECK(gl_context);
|
|
#if TARGET_OS_OSX
|
|
CVTextureType cv_texture_temp;
|
|
err = CVOpenGLTextureCacheCreateTextureFromImage(
|
|
kCFAllocatorDefault, gl_context->cv_texture_cache(), **this, NULL,
|
|
&cv_texture_temp);
|
|
CHECK(cv_texture_temp && !err)
|
|
<< "CVOpenGLTextureCacheCreateTextureFromImage failed: " << err;
|
|
CFHolder<CVTextureType> cv_texture;
|
|
cv_texture.adopt(cv_texture_temp);
|
|
return GlTextureView(
|
|
gl_context.get(), CVOpenGLTextureGetTarget(*cv_texture),
|
|
CVOpenGLTextureGetName(*cv_texture), width(), height(), *this, plane,
|
|
[cv_texture](mediapipe::GlTextureView&) { /* only retains cv_texture */ },
|
|
done_writing);
|
|
#else
|
|
const GlTextureInfo info = GlTextureInfoForGpuBufferFormat(
|
|
format(), plane, gl_context->GetGlVersion());
|
|
CVTextureType cv_texture_temp;
|
|
err = CVOpenGLESTextureCacheCreateTextureFromImage(
|
|
kCFAllocatorDefault, gl_context->cv_texture_cache(), **this, NULL,
|
|
GL_TEXTURE_2D, info.gl_internal_format, width() / info.downscale,
|
|
height() / info.downscale, info.gl_format, info.gl_type, plane,
|
|
&cv_texture_temp);
|
|
CHECK(cv_texture_temp && !err)
|
|
<< "CVOpenGLESTextureCacheCreateTextureFromImage failed: " << err;
|
|
CFHolder<CVTextureType> cv_texture;
|
|
cv_texture.adopt(cv_texture_temp);
|
|
return GlTextureView(
|
|
gl_context.get(), CVOpenGLESTextureGetTarget(*cv_texture),
|
|
CVOpenGLESTextureGetName(*cv_texture), width(), height(), plane,
|
|
[cv_texture](mediapipe::GlTextureView&) { /* only retains cv_texture */ },
|
|
done_writing);
|
|
#endif // TARGET_OS_OSX
|
|
}
|
|
|
|
GlTextureView GpuBufferStorageCvPixelBuffer::GetReadView(
|
|
internal::types<GlTextureView>, int plane) const {
|
|
return GetTexture(plane, nullptr);
|
|
}
|
|
|
|
#if TARGET_IPHONE_SIMULATOR
|
|
static void ViewDoneWritingSimulatorWorkaround(CVPixelBufferRef pixel_buffer,
|
|
const GlTextureView& view) {
|
|
CHECK(pixel_buffer);
|
|
auto ctx = GlContext::GetCurrent().get();
|
|
if (!ctx) ctx = view.gl_context();
|
|
ctx->Run([pixel_buffer, &view, ctx] {
|
|
CVReturn err = CVPixelBufferLockBaseAddress(pixel_buffer, 0);
|
|
CHECK(err == kCVReturnSuccess)
|
|
<< "CVPixelBufferLockBaseAddress failed: " << err;
|
|
OSType pixel_format = CVPixelBufferGetPixelFormatType(pixel_buffer);
|
|
size_t bytes_per_row = CVPixelBufferGetBytesPerRow(pixel_buffer);
|
|
uint8_t* pixel_ptr =
|
|
static_cast<uint8_t*>(CVPixelBufferGetBaseAddress(pixel_buffer));
|
|
if (pixel_format == kCVPixelFormatType_32BGRA) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, kUtilityFramebuffer.Get(*ctx));
|
|
glViewport(0, 0, view.width(), view.height());
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
view.target(), view.name(), 0);
|
|
|
|
size_t contiguous_bytes_per_row = view.width() * 4;
|
|
if (bytes_per_row == contiguous_bytes_per_row) {
|
|
glReadPixels(0, 0, view.width(), view.height(), GL_BGRA,
|
|
GL_UNSIGNED_BYTE, pixel_ptr);
|
|
} else {
|
|
// TODO: use GL_PACK settings for row length. We can expect
|
|
// GLES 3.0 on iOS now.
|
|
std::vector<uint8_t> contiguous_buffer(contiguous_bytes_per_row *
|
|
view.height());
|
|
uint8_t* temp_ptr = contiguous_buffer.data();
|
|
glReadPixels(0, 0, view.width(), view.height(), GL_BGRA,
|
|
GL_UNSIGNED_BYTE, temp_ptr);
|
|
for (int i = 0; i < view.height(); ++i) {
|
|
memcpy(pixel_ptr, temp_ptr, contiguous_bytes_per_row);
|
|
temp_ptr += contiguous_bytes_per_row;
|
|
pixel_ptr += bytes_per_row;
|
|
}
|
|
}
|
|
// TODO: restore previous framebuffer?
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
view.target(), 0, 0);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
} else {
|
|
LOG(ERROR) << "unsupported pixel format: " << pixel_format;
|
|
}
|
|
err = CVPixelBufferUnlockBaseAddress(pixel_buffer, 0);
|
|
CHECK(err == kCVReturnSuccess)
|
|
<< "CVPixelBufferUnlockBaseAddress failed: " << err;
|
|
});
|
|
}
|
|
#endif // TARGET_IPHONE_SIMULATOR
|
|
|
|
GlTextureView GpuBufferStorageCvPixelBuffer::GetWriteView(
|
|
internal::types<GlTextureView>, int plane) {
|
|
return GetTexture(plane,
|
|
#if TARGET_IPHONE_SIMULATOR
|
|
[pixel_buffer = CFHolder<CVPixelBufferRef>(*this)](
|
|
const mediapipe::GlTextureView& view) {
|
|
ViewDoneWritingSimulatorWorkaround(*pixel_buffer, view);
|
|
}
|
|
#else
|
|
nullptr
|
|
#endif // TARGET_IPHONE_SIMULATOR
|
|
);
|
|
}
|
|
|
|
std::shared_ptr<const ImageFrame> GpuBufferStorageCvPixelBuffer::GetReadView(
|
|
internal::types<ImageFrame>) const {
|
|
return CreateImageFrameForCVPixelBuffer(**this);
|
|
}
|
|
std::shared_ptr<ImageFrame> GpuBufferStorageCvPixelBuffer::GetWriteView(
|
|
internal::types<ImageFrame>) {
|
|
return CreateImageFrameForCVPixelBuffer(**this);
|
|
}
|
|
|
|
static std::shared_ptr<GpuBufferStorageCvPixelBuffer> ConvertFromImageFrame(
|
|
std::shared_ptr<GpuBufferStorageImageFrame> frame) {
|
|
auto status_or_buffer =
|
|
CreateCVPixelBufferForImageFrame(frame->image_frame());
|
|
CHECK(status_or_buffer.ok());
|
|
return std::make_shared<GpuBufferStorageCvPixelBuffer>(
|
|
std::move(status_or_buffer).value());
|
|
}
|
|
|
|
static auto kConverterFromImageFrameRegistration =
|
|
internal::GpuBufferStorageRegistry::Get()
|
|
.RegisterConverter<GpuBufferStorageImageFrame,
|
|
GpuBufferStorageCvPixelBuffer>(
|
|
ConvertFromImageFrame);
|
|
|
|
namespace internal {
|
|
std::shared_ptr<internal::GpuBufferStorage> AsGpuBufferStorage(
|
|
CFHolder<CVPixelBufferRef> pixel_buffer) {
|
|
return std::make_shared<GpuBufferStorageCvPixelBuffer>(
|
|
std::move(pixel_buffer));
|
|
}
|
|
|
|
std::shared_ptr<internal::GpuBufferStorage> AsGpuBufferStorage(
|
|
CVPixelBufferRef pixel_buffer) {
|
|
return std::make_shared<GpuBufferStorageCvPixelBuffer>(pixel_buffer);
|
|
}
|
|
} // namespace internal
|
|
|
|
} // namespace mediapipe
|