* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
*\r
*/\r
+// TODO: Try to recover consumer from bad_alloc...\r
#include "../StdAfx.h"\r
\r
#ifdef _MSC_VER\r
#include <common/utility/timer.h>\r
#include <common/memory/memshfl.h>\r
\r
-#include <boost/circular_buffer.hpp>\r
+#include <tbb/mutex.h>\r
\r
namespace caspar { namespace core {\r
- \r
+\r
struct output::implementation\r
{ \r
- typedef std::pair<safe_ptr<const read_frame>, safe_ptr<const read_frame>> fill_and_key;\r
+ typedef std::pair<safe_ptr<read_frame>, safe_ptr<read_frame>> fill_and_key;\r
\r
- video_channel_context& channel_;\r
+ video_channel_context& channel_;\r
+ const std::function<void()> restart_channel_;\r
\r
std::map<int, safe_ptr<frame_consumer>> consumers_;\r
typedef std::map<int, safe_ptr<frame_consumer>>::value_type layer_t;\r
high_prec_timer timer_;\r
\r
public:\r
- implementation(video_channel_context& video_channel) \r
+ implementation(video_channel_context& video_channel, const std::function<void()>& restart_channel) \r
: channel_(video_channel)\r
- { \r
- }\r
-\r
- std::pair<size_t, size_t> buffer_depth()\r
- { \r
- auto depth_comp = [](const layer_t& lhs, const layer_t& rhs){ return lhs.second->buffer_depth() < rhs.second->buffer_depth(); };\r
- auto min = std::min_element(consumers_.begin(), consumers_.end(), depth_comp)->second->buffer_depth();\r
- auto max = std::max_element(consumers_.begin(), consumers_.end(), depth_comp)->second->buffer_depth();\r
- CASPAR_ASSERT(max >= min);\r
- return std::make_pair(min, max);\r
- }\r
-\r
+ , restart_channel_(restart_channel)\r
+ {\r
+ } \r
+ \r
void add(int index, safe_ptr<frame_consumer>&& consumer)\r
{ \r
+ channel_.execution().invoke([&]\r
+ {\r
+ consumers_.erase(index);\r
+ });\r
+\r
consumer->initialize(channel_.get_format_desc());\r
+\r
channel_.execution().invoke([&]\r
{\r
- this->remove(index);\r
consumers_.insert(std::make_pair(index, consumer));\r
\r
CASPAR_LOG(info) << print() << L" " << consumer->print() << L" Added.";\r
}\r
\r
void execute(const safe_ptr<read_frame>& frame)\r
- { \r
- try\r
- { \r
- if(!has_synchronization_clock())\r
- timer_.tick(1.0/channel_.get_format_desc().fps);\r
- \r
- auto fill = frame;\r
- auto key = get_key_frame(frame);\r
+ { \r
+ if(!has_synchronization_clock())\r
+ timer_.tick(1.0/channel_.get_format_desc().fps);\r
+\r
+ if(frame->image_size() != channel_.get_format_desc().size)\r
+ {\r
+ timer_.tick(1.0/channel_.get_format_desc().fps);\r
+ return;\r
+ }\r
+ \r
+ auto it = consumers_.begin();\r
+ while(it != consumers_.end())\r
+ {\r
+ auto consumer = it->second;\r
\r
- auto it = consumers_.begin();\r
- while(it != consumers_.end())\r
+ if(consumer->get_video_format_desc() != channel_.get_format_desc())\r
+ consumer->initialize(channel_.get_format_desc());\r
+\r
+ try\r
{\r
+ if(consumer->send(frame))\r
+ ++it;\r
+ else\r
+ consumers_.erase(it++);\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ CASPAR_LOG(warning) << "Trying to restart consumer: " << consumer->print() << L".";\r
try\r
{\r
- auto consumer = it->second;\r
+ consumer->initialize(channel_.get_format_desc());\r
+ consumer->send(frame);\r
+ }\r
+ catch(...)\r
+ { \r
+ CASPAR_LOG_CURRENT_EXCEPTION(); \r
+ CASPAR_LOG(warning) << "Consumer restart failed, trying to restart channel: " << consumer->print() << L"."; \r
\r
- if(consumer->get_video_format_desc() != channel_.get_format_desc())\r
+ try\r
+ {\r
+ restart_channel_();\r
consumer->initialize(channel_.get_format_desc());\r
-\r
- auto frame = consumer->key_only() ? key : fill;\r
-\r
- if(static_cast<size_t>(frame->image_data().size()) == consumer->get_video_format_desc().size)\r
consumer->send(frame);\r
-\r
- ++it;\r
- }\r
- catch(...)\r
- {\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- consumers_.erase(it++);\r
- CASPAR_LOG(error) << print() << L" " << it->second->print() << L" Removed.";\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ CASPAR_LOG(error) << "Failed to recover consumer: " << consumer->print() << L". Removing it.";\r
+ consumers_.erase(it++);\r
+ }\r
}\r
}\r
}\r
- catch(...)\r
- {\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- }\r
}\r
\r
private:\r
return p.second->has_synchronization_clock();\r
});\r
}\r
-\r
- safe_ptr<const read_frame> get_key_frame(const safe_ptr<const read_frame>& frame)\r
- {\r
- bool has_key_only = std::any_of(consumers_.begin(), consumers_.end(), [](const decltype(*consumers_.begin())& p)\r
- {\r
- return p.second->key_only();\r
- });\r
-\r
- if(has_key_only)\r
- {\r
- // Currently do key_only transform on cpu. Unsure if the extra 400MB/s (1080p50) overhead is worth it to do it on gpu.\r
- auto key_data = channel_.ogl().create_host_buffer(frame->image_data().size(), host_buffer::write_only); \r
- fast_memsfhl(key_data->data(), frame->image_data().begin(), frame->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
- std::vector<int16_t> audio_data(frame->audio_data().begin(), frame->audio_data().end());\r
- return make_safe<read_frame>(std::move(key_data), std::move(audio_data));\r
- }\r
- \r
- return make_safe<read_frame>();\r
- }\r
\r
-\r
std::wstring print() const\r
{\r
return L"output";\r
}\r
};\r
\r
-output::output(video_channel_context& video_channel) \r
- : impl_(new implementation(video_channel)){}\r
+output::output(video_channel_context& video_channel, const std::function<void()>& restart_channel) : impl_(new implementation(video_channel, restart_channel)){}\r
void output::add(int index, safe_ptr<frame_consumer>&& consumer){impl_->add(index, std::move(consumer));}\r
void output::remove(int index){impl_->remove(index);}\r
void output::execute(const safe_ptr<read_frame>& frame) {impl_->execute(frame); }\r