blob: a7c94738698ad370f1a71af2869804a64c4d68b6 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "texture_handler.h"
#include <cassert>
namespace camera_windows {
TextureHandler::~TextureHandler() {
// Texture might still be processed while destructor is called.
// Lock mutex for safe destruction
const std::lock_guard<std::mutex> lock(buffer_mutex_);
if (texture_registrar_ && texture_id_ > 0) {
texture_registrar_->UnregisterTexture(texture_id_);
}
texture_id_ = -1;
texture_ = nullptr;
texture_registrar_ = nullptr;
}
int64_t TextureHandler::RegisterTexture() {
if (!texture_registrar_) {
return -1;
}
// Create flutter desktop pixelbuffer texture;
texture_ =
std::make_unique<flutter::TextureVariant>(flutter::PixelBufferTexture(
[this](size_t width,
size_t height) -> const FlutterDesktopPixelBuffer* {
return this->ConvertPixelBufferForFlutter(width, height);
}));
texture_id_ = texture_registrar_->RegisterTexture(texture_.get());
return texture_id_;
}
bool TextureHandler::UpdateBuffer(uint8_t* data, uint32_t data_length) {
// Scoped lock guard.
{
const std::lock_guard<std::mutex> lock(buffer_mutex_);
if (!TextureRegistered()) {
return false;
}
if (source_buffer_.size() != data_length) {
// Update source buffer size.
source_buffer_.resize(data_length);
}
std::copy(data, data + data_length, source_buffer_.data());
}
OnBufferUpdated();
return true;
};
// Marks texture frame available after buffer is updated.
void TextureHandler::OnBufferUpdated() {
if (TextureRegistered()) {
texture_registrar_->MarkTextureFrameAvailable(texture_id_);
}
}
const FlutterDesktopPixelBuffer* TextureHandler::ConvertPixelBufferForFlutter(
size_t target_width, size_t target_height) {
// TODO: optimize image processing size by adjusting capture size
// dynamically to match target_width and target_height.
// If target size changes, create new media type for preview and set new
// target framesize to MF_MT_FRAME_SIZE attribute.
// Size should be kept inside requested resolution preset.
// Update output media type with IMFCaptureSink2::SetOutputMediaType method
// call and implement IMFCaptureEngineOnSampleCallback2::OnSynchronizedEvent
// to detect size changes.
// Lock buffer mutex to protect texture processing
std::unique_lock<std::mutex> buffer_lock(buffer_mutex_);
if (!TextureRegistered()) {
return nullptr;
}
const uint32_t bytes_per_pixel = 4;
const uint32_t pixels_total = preview_frame_width_ * preview_frame_height_;
const uint32_t data_size = pixels_total * bytes_per_pixel;
if (data_size > 0 && source_buffer_.size() == data_size) {
if (dest_buffer_.size() != data_size) {
dest_buffer_.resize(data_size);
}
// Map buffers to structs for easier conversion.
MFVideoFormatRGB32Pixel* src =
reinterpret_cast<MFVideoFormatRGB32Pixel*>(source_buffer_.data());
FlutterDesktopPixel* dst =
reinterpret_cast<FlutterDesktopPixel*>(dest_buffer_.data());
for (uint32_t y = 0; y < preview_frame_height_; y++) {
for (uint32_t x = 0; x < preview_frame_width_; x++) {
uint32_t sp = (y * preview_frame_width_) + x;
if (mirror_preview_) {
// Software mirror mode.
// IMFCapturePreviewSink also has the SetMirrorState setting,
// but if enabled, samples will not be processed.
// Calculates mirrored pixel position.
uint32_t tp =
(y * preview_frame_width_) + ((preview_frame_width_ - 1) - x);
dst[tp].r = src[sp].r;
dst[tp].g = src[sp].g;
dst[tp].b = src[sp].b;
dst[tp].a = 255;
} else {
dst[sp].r = src[sp].r;
dst[sp].g = src[sp].g;
dst[sp].b = src[sp].b;
dst[sp].a = 255;
}
}
}
if (!flutter_desktop_pixel_buffer_) {
flutter_desktop_pixel_buffer_ =
std::make_unique<FlutterDesktopPixelBuffer>();
// Unlocks mutex after texture is processed.
flutter_desktop_pixel_buffer_->release_callback =
[](void* release_context) {
auto mutex = reinterpret_cast<std::mutex*>(release_context);
mutex->unlock();
};
}
flutter_desktop_pixel_buffer_->buffer = dest_buffer_.data();
flutter_desktop_pixel_buffer_->width = preview_frame_width_;
flutter_desktop_pixel_buffer_->height = preview_frame_height_;
// Releases unique_lock and set mutex pointer for release context.
flutter_desktop_pixel_buffer_->release_context = buffer_lock.release();
return flutter_desktop_pixel_buffer_.get();
}
return nullptr;
}
} // namespace camera_windows