Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test PR #2

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/modules/newtek/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,45 @@ project (newtek)
set(SOURCES
consumer/newtek_ivga_consumer.cpp

consumer/newtek_ndi_consumer.cpp

producer/newtek_ndi_producer.cpp

util/air_send.cpp

util/ndi.cpp

newtek.cpp

StdAfx.cpp
)
set(HEADERS
consumer/newtek_ivga_consumer.h

consumer/newtek_ndi_consumer.h

producer/newtek_ndi_producer.h

util/air_send.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
)

Expand All @@ -28,15 +54,19 @@ include_directories(..)
include_directories(../..)
include_directories(${BOOST_INCLUDE_PATH})
include_directories(${TBB_INCLUDE_PATH})
include_directories(${FFMPEG_INCLUDE_PATH})

set_target_properties(newtek PROPERTIES FOLDER modules)
source_group(sources\\consumer consumer/*)
source_group(sources\\producer producer/*)
source_group(sources\\interop interop/*)
source_group(sources\\util util/*)
source_group(sources ./*)

target_link_libraries(newtek
common
core
ffmpeg
)

casparcg_add_include_statement("modules/newtek/newtek.h")
Expand Down
190 changes: 190 additions & 0 deletions src/modules/newtek/consumer/newtek_ndi_consumer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
* Copyright 2018
*
* 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 <http://www.gnu.org/licenses/>.
*
* Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
* based on work of Robert Nagy, ronag89@gmail.com
*/

#include "../StdAfx.h"

#include "newtek_ndi_consumer.h"

#include <core/consumer/frame_consumer.h>
#include <core/frame/frame.h>
#include <core/mixer/audio/audio_util.h>
#include <core/video_format.h>

#include <common/assert.h>
#include <common/diagnostics/graph.h>
#include <common/future.h>
#include <common/param.h>
#include <common/timer.h>

#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>

#include "../util/ndi.h"

namespace caspar { namespace newtek {

struct newtek_ndi_consumer : public core::frame_consumer
{
static int instances_;
const int instance_no_;
const std::wstring name_;
const bool allow_fields_;

core::video_format_desc format_desc_;
NDIlib_v3* ndi_lib_;
NDIlib_send_instance_t ndi_send_instance_;
NDIlib_video_frame_v2_t ndi_video_frame_;
NDIlib_audio_frame_interleaved_32f_t ndi_audio_frame_;
std::shared_ptr<uint8_t> field_data_;
spl::shared_ptr<diagnostics::graph> graph_;
caspar::timer tick_timer_;
caspar::timer frame_timer_;
int frame_no_;

public:
newtek_ndi_consumer(std::wstring name, bool allow_fields)
: name_(!name.empty() ? name : default_ndi_name())
, instance_no_(instances_++)
, frame_no_(0)
, allow_fields_(allow_fields)
{
if (!ndi::load_library()) {
ndi::not_installed();
}

graph_->set_text(print());
graph_->set_color("frame-time", diagnostics::color(0.5f, 1.0f, 0.2f));
graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
diagnostics::register_graph(graph_);
}

~newtek_ndi_consumer() { ndi_lib_->NDIlib_send_destroy(ndi_send_instance_); }

// frame_consumer

void initialize(const core::video_format_desc& format_desc, int channel_index) override
{
format_desc_ = format_desc;

ndi_lib_ = ndi::load_library();
NDIlib_send_create_t NDI_send_create_desc;

auto tmp_name = u8(name_);
NDI_send_create_desc.p_ndi_name = tmp_name.c_str();

ndi_send_instance_ = ndi_lib_->NDIlib_send_create(&NDI_send_create_desc);

ndi_video_frame_.xres = format_desc.width;
ndi_video_frame_.yres = format_desc.height;
ndi_video_frame_.frame_rate_N = format_desc.framerate.numerator();
ndi_video_frame_.frame_rate_D = format_desc.framerate.denominator();
ndi_video_frame_.FourCC = NDIlib_FourCC_type_BGRA;
ndi_video_frame_.line_stride_in_bytes = format_desc.width * 4;
ndi_video_frame_.frame_format_type = NDIlib_frame_format_type_progressive;

if (format_desc.field_count == 2 && allow_fields_) {
ndi_video_frame_.yres /= 2;
ndi_video_frame_.frame_rate_N /= 2;
ndi_video_frame_.picture_aspect_ratio = format_desc.width * 1.0f / format_desc.height;
field_data_.reset(new uint8_t[ndi_video_frame_.line_stride_in_bytes * ndi_video_frame_.yres],
std::default_delete<uint8_t[]>());
ndi_video_frame_.p_data = field_data_.get();
}

ndi_audio_frame_.sample_rate = format_desc_.audio_sample_rate;
ndi_audio_frame_.no_channels = format_desc_.audio_channels;
ndi_audio_frame_.timecode = NDIlib_send_timecode_synthesize;

CASPAR_VERIFY(ndi_send_instance_);
}

std::future<bool> send(core::const_frame frame) override
{
CASPAR_VERIFY(format_desc_.height * format_desc_.width * 4 == frame.image_data(0).size());

graph_->set_value("tick-time", tick_timer_.elapsed() * format_desc_.fps * 0.5);
tick_timer_.restart();
frame_timer_.restart();
auto audio_data = frame.audio_data();
int audio_data_size = static_cast<int>(audio_data.size());
ndi_audio_frame_.no_samples = audio_data_size / format_desc_.audio_channels;
std::vector<float> audio_buffer = ndi::audio_32_to_32f(audio_data.data(), audio_data_size);
ndi_audio_frame_.p_data = const_cast<float*>(audio_buffer.data());
ndi_lib_->NDIlib_util_send_send_audio_interleaved_32f(ndi_send_instance_, &ndi_audio_frame_);
if (format_desc_.field_count == 2 && allow_fields_) {
ndi_video_frame_.frame_format_type =
(frame_no_ % 2 ? NDIlib_frame_format_type_field_1 : NDIlib_frame_format_type_field_0);
for (auto y = 0; y < ndi_video_frame_.yres; ++y) {
std::memcpy(reinterpret_cast<char*>(ndi_video_frame_.p_data) + y * format_desc_.width * 4,
frame.image_data(0).data() + (y * 2 + frame_no_ % 2) * format_desc_.width * 4,
format_desc_.width * 4);
}
} else {
ndi_video_frame_.p_data = const_cast<uint8_t*>(frame.image_data(0).begin());
}
ndi_lib_->NDIlib_send_send_video_v2(ndi_send_instance_, &ndi_video_frame_);
frame_no_++;
graph_->set_value("frame-time", frame_timer_.elapsed() * format_desc_.fps * 0.5);

return make_ready_future(true);
}

std::wstring print() const override { return L"ndi[" + name_ + L"]"; } // TODO: maybe put tally status in the name

std::wstring name() const override { return L"ndi"; }

std::wstring default_ndi_name() const
{
return L"CasparCG" + (instance_no_ ? L" " + boost::lexical_cast<std::wstring>(instance_no_) : L"");
}

int index() const override { return 900; }

bool has_synchronization_clock() const override { return false; }
};

int newtek_ndi_consumer::instances_ = 0;

spl::shared_ptr<core::frame_consumer> create_ndi_consumer(const std::vector<std::wstring>& params,
std::vector<spl::shared_ptr<core::video_channel>> channels)
{
if (params.size() < 1 || !boost::iequals(params.at(0), L"NDI"))
return core::frame_consumer::empty();
std::wstring name;
if (contains_param(L"NAME", params)) {
name = get_param(L"NAME", params);
}
bool allow_fields = contains_param(L"ALLOW_FIELDS", params);
return spl::make_shared<newtek_ndi_consumer>(name, allow_fields);
}

spl::shared_ptr<core::frame_consumer>
create_preconfigured_ndi_consumer(const boost::property_tree::wptree& ptree,
std::vector<spl::shared_ptr<core::video_channel>> channels)
{
auto name = ptree.get(L"name", L"");
bool allow_fields = ptree.get(L"allow-fields", false);
return spl::make_shared<newtek_ndi_consumer>(name, allow_fields);
}

}} // namespace caspar::newtek
41 changes: 41 additions & 0 deletions src/modules/newtek/consumer/newtek_ndi_consumer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2013 Sveriges Television AB http://casparcg.com/
*
* 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 <http://www.gnu.org/licenses/>.
*
* Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
* based on work of Robert Nagy, ronag89@gmail.com work
*/

#pragma once

#include <common/memory.h>

#include <core/fwd.h>

#include <boost/property_tree/ptree_fwd.hpp>

#include <string>

namespace caspar { namespace newtek {

spl::shared_ptr<core::frame_consumer> create_ndi_consumer(const std::vector<std::wstring>& params,
std::vector<spl::shared_ptr<core::video_channel>> channels);
spl::shared_ptr<core::frame_consumer>
create_preconfigured_ndi_consumer(const boost::property_tree::wptree& ptree,
std::vector<spl::shared_ptr<core::video_channel>> channels);

}} // namespace caspar::newtek
Loading