From 5a56b64eface04e60bf013a0e2fd654065795348 Mon Sep 17 00:00:00 2001 From: Niklas Andersson <3985238+niklaspandersson@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:42:47 +0200 Subject: [PATCH 1/4] refactor decklink frame conversion --- src/modules/decklink/CMakeLists.txt | 7 +- .../decklink/consumer/decklink_consumer.cpp | 69 +++--- src/modules/decklink/consumer/frame.cpp | 208 ----------------- src/modules/decklink/consumer/frame.h | 50 ----- src/modules/decklink/consumer/frame_factory.h | 59 +++++ .../consumer/frame_factory_hdr_rgbxle.cpp | 151 +++++++++++++ .../consumer/frame_factory_hdr_rgbxle.h | 61 +++++ .../consumer/frame_factory_sdr_bgra.cpp | 210 ++++++++++++++++++ .../consumer/frame_factory_sdr_bgra.h | 61 +++++ 9 files changed, 587 insertions(+), 289 deletions(-) delete mode 100644 src/modules/decklink/consumer/frame.cpp delete mode 100644 src/modules/decklink/consumer/frame.h create mode 100644 src/modules/decklink/consumer/frame_factory.h create mode 100644 src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp create mode 100644 src/modules/decklink/consumer/frame_factory_hdr_rgbxle.h create mode 100644 src/modules/decklink/consumer/frame_factory_sdr_bgra.cpp create mode 100644 src/modules/decklink/consumer/frame_factory_sdr_bgra.h diff --git a/src/modules/decklink/CMakeLists.txt b/src/modules/decklink/CMakeLists.txt index ec0e3389e8..16d1194788 100644 --- a/src/modules/decklink/CMakeLists.txt +++ b/src/modules/decklink/CMakeLists.txt @@ -3,7 +3,8 @@ project (decklink) set(SOURCES consumer/decklink_consumer.cpp - consumer/frame.cpp + consumer/frame_factory_hdr_rgbxle.cpp + consumer/frame_factory_sdr_bgra.cpp consumer/config.cpp consumer/monitor.cpp @@ -13,7 +14,9 @@ set(SOURCES ) set(HEADERS consumer/decklink_consumer.h - consumer/frame.h + consumer/frame_factory_hdr_rgbxle.h + consumer/frame_factory_sdr_bgra.h + consumer/frame_factory.h consumer/config.h consumer/monitor.h diff --git a/src/modules/decklink/consumer/decklink_consumer.cpp b/src/modules/decklink/consumer/decklink_consumer.cpp index fef9e5c40a..5da0a329e1 100644 --- a/src/modules/decklink/consumer/decklink_consumer.cpp +++ b/src/modules/decklink/consumer/decklink_consumer.cpp @@ -25,7 +25,8 @@ #include "common/os/thread.h" #include "config.h" #include "decklink_consumer.h" -#include "frame.h" +#include "frame_factory_hdr_rgbxle.h" +#include "frame_factory_sdr_bgra.h" #include "monitor.h" #include "../decklink.h" @@ -78,8 +79,7 @@ void set_latency(const com_iface_ptr& config, com_ptr get_display_mode(const com_iface_ptr& device, core::video_format fmt, BMDPixelFormat pix_fmt, - BMDSupportedVideoModeFlags flag, - bool hdr) + BMDSupportedVideoModeFlags flag) { auto format = get_decklink_video_format(fmt); @@ -194,6 +194,12 @@ core::video_format_desc get_decklink_format(const port_configuration& confi return fallback_format_desc; } +spl::shared_ptr create_frame_factory(bool hdr) +{ + return hdr ? spl::make_shared() + : spl::make_shared(); +} + enum EOTF { SDR = 0, @@ -230,22 +236,26 @@ class decklink_frame hdr_meta_configuration hdr_metadata_; BMDFrameFlags flags_; BMDPixelFormat pix_fmt_; + int row_bytes_; public: decklink_frame(std::shared_ptr data, core::video_format_desc format_desc, int nb_samples, bool hdr, + BMDPixelFormat pix_fmt, + int row_bytes, core::color_space color_space, const hdr_meta_configuration& hdr_metadata) : format_desc_(std::move(format_desc)) , data_(std::move(data)) , nb_samples_(nb_samples) , hdr_(hdr) + , pix_fmt_(pix_fmt) + , row_bytes_(row_bytes) , color_space_(color_space) , hdr_metadata_(hdr_metadata) , flags_(hdr ? bmdFrameFlagDefault | bmdFrameContainsHDRMetadata : bmdFrameFlagDefault) - , pix_fmt_(get_pixel_format(hdr)) { } @@ -299,7 +309,7 @@ class decklink_frame long STDMETHODCALLTYPE GetWidth() override { return static_cast(format_desc_.width); } long STDMETHODCALLTYPE GetHeight() override { return static_cast(format_desc_.height); } - long STDMETHODCALLTYPE GetRowBytes() override { return static_cast(get_row_bytes(format_desc_, hdr_)); } + long STDMETHODCALLTYPE GetRowBytes() override { return static_cast(row_bytes_); } BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() override { return pix_fmt_; } BMDFrameFlags STDMETHODCALLTYPE GetFlags() override { return flags_; } @@ -427,6 +437,7 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback { const configuration config_; const port_configuration output_config_; + spl::shared_ptr frame_factory_; com_ptr decklink_ = get_device(output_config_.device_index); com_iface_ptr output_ = iface_cast(decklink_); com_iface_ptr keyer_ = iface_cast(decklink_, true); @@ -443,9 +454,8 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback const core::video_format_desc decklink_format_desc_; com_ptr mode_ = get_display_mode(output_, decklink_format_desc_.format, - get_pixel_format(config_.hdr), - bmdSupportedVideoModeDefault, - config_.hdr); + frame_factory_->get_pixel_format(), + bmdSupportedVideoModeDefault); decklink_secondary_port(const configuration& config, port_configuration output_config, @@ -455,6 +465,7 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback int device_sync_group) : config_(config) , output_config_(std::move(output_config)) + , frame_factory_(new frame_factory_sdr_bgra()) , device_sync_group_(device_sync_group) , channel_format_desc_(std::move(channel_format_desc)) , decklink_format_desc_(get_decklink_format(output_config_, main_decklink_format_desc)) @@ -546,13 +557,8 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback frame1 = frame; } - auto image_data = convert_frame_for_port(channel_format_desc_, - decklink_format_desc_, - output_config_, - frame1, - frame2, - mode_->GetFieldDominance(), - config_.hdr); + auto image_data = frame_factory_->convert_frame_for_port( + channel_format_desc_, decklink_format_desc_, output_config_, frame1, frame2, mode_->GetFieldDominance()); schedule_next_video(image_data, 0, display_time); } @@ -563,6 +569,8 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback decklink_format_desc_, nb_samples, config_.hdr, + frame_factory_->get_pixel_format(), + frame_factory_->get_row_bytes(decklink_format_desc_.width), core::color_space::bt709, config_.hdr_meta)); if (FAILED(output_->ScheduleVideoFrame(get_raw(packed_frame), @@ -592,8 +600,9 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback struct decklink_consumer final : public IDeckLinkVideoOutputCallback { - const int channel_index_; - const configuration config_; + const int channel_index_; + const configuration config_; + spl::shared_ptr frame_factory_; com_ptr decklink_ = get_device(config_.primary.device_index); com_iface_ptr output_ = iface_cast(decklink_); @@ -629,9 +638,8 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback com_ptr mode_ = get_display_mode(output_, decklink_format_desc_.format, - get_pixel_format(config_.hdr), - bmdSupportedVideoModeDefault, - config_.hdr); + frame_factory_->get_pixel_format(), + bmdSupportedVideoModeDefault); std::atomic abort_request_{false}; @@ -639,6 +647,7 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback decklink_consumer(const configuration& config, core::video_format_desc channel_format_desc, int channel_index) : channel_index_(channel_index) , config_(config) + , frame_factory_(new frame_factory_sdr_bgra()) , channel_format_desc_(std::move(channel_format_desc)) , decklink_format_desc_(get_decklink_format(config.primary, channel_format_desc_)) { @@ -717,7 +726,7 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback nb_samples); } - std::shared_ptr image_data = allocate_frame_data(decklink_format_desc_, config_.hdr); + std::shared_ptr image_data = frame_factory_->allocate_frame_data(decklink_format_desc_); schedule_next_video(image_data, nb_samples, video_scheduled_, config_.hdr_meta.default_color_space); for (auto& context : secondary_port_contexts_) { @@ -933,13 +942,13 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback tbb::parallel_for(-1, static_cast(secondary_port_contexts_.size()), [&](int i) { if (i == -1) { // Primary port - std::shared_ptr image_data = convert_frame_for_port(channel_format_desc_, - decklink_format_desc_, - config_.primary, - frame1, - frame2, - mode_->GetFieldDominance(), - config_.hdr); + std::shared_ptr image_data = + frame_factory_->convert_frame_for_port(channel_format_desc_, + decklink_format_desc_, + config_.primary, + frame1, + frame2, + mode_->GetFieldDominance()); schedule_next_video( image_data, nb_samples, video_display_time, frame1.pixel_format_desc().color_space); @@ -1007,8 +1016,10 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback BMDTimeValue display_time, core::color_space color_space) { + auto fmt = frame_factory_->get_pixel_format(); + auto row_bytes = frame_factory_->get_row_bytes(decklink_format_desc_.width); auto fill_frame = wrap_raw(new decklink_frame( - std::move(image_data), decklink_format_desc_, nb_samples, config_.hdr, color_space, config_.hdr_meta)); + std::move(image_data), decklink_format_desc_, nb_samples, config_.hdr, fmt, row_bytes, color_space, config_.hdr_meta)); if (FAILED(output_->ScheduleVideoFrame( get_raw(fill_frame), display_time, decklink_format_desc_.duration, decklink_format_desc_.time_scale))) { CASPAR_LOG(error) << print() << L" Failed to schedule primary video."; diff --git a/src/modules/decklink/consumer/frame.cpp b/src/modules/decklink/consumer/frame.cpp deleted file mode 100644 index 21bc5e3f91..0000000000 --- a/src/modules/decklink/consumer/frame.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2011 Sveriges Television AB - * - * This file is part of CasparCG (www.casparcg.com). - * - * CasparCG 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 3 of the License, or - * (at your option) any later version. - * - * CasparCG 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 CasparCG. If not, see . - * - * Author: Julian Waller, julian@superfly.tv - */ - -#include "../StdAfx.h" - -#include "frame.h" - -#include - -#include -#include - -namespace caspar { namespace decklink { - -BMDPixelFormat get_pixel_format(bool hdr) { return hdr ? bmdFormat10BitRGBXLE : bmdFormat8BitBGRA; } -int get_row_bytes(const core::video_format_desc& format_desc, bool hdr) -{ - return hdr ? ((format_desc.width + 63) / 64) * 256 : format_desc.width * 4; -} - -std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc, bool hdr) -{ - auto alignment = hdr ? 256 : 64; - auto size = hdr ? get_row_bytes(format_desc, hdr) * format_desc.height : format_desc.size; - return create_aligned_buffer(size, alignment); -} - -std::shared_ptr convert_to_key_only(const std::shared_ptr& image_data, std::size_t byte_count) -{ - auto key_data = create_aligned_buffer(byte_count); - - aligned_memshfl(key_data.get(), image_data.get(), byte_count, 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303); - - return key_data; -} - -void convert_frame(const core::video_format_desc& channel_format_desc, - const core::video_format_desc& decklink_format_desc, - const port_configuration& config, - std::shared_ptr& image_data, - bool topField, - const core::const_frame& frame, - bool hdr) -{ - // No point copying an empty frame - if (!frame) - return; - - int firstLine = topField ? 0 : 1; - - if (channel_format_desc.format == decklink_format_desc.format && config.src_x == 0 && config.src_y == 0 && - config.region_w == 0 && config.region_h == 0 && config.dest_x == 0 && config.dest_y == 0) { - // Fast path - - if (hdr) { - // Pack eight byte R16G16B16A16 pixels as four byte 10bit RGB R10G10B10XX - const int NUM_THREADS = 4; - auto rows_per_thread = decklink_format_desc.height / NUM_THREADS; - size_t byte_count_line = get_row_bytes(decklink_format_desc, hdr); - tbb::parallel_for(0, NUM_THREADS, [&](int i) { - auto end = (i + 1) * rows_per_thread; - for (int y = firstLine + i * rows_per_thread; y < end; y += decklink_format_desc.field_count) { - auto dest = reinterpret_cast(image_data.get()) + (long long)y * byte_count_line / 4; - for (int x = 0; x < decklink_format_desc.width; x += 1) { - auto src = reinterpret_cast( - frame.image_data(0).data() + (long long)y * decklink_format_desc.width * 8 + x * 8); - uint16_t blue = src[0] >> 6; - uint16_t green = src[1] >> 6; - uint16_t red = src[2] >> 6; - dest[x] = ((uint32_t)(red) << 22) + ((uint32_t)(green) << 12) + ((uint32_t)(blue) << 2); - } - } - }); - - } else { - size_t byte_count_line = (size_t)decklink_format_desc.width * 4; - for (int y = firstLine; y < decklink_format_desc.height; y += decklink_format_desc.field_count) { - std::memcpy(reinterpret_cast(image_data.get()) + (long long)y * byte_count_line, - frame.image_data(0).data() + (long long)y * byte_count_line, - byte_count_line); - } - } - } else { - // Take a sub-region - - // TODO: Add support for hdr frames - - // Some repetetive numbers - size_t byte_count_dest_line = (size_t)decklink_format_desc.width * 4; - size_t byte_count_src_line = (size_t)channel_format_desc.width * 4; - size_t byte_offset_src_line = std::max(0, (config.src_x * 4)); - size_t byte_offset_dest_line = std::max(0, (config.dest_x * 4)); - int y_skip_src_lines = std::max(0, config.src_y); - int y_skip_dest_lines = std::max(0, config.dest_y); - - size_t byte_copy_per_line = - std::min(byte_count_src_line - byte_offset_src_line, byte_count_dest_line - byte_offset_dest_line); - if (config.region_w > 0) // If the user chose a width, respect that - byte_copy_per_line = std::min(byte_copy_per_line, (size_t)config.region_w * 4); - - size_t byte_pad_end_of_line = - std::max(((size_t)decklink_format_desc.width * 4) - byte_copy_per_line - byte_offset_dest_line, (size_t)0); - - int copy_line_count = - std::min(channel_format_desc.height - y_skip_src_lines, decklink_format_desc.height - y_skip_dest_lines); - if (config.region_h > 0) // If the user chose a height, respect that - copy_line_count = std::min(copy_line_count, config.region_h); - - int max_y_content = y_skip_dest_lines + std::min(copy_line_count, channel_format_desc.height); - - for (int y = firstLine; y < y_skip_dest_lines; y += decklink_format_desc.field_count) { - // Fill the line with black - std::memset( - reinterpret_cast(image_data.get()) + (byte_count_dest_line * y), 0, byte_count_dest_line); - } - - int firstFillLine = y_skip_dest_lines; - if (decklink_format_desc.field_count != 1 && firstFillLine % 2 != firstLine) - firstFillLine += 1; - for (int y = firstFillLine; y < max_y_content; y += decklink_format_desc.field_count) { - auto line_start_ptr = reinterpret_cast(image_data.get()) + (long long)y * byte_count_dest_line; - auto line_content_ptr = line_start_ptr + byte_offset_dest_line; - - // Fill the start with black - if (byte_offset_dest_line > 0) { - std::memset(line_start_ptr, 0, byte_offset_dest_line); - } - - // Copy the pixels - long long src_y = y + y_skip_src_lines - y_skip_dest_lines; - std::memcpy(line_content_ptr, - frame.image_data(0).data() + src_y * byte_count_src_line + byte_offset_src_line, - byte_copy_per_line); - - // Fill the end with black - if (byte_pad_end_of_line > 0) { - std::memset(line_content_ptr + byte_copy_per_line, 0, byte_pad_end_of_line); - } - } - - // Calculate the first line number to fill with black - if (decklink_format_desc.field_count != 1 && max_y_content % 2 != firstLine) - max_y_content += 1; - for (int y = max_y_content; y < decklink_format_desc.height; y += decklink_format_desc.field_count) { - // Fill the line with black - std::memset( - reinterpret_cast(image_data.get()) + (byte_count_dest_line * y), 0, byte_count_dest_line); - } - } -} - -std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, - const core::video_format_desc& decklink_format_desc, - const port_configuration& config, - const core::const_frame& frame1, - const core::const_frame& frame2, - BMDFieldDominance field_dominance, - bool hdr) -{ - std::shared_ptr image_data = allocate_frame_data(decklink_format_desc, hdr); - - if (field_dominance != bmdProgressiveFrame) { - convert_frame(channel_format_desc, - decklink_format_desc, - config, - image_data, - field_dominance == bmdUpperFieldFirst, - frame1, - hdr); - - convert_frame(channel_format_desc, - decklink_format_desc, - config, - image_data, - field_dominance != bmdUpperFieldFirst, - frame2, - hdr); - - } else { - convert_frame(channel_format_desc, decklink_format_desc, config, image_data, true, frame1, hdr); - } - - if (config.key_only) { - image_data = convert_to_key_only(image_data, decklink_format_desc.size); - } - - return image_data; -} - -}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame.h b/src/modules/decklink/consumer/frame.h deleted file mode 100644 index e96109c4ca..0000000000 --- a/src/modules/decklink/consumer/frame.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2011 Sveriges Television AB - * - * This file is part of CasparCG (www.casparcg.com). - * - * CasparCG 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 3 of the License, or - * (at your option) any later version. - * - * CasparCG 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 CasparCG. If not, see . - * - * Author: Julian Waller, julian@superfly.tv - */ - -#pragma once - -#include "../StdAfx.h" - -#include "config.h" - -#include "../decklink_api.h" - -#include -#include - -#include - -namespace caspar { namespace decklink { - -BMDPixelFormat get_pixel_format(bool hdr); -int get_row_bytes(const core::video_format_desc& format_desc, bool hdr); - -std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc, bool hdr); - -std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, - const core::video_format_desc& decklink_format_desc, - const port_configuration& config, - const core::const_frame& frame1, - const core::const_frame& frame2, - BMDFieldDominance field_dominance, - bool hdr); - -}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory.h b/src/modules/decklink/consumer/frame_factory.h new file mode 100644 index 0000000000..a8a1db16e5 --- /dev/null +++ b/src/modules/decklink/consumer/frame_factory.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Sveriges Television AB + * + * This file is part of CasparCG (www.casparcg.com). + * + * CasparCG 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 3 of the License, or + * (at your option) any later version. + * + * CasparCG 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 CasparCG. If not, see . + * + * Author: Niklas P Andersson, niklas@nxtedition.com + */ + +#pragma once + +#include "../StdAfx.h" + +#include "config.h" + +#include "../decklink_api.h" + +#include +#include + +#include + +namespace caspar { namespace decklink { + +class frame_factory +{ + protected: + frame_factory() = default; + + public: + frame_factory& operator=(const frame_factory&) = delete; + virtual ~frame_factory() = default; + + frame_factory(const frame_factory&) = delete; + + virtual BMDPixelFormat get_pixel_format() = 0; + virtual int get_row_bytes(int width) = 0; + virtual std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc) = 0; + virtual std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) = 0; +}; + +}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp b/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp new file mode 100644 index 0000000000..ee00cd26e6 --- /dev/null +++ b/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2011 Sveriges Television AB + * + * This file is part of CasparCG (www.casparcg.com). + * + * CasparCG 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 3 of the License, or + * (at your option) any later version. + * + * CasparCG 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 CasparCG. If not, see . + * + * Author: Niklas Andersson, niklas@nxtedition.com + */ + +#include "../StdAfx.h" + +#include "frame.h" +#include "frame_factory_hdr_rgbxle.h" + +#include + +#include +#include + +namespace caspar { namespace decklink { + +struct frame_factory_hdr_rgbxle::impl final +{ + public: + impl() = default; + + BMDPixelFormat get_pixel_format() { return bmdFormat10BitRGBXLE; } + + int get_row_bytes(int width) { return ((width + 63) / 64) * 256; } + + std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc) + { + auto size = get_row_bytes(format_desc.width) * format_desc.height; + return create_aligned_buffer(format_desc.size, 256); + } + + void convert_frame(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + std::shared_ptr& image_data, + bool topField, + const core::const_frame& frame) + { + // No point copying an empty frame + if (!frame) + return; + + int firstLine = topField ? 0 : 1; + + if (channel_format_desc.format == decklink_format_desc.format && config.src_x == 0 && config.src_y == 0 && + config.region_w == 0 && config.region_h == 0 && config.dest_x == 0 && config.dest_y == 0) { + // Fast path + + const int NUM_THREADS = 4; + auto rows_per_thread = decklink_format_desc.height / NUM_THREADS; + size_t byte_count_line = get_row_bytes(decklink_format_desc.width); + tbb::parallel_for(0, NUM_THREADS, [&](int i) { + auto end = (i + 1) * rows_per_thread; + for (int y = firstLine + i * rows_per_thread; y < end; y += decklink_format_desc.field_count) { + auto dest = reinterpret_cast(image_data.get()) + (long long)y * byte_count_line / 4; + for (int x = 0; x < decklink_format_desc.width; x += 1) { + auto src = reinterpret_cast( + frame.image_data(0).data() + (long long)y * decklink_format_desc.width * 8 + x * 8); + uint16_t blue = src[0] >> 6; + uint16_t green = src[1] >> 6; + uint16_t red = src[2] >> 6; + dest[x] = ((uint32_t)(red) << 22) + ((uint32_t)(green) << 12) + ((uint32_t)(blue) << 2); + } + } + }); + } else { + // Take a sub-region + // TODO: Add support for hdr frames + } + } + + std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) + { + std::shared_ptr image_data = allocate_frame_data(decklink_format_desc); + + if (field_dominance != bmdProgressiveFrame) { + convert_frame(channel_format_desc, + decklink_format_desc, + config, + image_data, + field_dominance == bmdUpperFieldFirst, + frame1); + + convert_frame(channel_format_desc, + decklink_format_desc, + config, + image_data, + field_dominance != bmdUpperFieldFirst, + frame2); + + } else { + convert_frame(channel_format_desc, decklink_format_desc, config, image_data, true, frame1); + } + + if (config.key_only) { + // TODO: Add support for hdr frames + } + + return image_data; + } +}; + +frame_factory_hdr_rgbxle::frame_factory_hdr_rgbxle() + : impl_(new impl()) +{ +} + +frame_factory_hdr_rgbxle::~frame_factory_hdr_rgbxle() {} + +BMDPixelFormat frame_factory_hdr_rgbxle::get_pixel_format() { return impl_->get_pixel_format(); } +int frame_factory_hdr_rgbxle::get_row_bytes(int width) { return impl_->get_row_bytes(width); } + +std::shared_ptr frame_factory_hdr_rgbxle::allocate_frame_data(const core::video_format_desc& format_desc) +{ + return impl_->allocate_frame_data(format_desc); +} +std::shared_ptr +frame_factory_hdr_rgbxle::convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) +{ + return impl_->convert_frame_for_port( + channel_format_desc, decklink_format_desc, config, frame1, frame2, field_dominance); +} + +}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.h b/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.h new file mode 100644 index 0000000000..b03b654eae --- /dev/null +++ b/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 Sveriges Television AB + * + * This file is part of CasparCG (www.casparcg.com). + * + * CasparCG 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 3 of the License, or + * (at your option) any later version. + * + * CasparCG 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 CasparCG. If not, see . + * + * Author: Niklas Andersson, niklas@nxtedition.com + */ + +#pragma once + +#include "../StdAfx.h" + +#include "config.h" +#include "frame_factory.h" + +#include "../decklink_api.h" + +#include +#include + +#include + +namespace caspar { namespace decklink { + +class frame_factory_hdr_rgbxle : public frame_factory +{ + public: + explicit frame_factory_hdr_rgbxle(); + virtual ~frame_factory_hdr_rgbxle(); + + virtual BMDPixelFormat get_pixel_format(); + virtual int get_row_bytes(int width); + virtual std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc); + virtual std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance); + + private: + struct impl; + std::unique_ptr impl_; + frame_factory_hdr_rgbxle(const frame_factory_hdr_rgbxle&) = delete; + frame_factory_hdr_rgbxle& operator=(const frame_factory_hdr_rgbxle&) = delete; +}; + +}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_sdr_bgra.cpp b/src/modules/decklink/consumer/frame_factory_sdr_bgra.cpp new file mode 100644 index 0000000000..14e7e070dd --- /dev/null +++ b/src/modules/decklink/consumer/frame_factory_sdr_bgra.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2011 Sveriges Television AB + * + * This file is part of CasparCG (www.casparcg.com). + * + * CasparCG 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 3 of the License, or + * (at your option) any later version. + * + * CasparCG 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 CasparCG. If not, see . + * + * Author: Julian Waller, julian@superfly.tv + * Author: Niklas Andersson, niklas@nxtedition.com + */ + +#include "../StdAfx.h" + +#include "frame_factory_sdr_bgra.h" + +#include + +#include +#include + +namespace caspar { namespace decklink { + +std::shared_ptr convert_to_key_only(const std::shared_ptr& image_data, std::size_t byte_count) +{ + auto key_data = create_aligned_buffer(byte_count); + + aligned_memshfl(key_data.get(), image_data.get(), byte_count, 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303); + + return key_data; +} + +struct frame_factory_sdr_bgra::impl final +{ + public: + impl() = default; + + BMDPixelFormat get_pixel_format() { return bmdFormat8BitBGRA; } + int get_row_bytes(int width) { return width * 4; } + + std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc) + { + return create_aligned_buffer(format_desc.size, 64); + } + + void convert_frame(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + std::shared_ptr& image_data, + bool topField, + const core::const_frame& frame) + { + // No point copying an empty frame + if (!frame) + return; + + int firstLine = topField ? 0 : 1; + + if (channel_format_desc.format == decklink_format_desc.format && config.src_x == 0 && config.src_y == 0 && + config.region_w == 0 && config.region_h == 0 && config.dest_x == 0 && config.dest_y == 0) { + // Fast path + + size_t byte_count_line = (size_t)decklink_format_desc.width * 4; + for (int y = firstLine; y < decklink_format_desc.height; y += decklink_format_desc.field_count) { + std::memcpy(reinterpret_cast(image_data.get()) + (long long)y * byte_count_line, + frame.image_data(0).data() + (long long)y * byte_count_line, + byte_count_line); + } + } else { + // Take a sub-region + + // TODO: Add support for hdr frames + + // Some repetetive numbers + size_t byte_count_dest_line = (size_t)decklink_format_desc.width * 4; + size_t byte_count_src_line = (size_t)channel_format_desc.width * 4; + size_t byte_offset_src_line = std::max(0, (config.src_x * 4)); + size_t byte_offset_dest_line = std::max(0, (config.dest_x * 4)); + int y_skip_src_lines = std::max(0, config.src_y); + int y_skip_dest_lines = std::max(0, config.dest_y); + + size_t byte_copy_per_line = + std::min(byte_count_src_line - byte_offset_src_line, byte_count_dest_line - byte_offset_dest_line); + if (config.region_w > 0) // If the user chose a width, respect that + byte_copy_per_line = std::min(byte_copy_per_line, (size_t)config.region_w * 4); + + size_t byte_pad_end_of_line = + std::max(((size_t)decklink_format_desc.width * 4) - byte_copy_per_line - byte_offset_dest_line, (size_t)0); + + int copy_line_count = + std::min(channel_format_desc.height - y_skip_src_lines, decklink_format_desc.height - y_skip_dest_lines); + if (config.region_h > 0) // If the user chose a height, respect that + copy_line_count = std::min(copy_line_count, config.region_h); + + int max_y_content = y_skip_dest_lines + std::min(copy_line_count, channel_format_desc.height); + + for (int y = firstLine; y < y_skip_dest_lines; y += decklink_format_desc.field_count) { + // Fill the line with black + std::memset( + reinterpret_cast(image_data.get()) + (byte_count_dest_line * y), 0, byte_count_dest_line); + } + + int firstFillLine = y_skip_dest_lines; + if (decklink_format_desc.field_count != 1 && firstFillLine % 2 != firstLine) + firstFillLine += 1; + for (int y = firstFillLine; y < max_y_content; y += decklink_format_desc.field_count) { + auto line_start_ptr = reinterpret_cast(image_data.get()) + (long long)y * byte_count_dest_line; + auto line_content_ptr = line_start_ptr + byte_offset_dest_line; + + // Fill the start with black + if (byte_offset_dest_line > 0) { + std::memset(line_start_ptr, 0, byte_offset_dest_line); + } + + // Copy the pixels + long long src_y = y + y_skip_src_lines - y_skip_dest_lines; + std::memcpy(line_content_ptr, + frame.image_data(0).data() + src_y * byte_count_src_line + byte_offset_src_line, + byte_copy_per_line); + + // Fill the end with black + if (byte_pad_end_of_line > 0) { + std::memset(line_content_ptr + byte_copy_per_line, 0, byte_pad_end_of_line); + } + } + + // Calculate the first line number to fill with black + if (decklink_format_desc.field_count != 1 && max_y_content % 2 != firstLine) + max_y_content += 1; + for (int y = max_y_content; y < decklink_format_desc.height; y += decklink_format_desc.field_count) { + // Fill the line with black + std::memset( + reinterpret_cast(image_data.get()) + (byte_count_dest_line * y), 0, byte_count_dest_line); + } + } + } + + std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) + { + std::shared_ptr image_data = allocate_frame_data(decklink_format_desc); + + if (field_dominance != bmdProgressiveFrame) { + convert_frame(channel_format_desc, + decklink_format_desc, + config, + image_data, + field_dominance == bmdUpperFieldFirst, + frame1); + + convert_frame(channel_format_desc, + decklink_format_desc, + config, + image_data, + field_dominance != bmdUpperFieldFirst, + frame2); + + } else { + convert_frame(channel_format_desc, decklink_format_desc, config, image_data, true, frame1); + } + + if (config.key_only) { + image_data = convert_to_key_only(image_data, decklink_format_desc.size); + } + + return image_data; + } +}; + +frame_factory_sdr_bgra::frame_factory_sdr_bgra() + : impl_(new impl()) +{ +} + +frame_factory_sdr_bgra::~frame_factory_sdr_bgra() {} + +BMDPixelFormat frame_factory_sdr_bgra::get_pixel_format() { return impl_->get_pixel_format(); } +int frame_factory_sdr_bgra::get_row_bytes(int width) { return impl_->get_row_bytes(width); } + +std::shared_ptr frame_factory_sdr_bgra::allocate_frame_data(const core::video_format_desc& format_desc) +{ + return impl_->allocate_frame_data(format_desc); +} +std::shared_ptr +frame_factory_sdr_bgra::convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) +{ + return impl_->convert_frame_for_port( + channel_format_desc, decklink_format_desc, config, frame1, frame2, field_dominance); +} + +}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_sdr_bgra.h b/src/modules/decklink/consumer/frame_factory_sdr_bgra.h new file mode 100644 index 0000000000..6638777333 --- /dev/null +++ b/src/modules/decklink/consumer/frame_factory_sdr_bgra.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 Sveriges Television AB + * + * This file is part of CasparCG (www.casparcg.com). + * + * CasparCG 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 3 of the License, or + * (at your option) any later version. + * + * CasparCG 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 CasparCG. If not, see . + * + * Author: Niklas Andersson, niklas@nxtedition.com + */ + +#pragma once + +#include "../StdAfx.h" + +#include "config.h" +#include "frame_factory.h" + +#include "../decklink_api.h" + +#include +#include + +#include + +namespace caspar { namespace decklink { + +class frame_factory_sdr_bgra : public frame_factory +{ + public: + explicit frame_factory_sdr_bgra(); + virtual ~frame_factory_sdr_bgra(); + + virtual BMDPixelFormat get_pixel_format(); + virtual int get_row_bytes(int width); + virtual std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc); + virtual std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance); + + private: + struct impl; + std::unique_ptr impl_; + frame_factory_sdr_bgra(const frame_factory_sdr_bgra&) = delete; + frame_factory_sdr_bgra& operator=(const frame_factory_sdr_bgra&) = delete; +}; + +}} // namespace caspar::decklink From 91831c1f0251377315f838af962db6f0021e72ac Mon Sep 17 00:00:00 2001 From: Niklas Andersson <3985238+niklaspandersson@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:27:30 +0200 Subject: [PATCH 2/4] decklink: replace 10bitRGBXLE with v210 --- src/CMakeModules/Bootstrap_Linux.cmake | 2 + src/CMakeModules/Bootstrap_Windows.cmake | 2 +- src/accelerator/ogl/image/image_mixer.cpp | 4 +- src/common/memory.h | 6 + src/modules/decklink/CMakeLists.txt | 4 +- .../decklink/consumer/decklink_consumer.cpp | 6 +- .../consumer/frame_factory_hdr_rgbxle.cpp | 151 ------- .../consumer/frame_factory_hdr_v210.cpp | 367 ++++++++++++++++++ ..._hdr_rgbxle.h => frame_factory_hdr_v210.h} | 12 +- .../consumer/frame_factory_sdr_bgra.h | 4 +- 10 files changed, 394 insertions(+), 164 deletions(-) delete mode 100644 src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp create mode 100644 src/modules/decklink/consumer/frame_factory_hdr_v210.cpp rename src/modules/decklink/consumer/{frame_factory_hdr_rgbxle.h => frame_factory_hdr_v210.h} (85%) diff --git a/src/CMakeModules/Bootstrap_Linux.cmake b/src/CMakeModules/Bootstrap_Linux.cmake index fe285513f8..da111e09b7 100644 --- a/src/CMakeModules/Bootstrap_Linux.cmake +++ b/src/CMakeModules/Bootstrap_Linux.cmake @@ -123,7 +123,9 @@ endif() IF (CMAKE_SYSTEM_PROCESSOR MATCHES "(i[3-6]86|x64|x86_64|amd64|e2k)") ADD_COMPILE_OPTIONS (-msse3) ADD_COMPILE_OPTIONS (-mssse3) + ADD_COMPILE_OPTIONS (-mavx) ADD_COMPILE_OPTIONS (-msse4.1) + ADD_COMPILE_OPTIONS (-mavx2) ELSE () ADD_COMPILE_DEFINITIONS (USE_SIMDE) ENDIF () diff --git a/src/CMakeModules/Bootstrap_Windows.cmake b/src/CMakeModules/Bootstrap_Windows.cmake index 11d92d9bcd..261bf634b0 100644 --- a/src/CMakeModules/Bootstrap_Windows.cmake +++ b/src/CMakeModules/Bootstrap_Windows.cmake @@ -268,4 +268,4 @@ add_definitions(-D_WIN32_WINNT=0x601) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHa /Zi /W4 /WX /MP /fp:fast /Zm192 /FIcommon/compiler/vs/disable_silly_warnings.h") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D TBB_USE_ASSERT=1 /D TBB_USE_DEBUG /bigobj") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /Ot /Gy /bigobj") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /arch:AVX2 /Ot /Gy /bigobj") diff --git a/src/accelerator/ogl/image/image_mixer.cpp b/src/accelerator/ogl/image/image_mixer.cpp index 7be6c28938..6fc42d8ba8 100644 --- a/src/accelerator/ogl/image/image_mixer.cpp +++ b/src/accelerator/ogl/image/image_mixer.cpp @@ -26,6 +26,8 @@ #include "../util/device.h" #include "../util/texture.h" +#include + #include #include #include @@ -91,7 +93,7 @@ class image_renderer const core::video_format_desc& format_desc) { if (layers.empty()) { // Bypass GPU with empty frame. - static const std::vector buffer(max_frame_size_, 0); + static const std::vector> buffer(max_frame_size_, 0); return make_ready_future(array(buffer.data(), format_desc.size, true)); } diff --git a/src/common/memory.h b/src/common/memory.h index 65379ab004..f2a8e27aa4 100644 --- a/src/common/memory.h +++ b/src/common/memory.h @@ -641,6 +641,12 @@ shared_ptr make_shared() return shared_ptr(std::make_shared()); } +template +shared_ptr make_shared() +{ + return shared_ptr(std::make_shared()); +} + template shared_ptr make_shared(P0&& p0) { diff --git a/src/modules/decklink/CMakeLists.txt b/src/modules/decklink/CMakeLists.txt index 16d1194788..02f8d230ae 100644 --- a/src/modules/decklink/CMakeLists.txt +++ b/src/modules/decklink/CMakeLists.txt @@ -3,7 +3,7 @@ project (decklink) set(SOURCES consumer/decklink_consumer.cpp - consumer/frame_factory_hdr_rgbxle.cpp + consumer/frame_factory_hdr_v210.cpp consumer/frame_factory_sdr_bgra.cpp consumer/config.cpp consumer/monitor.cpp @@ -14,7 +14,7 @@ set(SOURCES ) set(HEADERS consumer/decklink_consumer.h - consumer/frame_factory_hdr_rgbxle.h + consumer/frame_factory_hdr_v210.h consumer/frame_factory_sdr_bgra.h consumer/frame_factory.h consumer/config.h diff --git a/src/modules/decklink/consumer/decklink_consumer.cpp b/src/modules/decklink/consumer/decklink_consumer.cpp index 5da0a329e1..ed6081aa93 100644 --- a/src/modules/decklink/consumer/decklink_consumer.cpp +++ b/src/modules/decklink/consumer/decklink_consumer.cpp @@ -25,7 +25,7 @@ #include "common/os/thread.h" #include "config.h" #include "decklink_consumer.h" -#include "frame_factory_hdr_rgbxle.h" +#include "frame_factory_hdr_v210.h" #include "frame_factory_sdr_bgra.h" #include "monitor.h" @@ -196,7 +196,7 @@ core::video_format_desc get_decklink_format(const port_configuration& confi spl::shared_ptr create_frame_factory(bool hdr) { - return hdr ? spl::make_shared() + return hdr ? spl::make_shared() : spl::make_shared(); } @@ -647,7 +647,7 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback decklink_consumer(const configuration& config, core::video_format_desc channel_format_desc, int channel_index) : channel_index_(channel_index) , config_(config) - , frame_factory_(new frame_factory_sdr_bgra()) + , frame_factory_(create_frame_factory(config.hdr)) , channel_format_desc_(std::move(channel_format_desc)) , decklink_format_desc_(get_decklink_format(config.primary, channel_format_desc_)) { diff --git a/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp b/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp deleted file mode 100644 index ee00cd26e6..0000000000 --- a/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2011 Sveriges Television AB - * - * This file is part of CasparCG (www.casparcg.com). - * - * CasparCG 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 3 of the License, or - * (at your option) any later version. - * - * CasparCG 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 CasparCG. If not, see . - * - * Author: Niklas Andersson, niklas@nxtedition.com - */ - -#include "../StdAfx.h" - -#include "frame.h" -#include "frame_factory_hdr_rgbxle.h" - -#include - -#include -#include - -namespace caspar { namespace decklink { - -struct frame_factory_hdr_rgbxle::impl final -{ - public: - impl() = default; - - BMDPixelFormat get_pixel_format() { return bmdFormat10BitRGBXLE; } - - int get_row_bytes(int width) { return ((width + 63) / 64) * 256; } - - std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc) - { - auto size = get_row_bytes(format_desc.width) * format_desc.height; - return create_aligned_buffer(format_desc.size, 256); - } - - void convert_frame(const core::video_format_desc& channel_format_desc, - const core::video_format_desc& decklink_format_desc, - const port_configuration& config, - std::shared_ptr& image_data, - bool topField, - const core::const_frame& frame) - { - // No point copying an empty frame - if (!frame) - return; - - int firstLine = topField ? 0 : 1; - - if (channel_format_desc.format == decklink_format_desc.format && config.src_x == 0 && config.src_y == 0 && - config.region_w == 0 && config.region_h == 0 && config.dest_x == 0 && config.dest_y == 0) { - // Fast path - - const int NUM_THREADS = 4; - auto rows_per_thread = decklink_format_desc.height / NUM_THREADS; - size_t byte_count_line = get_row_bytes(decklink_format_desc.width); - tbb::parallel_for(0, NUM_THREADS, [&](int i) { - auto end = (i + 1) * rows_per_thread; - for (int y = firstLine + i * rows_per_thread; y < end; y += decklink_format_desc.field_count) { - auto dest = reinterpret_cast(image_data.get()) + (long long)y * byte_count_line / 4; - for (int x = 0; x < decklink_format_desc.width; x += 1) { - auto src = reinterpret_cast( - frame.image_data(0).data() + (long long)y * decklink_format_desc.width * 8 + x * 8); - uint16_t blue = src[0] >> 6; - uint16_t green = src[1] >> 6; - uint16_t red = src[2] >> 6; - dest[x] = ((uint32_t)(red) << 22) + ((uint32_t)(green) << 12) + ((uint32_t)(blue) << 2); - } - } - }); - } else { - // Take a sub-region - // TODO: Add support for hdr frames - } - } - - std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, - const core::video_format_desc& decklink_format_desc, - const port_configuration& config, - const core::const_frame& frame1, - const core::const_frame& frame2, - BMDFieldDominance field_dominance) - { - std::shared_ptr image_data = allocate_frame_data(decklink_format_desc); - - if (field_dominance != bmdProgressiveFrame) { - convert_frame(channel_format_desc, - decklink_format_desc, - config, - image_data, - field_dominance == bmdUpperFieldFirst, - frame1); - - convert_frame(channel_format_desc, - decklink_format_desc, - config, - image_data, - field_dominance != bmdUpperFieldFirst, - frame2); - - } else { - convert_frame(channel_format_desc, decklink_format_desc, config, image_data, true, frame1); - } - - if (config.key_only) { - // TODO: Add support for hdr frames - } - - return image_data; - } -}; - -frame_factory_hdr_rgbxle::frame_factory_hdr_rgbxle() - : impl_(new impl()) -{ -} - -frame_factory_hdr_rgbxle::~frame_factory_hdr_rgbxle() {} - -BMDPixelFormat frame_factory_hdr_rgbxle::get_pixel_format() { return impl_->get_pixel_format(); } -int frame_factory_hdr_rgbxle::get_row_bytes(int width) { return impl_->get_row_bytes(width); } - -std::shared_ptr frame_factory_hdr_rgbxle::allocate_frame_data(const core::video_format_desc& format_desc) -{ - return impl_->allocate_frame_data(format_desc); -} -std::shared_ptr -frame_factory_hdr_rgbxle::convert_frame_for_port(const core::video_format_desc& channel_format_desc, - const core::video_format_desc& decklink_format_desc, - const port_configuration& config, - const core::const_frame& frame1, - const core::const_frame& frame2, - BMDFieldDominance field_dominance) -{ - return impl_->convert_frame_for_port( - channel_format_desc, decklink_format_desc, config, frame1, frame2, field_dominance); -} - -}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_hdr_v210.cpp b/src/modules/decklink/consumer/frame_factory_hdr_v210.cpp new file mode 100644 index 0000000000..cceca7e608 --- /dev/null +++ b/src/modules/decklink/consumer/frame_factory_hdr_v210.cpp @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2011 Sveriges Television AB + * + * This file is part of CasparCG (www.casparcg.com). + * + * CasparCG 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 3 of the License, or + * (at your option) any later version. + * + * CasparCG 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 CasparCG. If not, see . + * + * Author: Niklas Andersson, niklas@nxtedition.com + */ + +#include "../StdAfx.h" + +#include "frame_factory_hdr_v210.h" + +#include + +#include +#include + +namespace caspar { namespace decklink { + +std::vector create_int_matrix(const std::vector& matrix) +{ + static const float LumaRangeWidth = 876.f * (1024.f / 1023.f); + static const float ChromaRangeWidth = 896.f * (1024.f / 1023.f); + + std::vector color_matrix_f(matrix); + + color_matrix_f[0] *= LumaRangeWidth; + color_matrix_f[1] *= LumaRangeWidth; + color_matrix_f[2] *= LumaRangeWidth; + + color_matrix_f[3] *= ChromaRangeWidth; + color_matrix_f[4] *= ChromaRangeWidth; + color_matrix_f[5] *= ChromaRangeWidth; + color_matrix_f[6] *= ChromaRangeWidth; + color_matrix_f[7] *= ChromaRangeWidth; + color_matrix_f[8] *= ChromaRangeWidth; + + std::vector int_matrix(color_matrix_f.size()); + + transform(color_matrix_f.cbegin(), color_matrix_f.cend(), int_matrix.begin(), [](const float& f) { + return (int32_t)round(f * 1024.f); + }); + + return int_matrix; +}; + +inline void rgb_to_yuv_avx2(__m256i pixel_pairs[4], + const std::vector& color_matrix, + __m256i* luma_out, + __m256i* chroma_out) +{ + /* COMPUTE LUMA */ + { + __m256i y_coeff = + _mm256_broadcastsi128_si256(_mm_set_epi32(0, color_matrix[2], color_matrix[1], color_matrix[0])); + __m256i y_offset = _mm256_set1_epi32(64 << 20); + + // Multiply by y-coefficients + __m256i y4[4]; + for (int i = 0; i < 4; i++) { + y4[i] = _mm256_mullo_epi32(pixel_pairs[i], y_coeff); + } + + // sum products + __m256i y2_sum0123 = _mm256_hadd_epi32(y4[0], y4[1]); + __m256i y2_sum4567 = _mm256_hadd_epi32(y4[2], y4[3]); + __m256i y_sum01452367 = _mm256_hadd_epi32(y2_sum0123, y2_sum4567); + *luma_out = _mm256_srli_epi32(_mm256_add_epi32(y_sum01452367, y_offset), + 20); // add offset and shift down to 10 bit precision + } + + /* COMPUTE CHROMA */ + { + __m256i cb_coeff = + _mm256_broadcastsi128_si256(_mm_set_epi32(0, color_matrix[5], color_matrix[4], color_matrix[3])); + __m256i cr_coeff = + _mm256_broadcastsi128_si256(_mm_set_epi32(0, color_matrix[8], color_matrix[7], color_matrix[6])); + __m256i c_offset = _mm256_set1_epi32((1025) << 19); + + // Multiply by cb-coefficients + __m256i cbcr4[4]; // 0 = cb02, 1 = cr02, 2 = cb46, 3 = cr46 + for (int i = 0; i < 2; i++) { + cbcr4[i * 2] = _mm256_mullo_epi32(pixel_pairs[i * 2], cb_coeff); + cbcr4[i * 2 + 1] = _mm256_mullo_epi32(pixel_pairs[i * 2], cr_coeff); + } + + // sum products + __m256i cbcr_sum02 = _mm256_hadd_epi32(cbcr4[1], cbcr4[0]); + __m256i cbcr_sum46 = _mm256_hadd_epi32(cbcr4[3], cbcr4[2]); + __m256i cbcr_sum_0426 = _mm256_hadd_epi32(cbcr_sum02, cbcr_sum46); + *chroma_out = _mm256_srli_epi32(_mm256_add_epi32(cbcr_sum_0426, c_offset), + 20); // add offset and shift down to 10 bit precision + } +} + +inline void pack_v210_avx2(__m256i luma[6], __m256i chroma[6], __m128i** v210_dest) +{ + __m256i luma_16bit[3]; + __m256i chroma_16bit[3]; + __m256i offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); + for (int i = 0; i < 3; i++) { + auto y16 = _mm256_packus_epi32(luma[i * 2], luma[i * 2 + 1]); + auto cbcr16 = _mm256_packus_epi32(chroma[i * 2], + chroma[i * 2 + 1]); // cbcr0 cbcr4 cbcr8 cbcr12 + // cbcr2 cbcr6 cbcr10 cbcr14 + luma_16bit[i] = + _mm256_permutevar8x32_epi32(y16, + offsets); // layout 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + chroma_16bit[i] = _mm256_permutevar8x32_epi32(cbcr16, + offsets); // cbcr0 cbcr2 cbcr4 cbcr6 cbcr8 cbcr10 cbcr12 cbcr14 + } + + __m128i chroma_mult = _mm_set_epi16(0, 0, 4, 16, 1, 4, 16, 1); + __m128i chroma_shuf = _mm_set_epi8(-1, 11, 10, -1, 9, 8, 7, 6, -1, 5, 4, -1, 3, 2, 1, 0); + + __m128i luma_mult = _mm_set_epi16(0, 0, 16, 1, 4, 16, 1, 4); + __m128i luma_shuf = _mm_set_epi8(11, 10, 9, 8, -1, 7, 6, -1, 5, 4, 3, 2, -1, 1, 0, -1); + + uint16_t* luma_ptr = reinterpret_cast(luma_16bit); + uint16_t* chroma_ptr = reinterpret_cast(chroma_16bit); + for (int i = 0; i < 8; ++i) { + __m128i luma_values = _mm_loadu_si128(reinterpret_cast<__m128i*>(luma_ptr)); + __m128i chroma_values = _mm_loadu_si128(reinterpret_cast<__m128i*>(chroma_ptr)); + __m128i luma_packed = _mm_mullo_epi16(luma_values, luma_mult); + __m128i chroma_packed = _mm_mullo_epi16(chroma_values, chroma_mult); + + luma_packed = _mm_shuffle_epi8(luma_packed, luma_shuf); + chroma_packed = _mm_shuffle_epi8(chroma_packed, chroma_shuf); + + auto res = _mm_or_si128(luma_packed, chroma_packed); + _mm_store_si128((*v210_dest)++, res); + + luma_ptr += 6; + chroma_ptr += 6; + } +} + +struct ARGBPixel +{ + uint16_t R; + uint16_t G; + uint16_t B; + uint16_t A; +}; + +void pack_v210(const ARGBPixel* src, const std::vector& color_matrix, uint32_t* dest, int num_pixels) +{ + auto write_v210 = [dest, index = 0, shift = 0](uint32_t val) mutable { + dest[index] |= ((val & 0x3FF) << shift); + + shift += 10; + if (shift >= 30) { + index++; + shift = 0; + } + }; + + for (int x = 0; x < num_pixels; ++x, ++src) { + auto r = src->R >> 6; + auto g = src->G >> 6; + auto b = src->B >> 6; + + if (x % 2 == 0) { + // Compute Cr + uint32_t v = 1025 << 19; + v += (uint32_t)(color_matrix[6] * r + color_matrix[7] * g + color_matrix[8] * b); + v >>= 20; + write_v210(v); + } + + // Compute Y + uint32_t luma = 64 << 20; + luma += (color_matrix[0] * r + color_matrix[1] * g + color_matrix[2] * b); + luma >>= 20; + write_v210(luma); + + if (x % 2 == 0) { + // Compute Cb + uint32_t u = 1025 << 19; + u += (int32_t)(color_matrix[3] * r + color_matrix[4] * g + color_matrix[5] * b); + u >>= 20; + write_v210(u); + } + } +} + + +struct frame_factory_hdr_v210::impl final +{ + std::vector bt709{0.2126, 0.7152, 0.0722, -0.1146, -0.3854, 0.5, 0.5, -0.4542, -0.0458}; + + public: + impl() = default; + + BMDPixelFormat get_pixel_format() { return bmdFormat10BitYUV; } + + int get_row_bytes(int width) { return ((width + 47) / 48) * 128; } + + std::shared_ptr allocate_frame_data(const core::video_format_desc& format_desc) + { + auto size = get_row_bytes(format_desc.width) * format_desc.height; + return create_aligned_buffer(size, 128); + } + + void convert_frame(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + std::shared_ptr& image_data, + bool topField, + const core::const_frame& frame) + { + // No point copying an empty frame + if (!frame) + return; + + int firstLine = topField ? 0 : 1; + + if (channel_format_desc.format == decklink_format_desc.format && config.src_x == 0 && config.src_y == 0 && + config.region_w == 0 && config.region_h == 0 && config.dest_x == 0 && config.dest_y == 0) { + // Fast path + + auto color_matrix = create_int_matrix(bt709); + + // Pack R16G16B16A16 as v210 + const int NUM_THREADS = 8; + auto rows_per_thread = decklink_format_desc.height / NUM_THREADS; + size_t byte_count_line = get_row_bytes(decklink_format_desc.width); + int fullspeed_x = decklink_format_desc.width / 48; + int rest_x = decklink_format_desc.width - fullspeed_x * 48; + tbb::parallel_for(0, NUM_THREADS, [&](int thread_index) { + auto start = firstLine + thread_index * rows_per_thread; + auto end = (thread_index + 1) * rows_per_thread; + + for (int y = start; y < end; y += decklink_format_desc.field_count) { + auto dest_row = reinterpret_cast(image_data.get()) + (long long)y * byte_count_line / 4; + __m128i* v210_dest = reinterpret_cast<__m128i*>(dest_row); + + // Pack pixels in batches of 48 + for (int x = 0; x < fullspeed_x; x++) { + auto src = reinterpret_cast( + frame.image_data(0).data() + ((long long)y * decklink_format_desc.width + x * 48) * 8); + + const __m256i* pixeldata = reinterpret_cast(src); + + __m256i luma[6]; + __m256i chroma[6]; + + __m256i zero = _mm256_setzero_si256(); + for (int batch_index = 0; batch_index < 6; batch_index++) { + __m256i p0123 = _mm256_load_si256(pixeldata + batch_index * 2); + __m256i p4567 = _mm256_load_si256(pixeldata + batch_index * 2 + 1); + + // shift down to 10 bit precision + p0123 = _mm256_srli_epi16(p0123, 6); + p4567 = _mm256_srli_epi16(p4567, 6); + + // unpack 16 bit values to 32 bit registers, padding with zeros + __m256i pixel_pairs[4]; + pixel_pairs[0] = _mm256_unpacklo_epi16(p0123, zero); // pixels 0 2 + pixel_pairs[1] = _mm256_unpackhi_epi16(p0123, zero); // pixels 1 3 + pixel_pairs[2] = _mm256_unpacklo_epi16(p4567, zero); // pixels 4 6 + pixel_pairs[3] = _mm256_unpackhi_epi16(p4567, zero); // pixels 5 7 + + rgb_to_yuv_avx2(pixel_pairs, color_matrix, &luma[batch_index], &chroma[batch_index]); + } + + pack_v210_avx2(luma, chroma, &v210_dest); + } + + // Pack the final pixels one by one + if (rest_x > 0) { + auto src = reinterpret_cast(frame.image_data(0).data()) + + (y * decklink_format_desc.width + fullspeed_x * 48); + auto dest = reinterpret_cast(v210_dest); + + // clear the remainder of the row + auto rest_bytes = reinterpret_cast(dest_row) + byte_count_line - + reinterpret_cast(dest); + memset(dest, 0, rest_bytes); + + // pack pixels + pack_v210(src, color_matrix, dest, rest_x); + } + } + }); + } else { + // Take a sub-region + // TODO: Add support for hdr frames + } + } + + std::shared_ptr convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) + { + std::shared_ptr image_data = allocate_frame_data(decklink_format_desc); + + if (field_dominance != bmdProgressiveFrame) { + convert_frame(channel_format_desc, + decklink_format_desc, + config, + image_data, + field_dominance == bmdUpperFieldFirst, + frame1); + + convert_frame(channel_format_desc, + decklink_format_desc, + config, + image_data, + field_dominance != bmdUpperFieldFirst, + frame2); + + } else { + convert_frame(channel_format_desc, decklink_format_desc, config, image_data, true, frame1); + } + + if (config.key_only) { + // TODO: Add support for hdr frames + } + + return image_data; + } +}; + +frame_factory_hdr_v210::frame_factory_hdr_v210() + : impl_(new impl()) +{ +} + +frame_factory_hdr_v210::~frame_factory_hdr_v210() {} + +BMDPixelFormat frame_factory_hdr_v210::get_pixel_format() { return impl_->get_pixel_format(); } +int frame_factory_hdr_v210::get_row_bytes(int width) { return impl_->get_row_bytes(width); } + +std::shared_ptr frame_factory_hdr_v210::allocate_frame_data(const core::video_format_desc& format_desc) +{ + return impl_->allocate_frame_data(format_desc); +} +std::shared_ptr +frame_factory_hdr_v210::convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) +{ + return impl_->convert_frame_for_port( + channel_format_desc, decklink_format_desc, config, frame1, frame2, field_dominance); +} + +}} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.h b/src/modules/decklink/consumer/frame_factory_hdr_v210.h similarity index 85% rename from src/modules/decklink/consumer/frame_factory_hdr_rgbxle.h rename to src/modules/decklink/consumer/frame_factory_hdr_v210.h index b03b654eae..5c532ba416 100644 --- a/src/modules/decklink/consumer/frame_factory_hdr_rgbxle.h +++ b/src/modules/decklink/consumer/frame_factory_hdr_v210.h @@ -35,11 +35,13 @@ namespace caspar { namespace decklink { -class frame_factory_hdr_rgbxle : public frame_factory +class frame_factory_hdr_v210 + : public frame_factory + , std::enable_shared_from_this { public: - explicit frame_factory_hdr_rgbxle(); - virtual ~frame_factory_hdr_rgbxle(); + explicit frame_factory_hdr_v210(); + virtual ~frame_factory_hdr_v210(); virtual BMDPixelFormat get_pixel_format(); virtual int get_row_bytes(int width); @@ -54,8 +56,8 @@ class frame_factory_hdr_rgbxle : public frame_factory private: struct impl; std::unique_ptr impl_; - frame_factory_hdr_rgbxle(const frame_factory_hdr_rgbxle&) = delete; - frame_factory_hdr_rgbxle& operator=(const frame_factory_hdr_rgbxle&) = delete; + frame_factory_hdr_v210(const frame_factory_hdr_v210&) = delete; + frame_factory_hdr_v210& operator=(const frame_factory_hdr_v210&) = delete; }; }} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_sdr_bgra.h b/src/modules/decklink/consumer/frame_factory_sdr_bgra.h index 6638777333..cf8fca6916 100644 --- a/src/modules/decklink/consumer/frame_factory_sdr_bgra.h +++ b/src/modules/decklink/consumer/frame_factory_sdr_bgra.h @@ -35,7 +35,9 @@ namespace caspar { namespace decklink { -class frame_factory_sdr_bgra : public frame_factory +class frame_factory_sdr_bgra + : public frame_factory + , std::enable_shared_from_this { public: explicit frame_factory_sdr_bgra(); From e6fcc58c699f6211d58c26a1aa2df2c3a22e364f Mon Sep 17 00:00:00 2001 From: Niklas P Andersson <3985238+niklaspandersson@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:12:42 +0200 Subject: [PATCH 3/4] decklink: better name for format strategies --- src/modules/decklink/CMakeLists.txt | 10 ++--- .../decklink/consumer/decklink_consumer.cpp | 40 +++++++++---------- .../{frame_factory.h => format_strategy.h} | 10 ++--- ...ory_hdr_v210.cpp => hdr_v210_strategy.cpp} | 16 ++++---- ...factory_sdr_bgra.h => hdr_v210_strategy.h} | 16 ++++---- ...ory_sdr_bgra.cpp => sdr_bgra_strategy.cpp} | 16 ++++---- ...factory_hdr_v210.h => sdr_bgra_strategy.h} | 16 ++++---- 7 files changed, 62 insertions(+), 62 deletions(-) rename src/modules/decklink/consumer/{frame_factory.h => format_strategy.h} (89%) rename src/modules/decklink/consumer/{frame_factory_hdr_v210.cpp => hdr_v210_strategy.cpp} (96%) rename src/modules/decklink/consumer/{frame_factory_sdr_bgra.h => hdr_v210_strategy.h} (83%) rename src/modules/decklink/consumer/{frame_factory_sdr_bgra.cpp => sdr_bgra_strategy.cpp} (93%) rename src/modules/decklink/consumer/{frame_factory_hdr_v210.h => sdr_bgra_strategy.h} (83%) diff --git a/src/modules/decklink/CMakeLists.txt b/src/modules/decklink/CMakeLists.txt index 02f8d230ae..4aabbc2cd6 100644 --- a/src/modules/decklink/CMakeLists.txt +++ b/src/modules/decklink/CMakeLists.txt @@ -3,8 +3,8 @@ project (decklink) set(SOURCES consumer/decklink_consumer.cpp - consumer/frame_factory_hdr_v210.cpp - consumer/frame_factory_sdr_bgra.cpp + consumer/hdr_v210_strategy.cpp + consumer/sdr_bgra_strategy.cpp consumer/config.cpp consumer/monitor.cpp @@ -14,9 +14,9 @@ set(SOURCES ) set(HEADERS consumer/decklink_consumer.h - consumer/frame_factory_hdr_v210.h - consumer/frame_factory_sdr_bgra.h - consumer/frame_factory.h + consumer/hdr_v210_strategy.h + consumer/sdr_bgra_strategy.h + consumer/format_strategy.h consumer/config.h consumer/monitor.h diff --git a/src/modules/decklink/consumer/decklink_consumer.cpp b/src/modules/decklink/consumer/decklink_consumer.cpp index ed6081aa93..a21540e724 100644 --- a/src/modules/decklink/consumer/decklink_consumer.cpp +++ b/src/modules/decklink/consumer/decklink_consumer.cpp @@ -25,8 +25,8 @@ #include "common/os/thread.h" #include "config.h" #include "decklink_consumer.h" -#include "frame_factory_hdr_v210.h" -#include "frame_factory_sdr_bgra.h" +#include "hdr_v210_strategy.h" +#include "sdr_bgra_strategy.h" #include "monitor.h" #include "../decklink.h" @@ -194,10 +194,10 @@ core::video_format_desc get_decklink_format(const port_configuration& confi return fallback_format_desc; } -spl::shared_ptr create_frame_factory(bool hdr) +spl::shared_ptr create_format_strategy(bool hdr) { - return hdr ? spl::make_shared() - : spl::make_shared(); + return hdr ? spl::make_shared() + : spl::make_shared(); } enum EOTF @@ -437,7 +437,7 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback { const configuration config_; const port_configuration output_config_; - spl::shared_ptr frame_factory_; + spl::shared_ptr format_strategy_; com_ptr decklink_ = get_device(output_config_.device_index); com_iface_ptr output_ = iface_cast(decklink_); com_iface_ptr keyer_ = iface_cast(decklink_, true); @@ -454,7 +454,7 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback const core::video_format_desc decklink_format_desc_; com_ptr mode_ = get_display_mode(output_, decklink_format_desc_.format, - frame_factory_->get_pixel_format(), + format_strategy_->get_pixel_format(), bmdSupportedVideoModeDefault); decklink_secondary_port(const configuration& config, @@ -465,7 +465,7 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback int device_sync_group) : config_(config) , output_config_(std::move(output_config)) - , frame_factory_(new frame_factory_sdr_bgra()) + , format_strategy_(new sdr_bgra_strategy()) , device_sync_group_(device_sync_group) , channel_format_desc_(std::move(channel_format_desc)) , decklink_format_desc_(get_decklink_format(output_config_, main_decklink_format_desc)) @@ -557,7 +557,7 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback frame1 = frame; } - auto image_data = frame_factory_->convert_frame_for_port( + auto image_data = format_strategy_->convert_frame_for_port( channel_format_desc_, decklink_format_desc_, output_config_, frame1, frame2, mode_->GetFieldDominance()); schedule_next_video(image_data, 0, display_time); @@ -569,8 +569,8 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback decklink_format_desc_, nb_samples, config_.hdr, - frame_factory_->get_pixel_format(), - frame_factory_->get_row_bytes(decklink_format_desc_.width), + format_strategy_->get_pixel_format(), + format_strategy_->get_row_bytes(decklink_format_desc_.width), core::color_space::bt709, config_.hdr_meta)); if (FAILED(output_->ScheduleVideoFrame(get_raw(packed_frame), @@ -600,9 +600,9 @@ struct decklink_secondary_port final : public IDeckLinkVideoOutputCallback struct decklink_consumer final : public IDeckLinkVideoOutputCallback { - const int channel_index_; - const configuration config_; - spl::shared_ptr frame_factory_; + const int channel_index_; + const configuration config_; + spl::shared_ptr format_strategy_; com_ptr decklink_ = get_device(config_.primary.device_index); com_iface_ptr output_ = iface_cast(decklink_); @@ -638,7 +638,7 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback com_ptr mode_ = get_display_mode(output_, decklink_format_desc_.format, - frame_factory_->get_pixel_format(), + format_strategy_->get_pixel_format(), bmdSupportedVideoModeDefault); std::atomic abort_request_{false}; @@ -647,7 +647,7 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback decklink_consumer(const configuration& config, core::video_format_desc channel_format_desc, int channel_index) : channel_index_(channel_index) , config_(config) - , frame_factory_(create_frame_factory(config.hdr)) + , format_strategy_(create_format_strategy(config.hdr)) , channel_format_desc_(std::move(channel_format_desc)) , decklink_format_desc_(get_decklink_format(config.primary, channel_format_desc_)) { @@ -726,7 +726,7 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback nb_samples); } - std::shared_ptr image_data = frame_factory_->allocate_frame_data(decklink_format_desc_); + std::shared_ptr image_data = format_strategy_->allocate_frame_data(decklink_format_desc_); schedule_next_video(image_data, nb_samples, video_scheduled_, config_.hdr_meta.default_color_space); for (auto& context : secondary_port_contexts_) { @@ -943,7 +943,7 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback if (i == -1) { // Primary port std::shared_ptr image_data = - frame_factory_->convert_frame_for_port(channel_format_desc_, + format_strategy_->convert_frame_for_port(channel_format_desc_, decklink_format_desc_, config_.primary, frame1, @@ -1016,8 +1016,8 @@ struct decklink_consumer final : public IDeckLinkVideoOutputCallback BMDTimeValue display_time, core::color_space color_space) { - auto fmt = frame_factory_->get_pixel_format(); - auto row_bytes = frame_factory_->get_row_bytes(decklink_format_desc_.width); + auto fmt = format_strategy_->get_pixel_format(); + auto row_bytes = format_strategy_->get_row_bytes(decklink_format_desc_.width); auto fill_frame = wrap_raw(new decklink_frame( std::move(image_data), decklink_format_desc_, nb_samples, config_.hdr, fmt, row_bytes, color_space, config_.hdr_meta)); if (FAILED(output_->ScheduleVideoFrame( diff --git a/src/modules/decklink/consumer/frame_factory.h b/src/modules/decklink/consumer/format_strategy.h similarity index 89% rename from src/modules/decklink/consumer/frame_factory.h rename to src/modules/decklink/consumer/format_strategy.h index a8a1db16e5..353c459f2d 100644 --- a/src/modules/decklink/consumer/frame_factory.h +++ b/src/modules/decklink/consumer/format_strategy.h @@ -34,16 +34,16 @@ namespace caspar { namespace decklink { -class frame_factory +class format_strategy { protected: - frame_factory() = default; + format_strategy() = default; public: - frame_factory& operator=(const frame_factory&) = delete; - virtual ~frame_factory() = default; + format_strategy& operator=(const format_strategy&) = delete; + virtual ~format_strategy() = default; - frame_factory(const frame_factory&) = delete; + format_strategy(const format_strategy&) = delete; virtual BMDPixelFormat get_pixel_format() = 0; virtual int get_row_bytes(int width) = 0; diff --git a/src/modules/decklink/consumer/frame_factory_hdr_v210.cpp b/src/modules/decklink/consumer/hdr_v210_strategy.cpp similarity index 96% rename from src/modules/decklink/consumer/frame_factory_hdr_v210.cpp rename to src/modules/decklink/consumer/hdr_v210_strategy.cpp index cceca7e608..614fa865da 100644 --- a/src/modules/decklink/consumer/frame_factory_hdr_v210.cpp +++ b/src/modules/decklink/consumer/hdr_v210_strategy.cpp @@ -21,7 +21,7 @@ #include "../StdAfx.h" -#include "frame_factory_hdr_v210.h" +#include "hdr_v210_strategy.h" #include @@ -198,7 +198,7 @@ void pack_v210(const ARGBPixel* src, const std::vector& color_matrix, u } -struct frame_factory_hdr_v210::impl final +struct hdr_v210_strategy::impl final { std::vector bt709{0.2126, 0.7152, 0.0722, -0.1146, -0.3854, 0.5, 0.5, -0.4542, -0.0458}; @@ -338,22 +338,22 @@ struct frame_factory_hdr_v210::impl final } }; -frame_factory_hdr_v210::frame_factory_hdr_v210() +hdr_v210_strategy::hdr_v210_strategy() : impl_(new impl()) { } -frame_factory_hdr_v210::~frame_factory_hdr_v210() {} +hdr_v210_strategy::~hdr_v210_strategy() {} -BMDPixelFormat frame_factory_hdr_v210::get_pixel_format() { return impl_->get_pixel_format(); } -int frame_factory_hdr_v210::get_row_bytes(int width) { return impl_->get_row_bytes(width); } +BMDPixelFormat hdr_v210_strategy::get_pixel_format() { return impl_->get_pixel_format(); } +int hdr_v210_strategy::get_row_bytes(int width) { return impl_->get_row_bytes(width); } -std::shared_ptr frame_factory_hdr_v210::allocate_frame_data(const core::video_format_desc& format_desc) +std::shared_ptr hdr_v210_strategy::allocate_frame_data(const core::video_format_desc& format_desc) { return impl_->allocate_frame_data(format_desc); } std::shared_ptr -frame_factory_hdr_v210::convert_frame_for_port(const core::video_format_desc& channel_format_desc, +hdr_v210_strategy::convert_frame_for_port(const core::video_format_desc& channel_format_desc, const core::video_format_desc& decklink_format_desc, const port_configuration& config, const core::const_frame& frame1, diff --git a/src/modules/decklink/consumer/frame_factory_sdr_bgra.h b/src/modules/decklink/consumer/hdr_v210_strategy.h similarity index 83% rename from src/modules/decklink/consumer/frame_factory_sdr_bgra.h rename to src/modules/decklink/consumer/hdr_v210_strategy.h index cf8fca6916..d04f2c6286 100644 --- a/src/modules/decklink/consumer/frame_factory_sdr_bgra.h +++ b/src/modules/decklink/consumer/hdr_v210_strategy.h @@ -24,7 +24,7 @@ #include "../StdAfx.h" #include "config.h" -#include "frame_factory.h" +#include "format_strategy.h" #include "../decklink_api.h" @@ -35,13 +35,13 @@ namespace caspar { namespace decklink { -class frame_factory_sdr_bgra - : public frame_factory - , std::enable_shared_from_this +class hdr_v210_strategy + : public format_strategy + , std::enable_shared_from_this { public: - explicit frame_factory_sdr_bgra(); - virtual ~frame_factory_sdr_bgra(); + explicit hdr_v210_strategy(); + virtual ~hdr_v210_strategy(); virtual BMDPixelFormat get_pixel_format(); virtual int get_row_bytes(int width); @@ -56,8 +56,8 @@ class frame_factory_sdr_bgra private: struct impl; std::unique_ptr impl_; - frame_factory_sdr_bgra(const frame_factory_sdr_bgra&) = delete; - frame_factory_sdr_bgra& operator=(const frame_factory_sdr_bgra&) = delete; + hdr_v210_strategy(const hdr_v210_strategy&) = delete; + hdr_v210_strategy& operator=(const hdr_v210_strategy&) = delete; }; }} // namespace caspar::decklink diff --git a/src/modules/decklink/consumer/frame_factory_sdr_bgra.cpp b/src/modules/decklink/consumer/sdr_bgra_strategy.cpp similarity index 93% rename from src/modules/decklink/consumer/frame_factory_sdr_bgra.cpp rename to src/modules/decklink/consumer/sdr_bgra_strategy.cpp index 14e7e070dd..ab33333b76 100644 --- a/src/modules/decklink/consumer/frame_factory_sdr_bgra.cpp +++ b/src/modules/decklink/consumer/sdr_bgra_strategy.cpp @@ -22,7 +22,7 @@ #include "../StdAfx.h" -#include "frame_factory_sdr_bgra.h" +#include "sdr_bgra_strategy.h" #include @@ -40,7 +40,7 @@ std::shared_ptr convert_to_key_only(const std::shared_ptr& image_dat return key_data; } -struct frame_factory_sdr_bgra::impl final +struct sdr_bgra_strategy::impl final { public: impl() = default; @@ -181,22 +181,22 @@ struct frame_factory_sdr_bgra::impl final } }; -frame_factory_sdr_bgra::frame_factory_sdr_bgra() +sdr_bgra_strategy::sdr_bgra_strategy() : impl_(new impl()) { } -frame_factory_sdr_bgra::~frame_factory_sdr_bgra() {} +sdr_bgra_strategy::~sdr_bgra_strategy() {} -BMDPixelFormat frame_factory_sdr_bgra::get_pixel_format() { return impl_->get_pixel_format(); } -int frame_factory_sdr_bgra::get_row_bytes(int width) { return impl_->get_row_bytes(width); } +BMDPixelFormat sdr_bgra_strategy::get_pixel_format() { return impl_->get_pixel_format(); } +int sdr_bgra_strategy::get_row_bytes(int width) { return impl_->get_row_bytes(width); } -std::shared_ptr frame_factory_sdr_bgra::allocate_frame_data(const core::video_format_desc& format_desc) +std::shared_ptr sdr_bgra_strategy::allocate_frame_data(const core::video_format_desc& format_desc) { return impl_->allocate_frame_data(format_desc); } std::shared_ptr -frame_factory_sdr_bgra::convert_frame_for_port(const core::video_format_desc& channel_format_desc, +sdr_bgra_strategy::convert_frame_for_port(const core::video_format_desc& channel_format_desc, const core::video_format_desc& decklink_format_desc, const port_configuration& config, const core::const_frame& frame1, diff --git a/src/modules/decklink/consumer/frame_factory_hdr_v210.h b/src/modules/decklink/consumer/sdr_bgra_strategy.h similarity index 83% rename from src/modules/decklink/consumer/frame_factory_hdr_v210.h rename to src/modules/decklink/consumer/sdr_bgra_strategy.h index 5c532ba416..5f5c4d8d99 100644 --- a/src/modules/decklink/consumer/frame_factory_hdr_v210.h +++ b/src/modules/decklink/consumer/sdr_bgra_strategy.h @@ -24,7 +24,7 @@ #include "../StdAfx.h" #include "config.h" -#include "frame_factory.h" +#include "format_strategy.h" #include "../decklink_api.h" @@ -35,13 +35,13 @@ namespace caspar { namespace decklink { -class frame_factory_hdr_v210 - : public frame_factory - , std::enable_shared_from_this +class sdr_bgra_strategy + : public format_strategy + , std::enable_shared_from_this { public: - explicit frame_factory_hdr_v210(); - virtual ~frame_factory_hdr_v210(); + explicit sdr_bgra_strategy(); + virtual ~sdr_bgra_strategy(); virtual BMDPixelFormat get_pixel_format(); virtual int get_row_bytes(int width); @@ -56,8 +56,8 @@ class frame_factory_hdr_v210 private: struct impl; std::unique_ptr impl_; - frame_factory_hdr_v210(const frame_factory_hdr_v210&) = delete; - frame_factory_hdr_v210& operator=(const frame_factory_hdr_v210&) = delete; + sdr_bgra_strategy(const sdr_bgra_strategy&) = delete; + sdr_bgra_strategy& operator=(const sdr_bgra_strategy&) = delete; }; }} // namespace caspar::decklink From c93dbf8c3e5bee59c8e24ce435e73fe54810d5ac Mon Sep 17 00:00:00 2001 From: Niklas P Andersson <3985238+niklaspandersson@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:20:19 +0200 Subject: [PATCH 4/4] chore: clang format --- .../decklink/consumer/hdr_v210_strategy.cpp | 28 +++++++++---------- .../decklink/consumer/hdr_v210_strategy.h | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/modules/decklink/consumer/hdr_v210_strategy.cpp b/src/modules/decklink/consumer/hdr_v210_strategy.cpp index 614fa865da..cbe4060bad 100644 --- a/src/modules/decklink/consumer/hdr_v210_strategy.cpp +++ b/src/modules/decklink/consumer/hdr_v210_strategy.cpp @@ -132,10 +132,10 @@ inline void pack_v210_avx2(__m256i luma[6], __m256i chroma[6], __m128i** v210_de uint16_t* luma_ptr = reinterpret_cast(luma_16bit); uint16_t* chroma_ptr = reinterpret_cast(chroma_16bit); for (int i = 0; i < 8; ++i) { - __m128i luma_values = _mm_loadu_si128(reinterpret_cast<__m128i*>(luma_ptr)); - __m128i chroma_values = _mm_loadu_si128(reinterpret_cast<__m128i*>(chroma_ptr)); - __m128i luma_packed = _mm_mullo_epi16(luma_values, luma_mult); - __m128i chroma_packed = _mm_mullo_epi16(chroma_values, chroma_mult); + __m128i luma_values = _mm_loadu_si128(reinterpret_cast<__m128i*>(luma_ptr)); + __m128i chroma_values = _mm_loadu_si128(reinterpret_cast<__m128i*>(chroma_ptr)); + __m128i luma_packed = _mm_mullo_epi16(luma_values, luma_mult); + __m128i chroma_packed = _mm_mullo_epi16(chroma_values, chroma_mult); luma_packed = _mm_shuffle_epi8(luma_packed, luma_shuf); chroma_packed = _mm_shuffle_epi8(chroma_packed, chroma_shuf); @@ -197,7 +197,6 @@ void pack_v210(const ARGBPixel* src, const std::vector& color_matrix, u } } - struct hdr_v210_strategy::impl final { std::vector bt709{0.2126, 0.7152, 0.0722, -0.1146, -0.3854, 0.5, 0.5, -0.4542, -0.0458}; @@ -283,12 +282,12 @@ struct hdr_v210_strategy::impl final // Pack the final pixels one by one if (rest_x > 0) { auto src = reinterpret_cast(frame.image_data(0).data()) + - (y * decklink_format_desc.width + fullspeed_x * 48); + (y * decklink_format_desc.width + fullspeed_x * 48); auto dest = reinterpret_cast(v210_dest); // clear the remainder of the row - auto rest_bytes = reinterpret_cast(dest_row) + byte_count_line - - reinterpret_cast(dest); + auto rest_bytes = + reinterpret_cast(dest_row) + byte_count_line - reinterpret_cast(dest); memset(dest, 0, rest_bytes); // pack pixels @@ -352,13 +351,12 @@ std::shared_ptr hdr_v210_strategy::allocate_frame_data(const core::video_f { return impl_->allocate_frame_data(format_desc); } -std::shared_ptr -hdr_v210_strategy::convert_frame_for_port(const core::video_format_desc& channel_format_desc, - const core::video_format_desc& decklink_format_desc, - const port_configuration& config, - const core::const_frame& frame1, - const core::const_frame& frame2, - BMDFieldDominance field_dominance) +std::shared_ptr hdr_v210_strategy::convert_frame_for_port(const core::video_format_desc& channel_format_desc, + const core::video_format_desc& decklink_format_desc, + const port_configuration& config, + const core::const_frame& frame1, + const core::const_frame& frame2, + BMDFieldDominance field_dominance) { return impl_->convert_frame_for_port( channel_format_desc, decklink_format_desc, config, frame1, frame2, field_dominance); diff --git a/src/modules/decklink/consumer/hdr_v210_strategy.h b/src/modules/decklink/consumer/hdr_v210_strategy.h index d04f2c6286..e9689d45c3 100644 --- a/src/modules/decklink/consumer/hdr_v210_strategy.h +++ b/src/modules/decklink/consumer/hdr_v210_strategy.h @@ -56,7 +56,7 @@ class hdr_v210_strategy private: struct impl; std::unique_ptr impl_; - hdr_v210_strategy(const hdr_v210_strategy&) = delete; + hdr_v210_strategy(const hdr_v210_strategy&) = delete; hdr_v210_strategy& operator=(const hdr_v210_strategy&) = delete; };