* Merged possibility for consumers to not participate in frame presentation time synchronization by returning -1 as buffer depth, allowing for unnecessary lags to be avoided when adding/removing consumers that are not expected to present video/audio at a specific time.
* Fixed counter initialization in async destruction proxies (this time it should be fixed).
~destroy_consumer_proxy()
{
- static tbb::atomic<int> counter = []
- {
- tbb::atomic<int> c;
- c = 0;
- return c;
- }();
+ static tbb::atomic<int> counter;
+ static std::once_flag counter_init_once;
+ std::call_once(counter_init_once, []{ counter = 0; });
++counter;
CASPAR_VERIFY(counter < 8);
virtual std::wstring name() const = 0;
virtual boost::property_tree::wptree info() const = 0;
virtual bool has_synchronization_clock() const {return true;}
- virtual int buffer_depth() const = 0;
+ virtual int buffer_depth() const = 0; // -1 to not participate in frame presentation synchronization
virtual int index() const = 0;
};
return cpplinq::from(ports_)
.select(values())
.select(std::mem_fn(&port::buffer_depth))
+ .where([](int v) { return v >= 0; })
.aggregate(minmax::initial_value<int>(), minmax());
}
for (auto it = ports_.begin(); it != ports_.end();)
{
auto& port = it->second;
- auto& frame = frames_.at(port.buffer_depth()-minmax.first);
+ auto depth = port.buffer_depth();
+ auto& frame = depth < 0 ? frames_.back() : frames_.at(depth - minmax.first);
try
{
virtual ~destroy_producer_proxy()
{
- static tbb::atomic<int> counter = []
- {
- tbb::atomic<int> c;
- c = 0;
- return c;
- }();
+ static tbb::atomic<int> counter;
+ static std::once_flag counter_init_once;
+ std::call_once(counter_init_once, []{ counter = 0; });
if(producer_ == core::frame_producer::empty())
return;
if (MSVC)
add_subdirectory(flash)
+ add_subdirectory(newtek)
endif ()
add_subdirectory(reroute)
int buffer_depth() const override
{
- return 1;
+ return -1;
}
int index() const override
int buffer_depth() const override
{
- return 0;
+ return -1;
}
int index() const override
--- /dev/null
+cmake_minimum_required (VERSION 2.6)
+project (newtek)
+
+set(SOURCES
+ consumer/newtek_ivga_consumer.cpp
+
+ util/air_send.cpp
+
+ newtek.cpp
+
+ StdAfx.cpp
+)
+set(HEADERS
+ consumer/newtek_ivga_consumer.h
+
+ util/air_send.h
+
+ newtek.h
+
+ StdAfx.h
+)
+
+add_library(newtek ${SOURCES} ${HEADERS})
+add_precompiled_header(newtek StdAfx.h FORCEINCLUDE)
+
+include_directories(..)
+include_directories(../..)
+include_directories(${BOOST_INCLUDE_PATH})
+include_directories(${TBB_INCLUDE_PATH})
+include_directories(${ASMLIB_INCLUDE_PATH})
+include_directories(${RXCPP_INCLUDE_PATH})
+
+set_target_properties(newtek PROPERTIES FOLDER modules)
+source_group(sources\\consumer consumer/*)
+source_group(sources\\util util/*)
+source_group(sources ./*)
+
+target_link_libraries(newtek
+ common
+ core
+)
+
+casparcg_add_include_statement("modules/newtek/newtek.h")
+casparcg_add_init_statement("newtek::init" "newtek")
+casparcg_add_module_project("newtek")
--- /dev/null
+/*
+* 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: Robert Nagy, ronag89@gmail.com
+*/
+
+// stdafx.cpp : source file that includes just the standard includes
+// dma.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
--- /dev/null
+/*
+* 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: Robert Nagy, ronag89@gmail.com
+*/
+
+#pragma once
+
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+
+#define NOMINMAX
+
+#include <memory>
+#include <array>
+#include <functional>
+#include <algorithm>
+#include <vector>
+#include <deque>
+#include <queue>
+#include <string>
+#include <math.h>
+
+#include <common/memory.h>
+//#include "../common/concurrency/executor.h" // Can't include this due to MSVC lambda bug
+
+#include <common/log.h>
+#include <common/except.h>
+
+#include <assert.h>
--- /dev/null
+/*
+* Copyright 2013 NewTek
+*
+* 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: Robert Nagy, ronag@live.com
+*/
+
+#include "../StdAfx.h"
+
+#include "newtek_ivga_consumer.h"
+
+#include <core/consumer/frame_consumer.h>
+#include <core/video_format.h>
+#include <core/frame/frame.h>
+#include <core/mixer/audio/audio_util.h>
+#include <core/monitor/monitor.h>
+
+#include <common/assert.h>
+#include <common/executor.h>
+#include <common/diagnostics/graph.h>
+#include <common/timer.h>
+#include <common/param.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/timer.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+#include <tbb/atomic.h>
+
+#include "../util/air_send.h"
+
+namespace caspar { namespace newtek {
+
+struct newtek_ivga_consumer : public core::frame_consumer
+{
+ core::monitor::subject monitor_subject_;
+ std::shared_ptr<void> air_send_;
+ core::video_format_desc format_desc_;
+ executor executor_;
+ bool provide_sync_;
+ tbb::atomic<bool> connected_;
+ spl::shared_ptr<diagnostics::graph> graph_;
+ timer tick_timer_;
+ timer frame_timer_;
+
+public:
+
+ newtek_ivga_consumer(bool provide_sync)
+ : executor_(print())
+ , provide_sync_(provide_sync)
+ {
+ if (!airsend::is_available())
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(airsend::dll_name() + L" not available"));
+
+ connected_ = false;
+
+ 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));
+ diagnostics::register_graph(graph_);
+ }
+
+ ~newtek_ivga_consumer()
+ {
+ }
+
+ // frame_consumer
+
+ virtual void initialize(
+ const core::video_format_desc& format_desc,
+ int channel_index) override
+ {
+ air_send_.reset(
+ airsend::create(
+ format_desc.width,
+ format_desc.height,
+ format_desc.time_scale,
+ format_desc.duration,
+ format_desc.field_mode == core::field_mode::progressive,
+ static_cast<float>(format_desc.square_width) / static_cast<float>(format_desc.square_height),
+ true,
+ format_desc.audio_channels,
+ format_desc.audio_sample_rate),
+ airsend::destroy);
+
+ CASPAR_VERIFY(air_send_);
+
+ format_desc_ = format_desc;
+
+ CASPAR_LOG(info) << print() << L" Successfully Initialized.";
+ }
+
+ virtual std::future<bool> send(core::const_frame frame) override
+ {
+ CASPAR_VERIFY(format_desc_.height * format_desc_.width * 4 == frame.image_data().size());
+
+ return executor_.begin_invoke([=]() -> bool
+ {
+ graph_->set_value("tick-time", tick_timer_.elapsed() * format_desc_.fps * 0.5);
+ tick_timer_.restart();
+ frame_timer_.restart();
+
+ // AUDIO
+
+ auto audio_buffer = core::audio_32_to_16(frame.audio_data());
+
+ airsend::add_audio(air_send_.get(), audio_buffer.data(), static_cast<int>(audio_buffer.size()) / format_desc_.audio_channels);
+
+ // VIDEO
+
+ connected_ = airsend::add_frame_bgra(air_send_.get(), frame.image_data().begin());
+
+ graph_->set_text(print());
+ graph_->set_value("frame-time", frame_timer_.elapsed() * format_desc_.fps * 0.5);
+
+ return true;
+ });
+ }
+
+ virtual core::monitor::subject& monitor_output() override
+ {
+ return monitor_subject_;
+ }
+
+ virtual std::wstring print() const override
+ {
+ return connected_ ?
+ L"newtek-ivga[connected]" : L"newtek-ivga[not connected]";
+ }
+
+ virtual std::wstring name() const override
+ {
+ return L"newtek-ivga";
+ }
+
+ virtual boost::property_tree::wptree info() const override
+ {
+ boost::property_tree::wptree info;
+ info.add(L"type", L"newtek-ivga-consumer");
+ info.add(L"connected", connected_ ? L"true" : L"false");
+ return info;
+ }
+
+ virtual int buffer_depth() const override
+ {
+ return -1;
+ }
+
+ virtual int index() const override
+ {
+ return 900;
+ }
+
+ virtual bool has_synchronization_clock() const override
+ {
+ return provide_sync_ && connected_;
+ }
+};
+
+spl::shared_ptr<core::frame_consumer> create_ivga_consumer(const std::vector<std::wstring>& params, core::interaction_sink*)
+{
+ if(params.size() < 1 || params[0] != L"NEWTEK_IVGA")
+ return core::frame_consumer::empty();
+
+ const auto provide_sync = get_param(L"PROVIDE_SYNC", params, true);
+
+ return spl::make_shared<newtek_ivga_consumer>(provide_sync);
+}
+
+spl::shared_ptr<core::frame_consumer> create_preconfigured_ivga_consumer(const boost::property_tree::wptree& ptree, core::interaction_sink*)
+{
+ const auto provide_sync = ptree.get(L"provide-sync", true);
+
+ return spl::make_shared<newtek_ivga_consumer>(provide_sync);
+}
+
+}}
\ No newline at end of file
--- /dev/null
+/*
+* 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: Robert Nagy, ronag89@gmail.com
+*/
+
+#pragma once
+
+#include <common/memory.h>
+
+#include <boost/property_tree/ptree_fwd.hpp>
+
+#include <string>
+
+namespace caspar {
+
+namespace core {
+ class frame_consumer;
+ struct interaction_sink;
+}
+
+namespace newtek {
+
+spl::shared_ptr<core::frame_consumer> create_ivga_consumer(const std::vector<std::wstring>& params, core::interaction_sink*);
+spl::shared_ptr<core::frame_consumer> create_preconfigured_ivga_consumer(const boost::property_tree::wptree& ptree, core::interaction_sink*);
+
+}}
\ No newline at end of file
--- /dev/null
+/*
+* 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: Robert Nagy, ronag89@gmail.com
+*/
+
+#include "newtek.h"
+
+#include "consumer/newtek_ivga_consumer.h"
+#include "util/air_send.h"
+
+#include <core/consumer/frame_consumer.h>
+
+namespace caspar { namespace newtek {
+
+void init(core::module_dependencies dependencies)
+{
+ try
+ {
+ if (airsend::is_available())
+ core::register_consumer_factory(create_ivga_consumer);
+ dependencies.system_info_provider_repo->register_system_info_provider([](boost::property_tree::wptree& info)
+ {
+ info.add(L"system.newtek-ivga.version", airsend::is_available()
+ ? L"available"
+ : L"unavailable (" + airsend::dll_name() + L")");
+ });
+ core::register_preconfigured_consumer_factory(L"newtek-ivga", create_preconfigured_ivga_consumer);
+ }
+ catch(...){}
+}
+
+}}
\ No newline at end of file
--- /dev/null
+/*
+* 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: Robert Nagy, ronag89@gmail.com
+*/
+
+#pragma once
+
+#include <core/module_dependencies.h>
+
+namespace caspar { namespace newtek {
+
+void init(core::module_dependencies dependencies);
+
+}}
\ No newline at end of file
--- /dev/null
+/*
+* 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: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "../StdAfx.h"
+
+#include "air_send.h"
+
+#include <memory>
+
+#include <Windows.h>
+
+#include <common/except.h>
+
+namespace caspar { namespace newtek { namespace airsend {
+
+void* (*create)(
+ const int width, const int height,
+ const int timescale, const int duration,
+ const bool progressive,
+ const float aspect_ratio,
+ const bool audio_enabled,
+ const int num_channels,
+ const int sample_rate) = nullptr;
+void (*destroy)(void* instance) = nullptr;
+bool (*add_audio)(
+ void* instance, const short* samples, const int num_samples) = nullptr;
+bool (*add_frame_bgra)(void* instance, const unsigned char* data) = nullptr;
+
+const std::wstring& dll_name()
+{
+ static std::wstring name = L"Processing.AirSend.x64.dll";
+
+ return name;
+}
+
+std::shared_ptr<void> load_library()
+{
+ auto module = LoadLibrary(dll_name().c_str());
+
+ if (!module)
+ return nullptr;
+
+ std::shared_ptr<void> lib(module, FreeLibrary);
+
+ wchar_t actualFilename[256];
+
+ GetModuleFileNameW(module, actualFilename, sizeof(actualFilename));
+
+ CASPAR_LOG(debug) << L"Loaded " << actualFilename;
+
+ create = reinterpret_cast<decltype(create)>(
+ GetProcAddress(module, "AirSend_Create"));
+ destroy = reinterpret_cast<decltype(destroy)>(
+ GetProcAddress(module, "AirSend_Destroy"));
+ add_audio = reinterpret_cast<decltype(add_audio)>(
+ GetProcAddress(module, "AirSend_add_audio"));
+ add_frame_bgra = reinterpret_cast<decltype(add_frame_bgra)>(
+ GetProcAddress(module, "AirSend_add_frame_bgra"));
+
+ if (create == nullptr
+ || destroy == nullptr
+ || add_audio == nullptr
+ || add_frame_bgra == nullptr)
+ {
+ create = nullptr;
+ destroy = nullptr;
+ add_audio = nullptr;
+ add_frame_bgra = nullptr;
+
+ return nullptr;
+ }
+
+ return lib;
+}
+
+bool is_available()
+{
+ static std::shared_ptr<void> lib = load_library();
+
+ return static_cast<bool>(lib);
+}
+
+}}}
--- /dev/null
+/*
+* 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: Helge Norberg, helge.norberg@svt.se
+*/
+#pragma once
+
+#include <string>
+
+namespace caspar { namespace newtek { namespace airsend {
+
+const std::wstring& dll_name();
+bool is_available();
+
+extern void* (*create)(
+ const int width, const int height,
+ const int timescale, const int duration,
+ const bool progressive,
+ const float aspect_ratio,
+ const bool audio_enabled,
+ const int num_channels,
+ const int sample_rate);
+extern void (*destroy)(void* instance);
+extern bool (*add_audio)(
+ void* instance, const short* samples, const int num_samples);
+extern bool (*add_frame_bgra)(void* instance, const unsigned char* data);
+
+}}}
<vsync>false [true|false]</vsync>\r
<interactive>true [true|false]</interactive>\r
</screen>\r
+ <newtek-ivga>\r
+ <provide-sync>true [true|false]</provide-sync>\r
+ </newtek-ivga>\r
<file>\r
<path></path>\r
<vcodec>libx264 [libx264|qtrle]</vcodec>\r