diff --git a/CMakeLists.txt b/CMakeLists.txt index 72a50f33..25b85464 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,8 @@ target_sources( src/utils/Obs_VolumeMeter_Helpers.h src/utils/Platform.cpp src/utils/Platform.h + src/utils/SourceScreenshot.cpp + src/utils/SourceScreenshot.h src/utils/Utils.h) configure_file(src/plugin-macros.h.in plugin-macros.generated.h) diff --git a/src/requesthandler/RequestHandler_Sources.cpp b/src/requesthandler/RequestHandler_Sources.cpp index 857a1ce9..cfc63aa6 100644 --- a/src/requesthandler/RequestHandler_Sources.cpp +++ b/src/requesthandler/RequestHandler_Sources.cpp @@ -25,84 +25,7 @@ with this program. If not, see #include "RequestHandler.h" #include "../utils/ImageFormat.h" - -QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t requestedWidth = 0, uint32_t requestedHeight = 0) -{ - // Get info about the requested source - const uint32_t sourceWidth = obs_source_get_width(source); - const uint32_t sourceHeight = obs_source_get_height(source); - const double sourceAspectRatio = ((double)sourceWidth / (double)sourceHeight); - - uint32_t imgWidth = sourceWidth; - uint32_t imgHeight = sourceHeight; - - // Determine suitable image width - if (requestedWidth) { - imgWidth = requestedWidth; - - if (!requestedHeight) - imgHeight = ((double)imgWidth / sourceAspectRatio); - } - - // Determine suitable image height - if (requestedHeight) { - imgHeight = requestedHeight; - - if (!requestedWidth) - imgWidth = ((double)imgHeight * sourceAspectRatio); - } - - // Create final image texture - QImage ret(imgWidth, imgHeight, QImage::Format::Format_RGBA8888); - ret.fill(0); - - // Video image buffer - uint8_t *videoData = nullptr; - uint32_t videoLinesize = 0; - - // Enter graphics context - obs_enter_graphics(); - - gs_texrender_t *texRender = gs_texrender_create(GS_RGBA, GS_ZS_NONE); - gs_stagesurf_t *stageSurface = gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA); - - success = false; - gs_texrender_reset(texRender); - if (gs_texrender_begin(texRender, imgWidth, imgHeight)) { - vec4 background; - vec4_zero(&background); - - gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0); - gs_ortho(0.0f, (float)sourceWidth, 0.0f, (float)sourceHeight, -100.0f, 100.0f); - - gs_blend_state_push(); - gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); - - obs_source_inc_showing(source); - obs_source_video_render(source); - obs_source_dec_showing(source); - - gs_blend_state_pop(); - gs_texrender_end(texRender); - - gs_stage_texture(stageSurface, gs_texrender_get_texture(texRender)); - if (gs_stagesurface_map(stageSurface, &videoData, &videoLinesize)) { - int lineSize = ret.bytesPerLine(); - for (uint y = 0; y < imgHeight; y++) { - memcpy(ret.scanLine(y), videoData + (y * videoLinesize), lineSize); - } - gs_stagesurface_unmap(stageSurface); - success = true; - } - } - - gs_stagesurface_destroy(stageSurface); - gs_texrender_destroy(texRender); - - obs_leave_graphics(); - - return ret; -} +#include "../utils/SourceScreenshot.h" bool IsImageFormatValid(std::string format) { @@ -214,7 +137,7 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request &request) } bool success; - QImage renderedImage = TakeSourceScreenshot(source, success, requestedWidth, requestedHeight); + QImage renderedImage = Utils::TakeSourceScreenshot(source, success, requestedWidth, requestedHeight); if (!success) return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to render screenshot."); @@ -309,7 +232,7 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request &request) } bool success; - QImage renderedImage = TakeSourceScreenshot(source, success, requestedWidth, requestedHeight); + QImage renderedImage = Utils::TakeSourceScreenshot(source, success, requestedWidth, requestedHeight); if (!success) return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Failed to render screenshot."); diff --git a/src/utils/SourceScreenshot.cpp b/src/utils/SourceScreenshot.cpp new file mode 100644 index 00000000..80a2cddf --- /dev/null +++ b/src/utils/SourceScreenshot.cpp @@ -0,0 +1,112 @@ +/* +obs-websocket +Copyright (C) 2016-2021 Stephane Lepin +Copyright (C) 2020-2021 Kyle Manning + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see +*/ + +#include + +#include "SourceScreenshot.h" + +QImage Utils::TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t requestedWidth, uint32_t requestedHeight) +{ + // Get info about the requested source + const uint32_t sourceWidth = obs_source_get_width(source); + const uint32_t sourceHeight = obs_source_get_height(source); + const double sourceAspectRatio = ((double)sourceWidth / (double)sourceHeight); + + uint32_t imgWidth = sourceWidth; + uint32_t imgHeight = sourceHeight; + + // Determine suitable image width + if (requestedWidth) { + imgWidth = requestedWidth; + + if (!requestedHeight) + imgHeight = ((double)imgWidth / sourceAspectRatio); + } + + // Determine suitable image height + if (requestedHeight) { + imgHeight = requestedHeight; + + if (!requestedWidth) + imgWidth = ((double)imgHeight * sourceAspectRatio); + } + + // Create final image texture + QImage ret(imgWidth, imgHeight, QImage::Format::Format_RGBA8888); + ret.fill(0); + + // Video image buffer + uint8_t *videoData = nullptr; + uint32_t videoLinesize = 0; + + // Enter graphics context + obs_enter_graphics(); + + gs_texrender_t *texRender = gs_texrender_create(GS_RGBA, GS_ZS_NONE); + gs_stagesurf_t *stageSurface = gs_stagesurface_create(imgWidth, imgHeight, GS_RGBA); + + success = false; + gs_texrender_reset(texRender); + if (gs_texrender_begin(texRender, imgWidth, imgHeight)) { + vec4 background; + vec4_zero(&background); + + gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0); + gs_ortho(0.0f, (float)sourceWidth, 0.0f, (float)sourceHeight, -100.0f, 100.0f); + + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + + // libobs keeps high-bit-depth sources (P010, I010, I210, I412: HEVC Main10, ProRes, 10-bit + // capture cards) as linear float. Scenes and the frontend previews draw with linear sRGB + // on, which encodes them to sRGB in an 8-bit target; without it the screenshot is stored + // linear and comes out too dark. 8-bit sRGB sources keep the direct path, so their pixels + // and straight alpha stay as they were. + const enum gs_color_space preferredSpaces[] = {GS_CS_SRGB, GS_CS_SRGB_16F, GS_CS_709_EXTENDED}; + const enum gs_color_space sourceSpace = + obs_source_get_color_space(source, OBS_COUNTOF(preferredSpaces), preferredSpaces); + const bool previousLinearSrgb = gs_set_linear_srgb(sourceSpace != GS_CS_SRGB); + + obs_source_inc_showing(source); + obs_source_video_render(source); + obs_source_dec_showing(source); + + gs_set_linear_srgb(previousLinearSrgb); + + gs_blend_state_pop(); + gs_texrender_end(texRender); + + gs_stage_texture(stageSurface, gs_texrender_get_texture(texRender)); + if (gs_stagesurface_map(stageSurface, &videoData, &videoLinesize)) { + int lineSize = ret.bytesPerLine(); + for (uint y = 0; y < imgHeight; y++) { + memcpy(ret.scanLine(y), videoData + (y * videoLinesize), lineSize); + } + gs_stagesurface_unmap(stageSurface); + success = true; + } + } + + gs_stagesurface_destroy(stageSurface); + gs_texrender_destroy(texRender); + + obs_leave_graphics(); + + return ret; +} diff --git a/src/utils/SourceScreenshot.h b/src/utils/SourceScreenshot.h new file mode 100644 index 00000000..a275bb95 --- /dev/null +++ b/src/utils/SourceScreenshot.h @@ -0,0 +1,29 @@ +/* +obs-websocket +Copyright (C) 2016-2021 Stephane Lepin +Copyright (C) 2020-2021 Kyle Manning + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see +*/ + +#pragma once + +#include +#include + +namespace Utils { + // Renders the source on its own into an 8-bit RGBA image of the requested size (0: the + // source's own width or height, keeping its aspect ratio). Enters the graphics context. + QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t requestedWidth = 0, uint32_t requestedHeight = 0); +}