From e9d5c8d04a2cf9f16af914e571137219dc3ca8f9 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Date: Wed, 12 Jun 2024 16:12:07 +0200 Subject: [PATCH] Pull make_frame and related functions from before e3289ecac25f into ndi This is to fix a reversion which causes stretching on the ndi feed --- src/modules/newtek/CMakeLists.txt | 76 ++++-------- .../newtek/producer/newtek_ndi_producer.cpp | 115 +++++++++++++++++- 2 files changed, 138 insertions(+), 53 deletions(-) diff --git a/src/modules/newtek/CMakeLists.txt b/src/modules/newtek/CMakeLists.txt index d57a42746b..3df7784639 100644 --- a/src/modules/newtek/CMakeLists.txt +++ b/src/modules/newtek/CMakeLists.txt @@ -1,51 +1,31 @@ -cmake_minimum_required (VERSION 3.16) -project (newtek) +cmake_minimum_required(VERSION 3.16) +project(newtek) -set(SOURCES - consumer/newtek_ndi_consumer.cpp - - producer/newtek_ndi_producer.cpp - - util/ndi.cpp - - newtek.cpp - -) +set(SOURCES consumer/newtek_ndi_consumer.cpp producer/newtek_ndi_producer.cpp + util/ndi.cpp newtek.cpp) set(HEADERS - consumer/newtek_ndi_consumer.h - - producer/newtek_ndi_producer.h - - util/ndi.h - - newtek.h - - interop/Processing.NDI.compat.h - interop/Processing.NDI.deprecated.h - interop/Processing.NDI.DynamicLoad.h - interop/Processing.NDI.Find.h - interop/Processing.NDI.FrameSync.h - interop/Processing.NDI.Lib.cplusplus.h - interop/Processing.NDI.Lib.h - interop/Processing.NDI.Recv.ex.h - interop/Processing.NDI.Recv.h - interop/Processing.NDI.Routing.h - interop/Processing.NDI.Send.h - interop/Processing.NDI.structs.h - interop/Processing.NDI.utilities.h - - StdAfx.h -) - -casparcg_add_module_project(newtek - SOURCES ${SOURCES} ${HEADERS} - INIT_FUNCTION "newtek::init" -) -target_include_directories(newtek PRIVATE - .. - ../.. - ${FFMPEG_INCLUDE_PATH} - ) + consumer/newtek_ndi_consumer.h + producer/newtek_ndi_producer.h + util/ndi.h + newtek.h + interop/Processing.NDI.compat.h + interop/Processing.NDI.deprecated.h + interop/Processing.NDI.DynamicLoad.h + interop/Processing.NDI.Find.h + interop/Processing.NDI.FrameSync.h + interop/Processing.NDI.Lib.cplusplus.h + interop/Processing.NDI.Lib.h + interop/Processing.NDI.Recv.ex.h + interop/Processing.NDI.Recv.h + interop/Processing.NDI.Routing.h + interop/Processing.NDI.Send.h + interop/Processing.NDI.structs.h + interop/Processing.NDI.utilities.h + StdAfx.h) + +casparcg_add_module_project(newtek SOURCES ${SOURCES} ${HEADERS} INIT_FUNCTION + "newtek::init") +target_include_directories(newtek PRIVATE .. ../.. ${FFMPEG_INCLUDE_PATH}) target_precompile_headers(newtek PRIVATE "StdAfx.h") set_target_properties(newtek PROPERTIES FOLDER modules) @@ -55,6 +35,4 @@ source_group(sources\\interop interop/*) source_group(sources\\util util/*) source_group(sources ./*) -target_link_libraries(newtek - ffmpeg -) +target_link_libraries(newtek ${FFMPEG_LIBRARIES}) diff --git a/src/modules/newtek/producer/newtek_ndi_producer.cpp b/src/modules/newtek/producer/newtek_ndi_producer.cpp index 0596f57999..33e4f4ae66 100644 --- a/src/modules/newtek/producer/newtek_ndi_producer.cpp +++ b/src/modules/newtek/producer/newtek_ndi_producer.cpp @@ -49,8 +49,7 @@ #include #include - -#include +#include #ifdef _MSC_VER #pragma warning(push) @@ -58,6 +57,7 @@ #endif extern "C" { #include +#include } #ifdef _MSC_VER #pragma warning(pop) @@ -67,6 +67,113 @@ extern "C" { namespace caspar { namespace newtek { +std::tuple get_pixel_format(AVPixelFormat pix_fmt) +{ + switch (pix_fmt) { + case AV_PIX_FMT_BGRA: + return {core::pixel_format::bgra, common::bit_depth::bit8}; + case AV_PIX_FMT_RGBA: + return {core::pixel_format::rgba, common::bit_depth::bit8}; + case AV_PIX_FMT_UYVY422: + return {core::pixel_format::uyvy, common::bit_depth::bit8}; + default: + return {core::pixel_format::invalid, common::bit_depth::bit8}; + } +} + +core::pixel_format_desc pixel_format_desc(AVPixelFormat pix_fmt, + int width, + int height, + std::vector& data_map, + core::color_space color_space) +{ + // Get linesizes + int linesizes[4]; + av_image_fill_linesizes(linesizes, pix_fmt, width); + + const auto fmt = get_pixel_format(pix_fmt); + auto desc = core::pixel_format_desc(std::get<0>(fmt), color_space); + auto depth = std::get<1>(fmt); + + switch (desc.format) { + case core::pixel_format::bgra: + case core::pixel_format::argb: + case core::pixel_format::rgba: + case core::pixel_format::abgr: { + desc.planes.push_back(core::pixel_format_desc::plane(linesizes[0] / 4, height, 4, depth)); + return desc; + } + case core::pixel_format::uyvy: { + desc.planes.push_back(core::pixel_format_desc::plane(linesizes[0] / 2, height, 2, depth)); + desc.planes.push_back(core::pixel_format_desc::plane(linesizes[0] / 4, height, 4, depth)); + + data_map.clear(); + data_map.push_back(0); + data_map.push_back(0); + + return desc; + } + default: + desc.format = core::pixel_format::invalid; + return desc; + } +} + +core::mutable_frame make_frame(void* tag, + core::frame_factory& frame_factory, + std::shared_ptr video, + std::shared_ptr audio, + core::color_space color_space) +{ + std::vector data_map; // TODO(perf) when using data_map, avoid uploading duplicate planes + + const auto pix_desc = + video ? pixel_format_desc( + static_cast(video->format), video->width, video->height, data_map, color_space) + : core::pixel_format_desc(core::pixel_format::invalid); + + auto frame = frame_factory.create_frame(tag, pix_desc); + + tbb::parallel_invoke( + [&]() { + if (video) { + for (int n = 0; n < static_cast(pix_desc.planes.size()); ++n) { + auto frame_plan_index = data_map.empty() ? n : data_map.at(n); + + tbb::parallel_for(0, pix_desc.planes[n].height, [&](int y) { + std::memcpy(frame.image_data(n).begin() + y * pix_desc.planes[n].linesize, + video->data[frame_plan_index] + y * video->linesize[frame_plan_index], + pix_desc.planes[n].linesize); + }); + } + } + }, + [&]() { + if (audio) { + const int channel_count = 16; + frame.audio_data() = std::vector(audio->nb_samples * channel_count, 0); + + if (audio->channels == channel_count) { + std::memcpy(frame.audio_data().data(), + reinterpret_cast(audio->data[0]), + sizeof(int32_t) * channel_count * audio->nb_samples); + } else { + // This isn't pretty, but some callers may not provide 16 channels + + auto dst = frame.audio_data().data(); + auto src = reinterpret_cast(audio->data[0]); + for (auto i = 0; i < audio->nb_samples; i++) { + for (auto j = 0; j < std::min(channel_count, audio->channels); ++j) { + dst[i * channel_count + j] = src[i * audio->channels + j]; + } + } + } + } + }); + + return frame; +} + struct newtek_ndi_producer : public core::frame_producer { static std::atomic instances_; @@ -211,8 +318,8 @@ struct newtek_ndi_producer : public core::frame_producer a_frame->nb_samples = audio_frame_32s.no_samples; a_frame->data[0] = reinterpret_cast(audio_frame_32s.p_data); } - auto mframe = - ffmpeg::make_frame(this, *(frame_factory_.get()), std::move(av_frame), std::move(a_frame)); + auto mframe = make_frame( + this, *(frame_factory_.get()), std::move(av_frame), std::move(a_frame), core::color_space::bt709); delete[] audio_frame_32s.p_data; auto dframe = core::draw_frame(std::move(mframe)); {