From: Robert Nagy Date: Sun, 14 Apr 2013 17:12:40 +0000 (+0200) Subject: - Implemented real-time state notification using OSC-UDP. X-Git-Tag: 2.0.6~41^2^2~15 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=07e90b68c9f8641b3f0b6bfb73bd774d2aa743f1;p=casparcg - Implemented real-time state notification using OSC-UDP. Commit sponsored by Boffins Technologies. Notes: - Based on similar functionality in CasparCG 2.1. - Note not all events/messages from 2.1 are implemented. Changes w.r.t CasparCG 2.1: - Better performance. - Use UDP instead of TCP. - Configurable in casparcg.config. - Use "Microsoft Asynchronous Agents Message Blocks" API (included in VS2010+), instead of custom "reactive" solution. --- diff --git a/core/core.vcxproj b/core/core.vcxproj index 872801ac6..fbd0729d9 100644 --- a/core/core.vcxproj +++ b/core/core.vcxproj @@ -253,8 +253,8 @@ + - @@ -312,18 +312,18 @@ ../../../stdafx.h ../../../stdafx.h + + ../stdafx.h + ../stdafx.h + ../stdafx.h + ../stdafx.h + ../../StdAfx.h ../../StdAfx.h ../../StdAfx.h ../../StdAfx.h - - ../../stdafx.h - ../../stdafx.h - ../../stdafx.h - ../../stdafx.h - diff --git a/core/core.vcxproj.filters b/core/core.vcxproj.filters index 1a7f733e3..0fb5e67d1 100644 --- a/core/core.vcxproj.filters +++ b/core/core.vcxproj.filters @@ -34,15 +34,15 @@ {35d7835f-f813-4b4b-8d8d-8a35dfef68d3} - - {80ce21ca-5ecd-48c1-97d2-c20ea8e2f2b6} - {e0a140f8-e217-465c-a934-163b7ea786be} {f2380c6b-6ec8-4a47-8394-357a05eb831a} + + {d8525088-072a-47d2-b6e1-ad662881f505} + @@ -127,9 +127,6 @@ source\mixer\audio - - source\producer\playlist - source\mixer\image\shader @@ -145,6 +142,9 @@ source + + source\monitor + @@ -214,9 +214,6 @@ source\producer\frame - - source\producer\playlist - source\producer @@ -232,5 +229,8 @@ source + + source\monitor + \ No newline at end of file diff --git a/core/monitor/monitor.cpp b/core/monitor/monitor.cpp new file mode 100644 index 000000000..cab17c18b --- /dev/null +++ b/core/monitor/monitor.cpp @@ -0,0 +1,7 @@ +#include "../StdAfx.h" + +#include "monitor.h" + +namespace caspar { namespace core { + +}} \ No newline at end of file diff --git a/core/monitor/monitor.h b/core/monitor/monitor.h new file mode 100644 index 000000000..1e1e504f6 --- /dev/null +++ b/core/monitor/monitor.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +namespace caspar { namespace core { namespace monitor { + +typedef boost::variant> data_t; + +class message +{ +public: + + message(std::string path, std::vector data = std::vector()) + : path_(std::move(path)) + , data_ptr_(std::make_shared>(std::move(data))) + { + CASPAR_ASSERT(path.empty() || path[0] == '/'); + } + + message(std::string path, safe_ptr> data_ptr) + : path_(std::move(path)) + , data_ptr_(std::move(data_ptr)) + { + CASPAR_ASSERT(path.empty() || path[0] == '/'); + } + + const std::string& path() const + { + return path_; + } + + const std::vector& data() const + { + return *data_ptr_; + } + + message propagate(const std::string& path) const + { + return message(path + path_, data_ptr_); + } + + template + message& operator%(T&& data) + { + data_ptr_->push_back(std::forward(data)); + return *this; + } + +private: + std::string path_; + safe_ptr> data_ptr_; +}; + +class subject : public Concurrency::transformer +{ +public: + subject(std::string path = "") + : Concurrency::transformer([=](const message& msg) + { + return msg.propagate(path); + }) + { + CASPAR_ASSERT(path.empty() || path[0] == '/'); + } + + template + subject& operator<<(T&& msg) + { + Concurrency::send(*this, std::forward(msg)); + return *this; + } +}; + +typedef Concurrency::ISource source; + + +}}} \ No newline at end of file diff --git a/core/producer/channel/channel_producer.cpp b/core/producer/channel/channel_producer.cpp index 6d3bdb263..2c52d88bf 100644 --- a/core/producer/channel/channel_producer.cpp +++ b/core/producer/channel/channel_producer.cpp @@ -23,6 +23,7 @@ #include "channel_producer.h" +#include "../../monitor/monitor.h" #include "../../consumer/frame_consumer.h" #include "../../consumer/output.h" #include "../../video_channel.h" @@ -34,7 +35,8 @@ #include #include -#include +#include + #include namespace caspar { namespace core { @@ -125,6 +127,8 @@ public: class channel_producer : public frame_producer { + monitor::subject monitor_subject_; + const safe_ptr frame_factory_; const safe_ptr consumer_; @@ -206,6 +210,11 @@ public: info.add(L"type", L"channel-producer"); return info; } + + monitor::source& monitor_output() + { + return monitor_subject_; + } }; safe_ptr create_channel_producer(const safe_ptr& frame_factory, const safe_ptr& channel) diff --git a/core/producer/color/color_producer.cpp b/core/producer/color/color_producer.cpp index 8582a2741..a6e16a6a1 100644 --- a/core/producer/color/color_producer.cpp +++ b/core/producer/color/color_producer.cpp @@ -23,6 +23,8 @@ #include "color_producer.h" +#include "../../monitor/monitor.h" + #include "../frame/basic_frame.h" #include "../frame/frame_factory.h" #include "../../mixer/write_frame.h" @@ -37,19 +39,23 @@ namespace caspar { namespace core { class color_producer : public frame_producer { - safe_ptr frame_; - const std::wstring color_str_; + monitor::subject monitor_subject_; + safe_ptr frame_; + const std::wstring color_str_; public: explicit color_producer(const safe_ptr& frame_factory, const std::wstring& color) : color_str_(color) , frame_(create_color_frame(this, frame_factory, color)) - {} + { + } // frame_producer virtual safe_ptr receive(int) override { + monitor_subject_ << monitor::message("/color") % color_str_; + return frame_; } @@ -70,6 +76,11 @@ public: info.add(L"color", color_str_); return info; } + + monitor::source& monitor_output() + { + return monitor_subject_; + } }; std::wstring get_hex_color(const std::wstring& str) diff --git a/core/producer/frame_producer.cpp b/core/producer/frame_producer.cpp index 02cc30f3a..581ff31fd 100644 --- a/core/producer/frame_producer.cpp +++ b/core/producer/frame_producer.cpp @@ -26,7 +26,6 @@ #include "frame/frame_transform.h" #include "color/color_producer.h" -#include "playlist/playlist_producer.h" #include "separated/separated_producer.h" #include @@ -105,6 +104,7 @@ public: virtual safe_ptr get_following_producer() const override {return (*producer_)->get_following_producer();} virtual void set_leading_producer(const safe_ptr& producer) override {(*producer_)->set_leading_producer(producer);} virtual uint32_t nb_frames() const override {return (*producer_)->nb_frames();} + virtual monitor::source& monitor_output() {return (*producer_)->monitor_output();} }; safe_ptr create_producer_destroy_proxy(safe_ptr producer) @@ -139,6 +139,7 @@ public: virtual safe_ptr get_following_producer() const override {return (producer_)->get_following_producer();} virtual void set_leading_producer(const safe_ptr& producer) override {(producer_)->set_leading_producer(producer);} virtual uint32_t nb_frames() const override {return (producer_)->nb_frames();} + virtual monitor::source& monitor_output() {return (producer_)->monitor_output();} }; safe_ptr create_producer_print_proxy(safe_ptr producer) @@ -170,6 +171,11 @@ public: info.add(L"type", L"last-frame-producer"); return info; } + virtual monitor::source& monitor_output() + { + static monitor::subject monitor_subject(""); + return monitor_subject; + } }; struct empty_frame_producer : public frame_producer @@ -186,6 +192,12 @@ struct empty_frame_producer : public frame_producer info.add(L"type", L"empty-producer"); return info; } + + virtual monitor::source& monitor_output() + { + static monitor::subject monitor_subject(""); + return monitor_subject; + } }; const safe_ptr& frame_producer::empty() // nothrow @@ -253,10 +265,7 @@ safe_ptr do_create_producer(const safe_ptr& if(producer == frame_producer::empty()) producer = create_color_producer(my_frame_factory, params); - - if(producer == frame_producer::empty()) - producer = create_playlist_producer(my_frame_factory, params); - + return producer; } diff --git a/core/producer/frame_producer.h b/core/producer/frame_producer.h index a29e78d03..8198bb803 100644 --- a/core/producer/frame_producer.h +++ b/core/producer/frame_producer.h @@ -21,6 +21,8 @@ #pragma once +#include "../monitor/monitor.h" + #include #include @@ -75,6 +77,8 @@ public: virtual safe_ptr create_thumbnail_frame(); static const safe_ptr& empty(); // nothrow + + virtual monitor::source& monitor_output() = 0; }; safe_ptr receive_and_follow(safe_ptr& producer, int hints); diff --git a/core/producer/layer.cpp b/core/producer/layer.cpp index 0a0ff51f1..94d74d334 100644 --- a/core/producer/layer.cpp +++ b/core/producer/layer.cpp @@ -37,14 +37,16 @@ struct layer::implementation int64_t frame_number_; int32_t auto_play_delta_; bool is_paused_; + monitor::subject monitor_subject_; public: - implementation() + implementation(int index) : foreground_(frame_producer::empty()) , background_(frame_producer::empty()) , frame_number_(0) , auto_play_delta_(-1) , is_paused_(false) + , monitor_subject_("/layer/" + boost::lexical_cast(index)) { } @@ -80,7 +82,8 @@ public: { background_->set_leading_producer(foreground_); - foreground_ = background_; + set_foreground(background_); + background_ = frame_producer::empty(); frame_number_ = 0; auto_play_delta_ = -1; @@ -91,7 +94,8 @@ public: void stop() { - foreground_ = frame_producer::empty(); + set_foreground(frame_producer::empty()); + background_ = background_; frame_number_ = 0; auto_play_delta_ = -1; @@ -111,7 +115,13 @@ public: return disable_audio(foreground_->last_frame()); } - auto frame = receive_and_follow(foreground_, hints); + auto foreground = foreground_; + + auto frame = receive_and_follow(foreground, hints); + + if(foreground != foreground_) + set_foreground(foreground); + if(frame == core::basic_frame::late()) return foreground_->last_frame(); @@ -157,22 +167,22 @@ public: info.add_child(L"background.producer", background_->info()); return info; } + + void set_foreground(safe_ptr producer) + { + foreground_->monitor_output().unlink_target(&monitor_subject_); + foreground_ = producer; + foreground_->monitor_output().link_target(&monitor_subject_); + } }; -layer::layer() : impl_(new implementation()){} +layer::layer(int index) : impl_(new implementation(index)){} layer::layer(layer&& other) : impl_(std::move(other.impl_)){} layer& layer::operator=(layer&& other) { impl_ = std::move(other.impl_); return *this; } -layer::layer(const layer& other) : impl_(new implementation(*other.impl_)){} -layer& layer::operator=(const layer& other) -{ - layer temp(other); - temp.swap(*this); - return *this; -} void layer::swap(layer& other) { impl_.swap(other.impl_); @@ -189,4 +199,5 @@ safe_ptr layer::background() const { return impl_->background_;} bool layer::empty() const {return impl_->empty();} boost::unique_future layer::call(bool foreground, const std::wstring& param){return impl_->call(foreground, param);} boost::property_tree::wptree layer::info() const{return impl_->info();} +monitor::source& layer::monitor_output(){return impl_->monitor_subject_;} }} \ No newline at end of file diff --git a/core/producer/layer.h b/core/producer/layer.h index 38ccd43a6..46e873364 100644 --- a/core/producer/layer.h +++ b/core/producer/layer.h @@ -21,6 +21,8 @@ #pragma once +#include "../monitor/monitor.h" + #include #include @@ -36,12 +38,12 @@ class basic_frame; class layer : boost::noncopyable { + layer(const layer&); + layer& operator=(const layer&); public: - layer(); // nothrow + layer(int index = -1); // nothrow layer(layer&& other); // nothrow layer& operator=(layer&& other); // nothrow - layer(const layer&); - layer& operator=(const layer&); void swap(layer& other); // nothrow @@ -62,6 +64,8 @@ public: safe_ptr receive(int hints); // nothrow boost::property_tree::wptree info() const; + + monitor::source& monitor_output(); private: struct implementation; safe_ptr impl_; diff --git a/core/producer/playlist/playlist_producer.cpp b/core/producer/playlist/playlist_producer.cpp deleted file mode 100644 index 2068e2a57..000000000 --- a/core/producer/playlist/playlist_producer.cpp +++ /dev/null @@ -1,222 +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: Robert Nagy, ronag89@gmail.com -*/ - -#include "../../stdafx.h" - -#include "playlist_producer.h" - -#include -#include - -#include -#include - -#include - -namespace caspar { namespace core { - -struct playlist_producer : public frame_producer -{ - safe_ptr factory_; - safe_ptr last_frame_; - safe_ptr current_; - bool loop_; - - std::deque> producers_; - - playlist_producer(const safe_ptr& factory, bool loop) - : factory_(factory) - , last_frame_(basic_frame::empty()) - , current_(frame_producer::empty()) - , loop_(loop) - { - } - - // frame_producer - - virtual safe_ptr receive(int hints) override - { - if(current_ == frame_producer::empty() && !producers_.empty()) - next(); - - auto frame = current_->receive(hints); - if(frame == basic_frame::eof()) - { - current_ = frame_producer::empty(); - return receive(hints); - } - - return last_frame_ = frame; - } - - virtual safe_ptr last_frame() const override - { - return disable_audio(last_frame_); - } - - virtual std::wstring print() const override - { - return L"playlist[" + current_->print() + L"]"; - } - - virtual boost::property_tree::wptree info() const override - { - boost::property_tree::wptree info; - info.add(L"type", L"playlist-producer"); - return info; - } - - virtual uint32_t nb_frames() const override - { - return std::numeric_limits::max(); - } - - virtual boost::unique_future call(const std::wstring& param) override - { - boost::promise promise; - promise.set_value(do_call(param)); - return promise.get_future(); - } - - // playlist_producer - - std::wstring do_call(const std::wstring& param) - { - static const boost::wregex push_front_exp (L"PUSH_FRONT (?.+)"); - static const boost::wregex push_back_exp (L"(PUSH_BACK|PUSH) (?.+)"); - static const boost::wregex pop_front_exp (L"POP_FRONT"); - static const boost::wregex pop_back_exp (L"(POP_BACK|POP)"); - static const boost::wregex clear_exp (L"CLEAR"); - static const boost::wregex next_exp (L"NEXT"); - static const boost::wregex insert_exp (L"INSERT (?\\d+) (?.+)"); - static const boost::wregex remove_exp (L"REMOVE (?\\d+) (?.+)"); - static const boost::wregex list_exp (L"LIST"); - static const boost::wregex loop_exp (L"LOOP\\s*(?\\d?)"); - - boost::wsmatch what; - - if(boost::regex_match(param, what, push_front_exp)) - return push_front(what["PARAM"].str()); - else if(boost::regex_match(param, what, push_back_exp)) - return push_back(what["PARAM"].str()); - if(boost::regex_match(param, what, pop_front_exp)) - return pop_front(); - else if(boost::regex_match(param, what, pop_back_exp)) - return pop_back(); - else if(boost::regex_match(param, what, clear_exp)) - return clear(); - else if(boost::regex_match(param, what, next_exp)) - return next(); - else if(boost::regex_match(param, what, insert_exp)) - return insert(boost::lexical_cast(what["POS"].str()), what["PARAM"].str()); - else if(boost::regex_match(param, what, remove_exp)) - return erase(boost::lexical_cast(what["POS"].str())); - else if(boost::regex_match(param, what, list_exp)) - return list(); - else if(boost::regex_match(param, what, loop_exp)) - { - if(!what["VALUE"].str().empty()) - loop_ = boost::lexical_cast(what["VALUE"].str()); - return boost::lexical_cast(loop_); - } - - BOOST_THROW_EXCEPTION(invalid_argument()); - } - - std::wstring push_front(const std::wstring& str) - { - producers_.push_front(create_producer(factory_, str)); - return L""; - } - - std::wstring push_back(const std::wstring& str) - { - producers_.push_back(create_producer(factory_, str)); - return L""; - } - - std::wstring pop_front() - { - producers_.pop_front(); - return L""; - } - - std::wstring pop_back() - { - producers_.pop_back(); - return L""; - } - - std::wstring clear() - { - producers_.clear(); - return L""; - } - - std::wstring next() - { - if(!producers_.empty()) - { - current_ = producers_.front(); - producers_.pop_front(); - //if(loop_) - // producers_.push_back(current_); - } - return L""; - } - - std::wstring insert(size_t pos, const std::wstring& str) - { - if(pos >= producers_.size()) - BOOST_THROW_EXCEPTION(out_of_range()); - producers_.insert(std::begin(producers_) + pos, create_producer(factory_, str)); - return L""; - } - - std::wstring erase(size_t pos) - { - if(pos >= producers_.size()) - BOOST_THROW_EXCEPTION(out_of_range()); - producers_.erase(std::begin(producers_) + pos); - return L""; - } - - std::wstring list() const - { - std::wstring result = L"\n"; - BOOST_FOREACH(auto& producer, producers_) - result += L"\t" + producer->print() + L"\n"; - return result + L""; - } -}; - -safe_ptr create_playlist_producer(const safe_ptr& frame_factory, const std::vector& params) -{ - if(boost::range::find(params, L"[PLAYLIST]") == params.end()) - return core::frame_producer::empty(); - - bool loop = boost::range::find(params, L"LOOP") != params.end(); - - return make_safe(frame_factory, loop); -} - -}} - diff --git a/core/producer/playlist/playlist_producer.h b/core/producer/playlist/playlist_producer.h deleted file mode 100644 index 23c130c3f..000000000 --- a/core/producer/playlist/playlist_producer.h +++ /dev/null @@ -1,34 +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: Robert Nagy, ronag89@gmail.com -*/ - -#pragma once - -#include "../frame_producer.h" - -#include -#include -#include - -namespace caspar { namespace core { - -safe_ptr create_playlist_producer(const safe_ptr& frame_factory, const std::vector& params); - -}} \ No newline at end of file diff --git a/core/producer/separated/separated_producer.cpp b/core/producer/separated/separated_producer.cpp index 9ef26c6f0..6f3ada8f3 100644 --- a/core/producer/separated/separated_producer.cpp +++ b/core/producer/separated/separated_producer.cpp @@ -21,6 +21,8 @@ #include "../../stdafx.h" +#include "../../monitor/monitor.h" + #include "separated_producer.h" #include @@ -31,6 +33,9 @@ namespace caspar { namespace core { struct separated_producer : public frame_producer { + monitor::subject monitor_subject_; + monitor::subject key_monitor_subject_; + safe_ptr fill_producer_; safe_ptr key_producer_; safe_ptr fill_; @@ -38,12 +43,18 @@ struct separated_producer : public frame_producer safe_ptr last_frame_; explicit separated_producer(const safe_ptr& fill, const safe_ptr& key) - : fill_producer_(fill) + : monitor_subject_("") + , key_monitor_subject_("/keyer") + , fill_producer_(fill) , key_producer_(key) , fill_(core::basic_frame::late()) , key_(core::basic_frame::late()) , last_frame_(core::basic_frame::empty()) { + key_monitor_subject_.link_target(&monitor_subject_); + + key_producer_->monitor_output().link_target(&key_monitor_subject_); + fill_producer_->monitor_output().link_target(&monitor_subject_); } // frame_producer @@ -116,6 +127,11 @@ struct separated_producer : public frame_producer info.add_child(L"key.producer", key_producer_->info()); return info; } + + monitor::source& monitor_output() + { + return monitor_subject_; + } }; safe_ptr create_separated_producer(const safe_ptr& fill, const safe_ptr& key) diff --git a/core/producer/stage.cpp b/core/producer/stage.cpp index 3422cf4c2..59fafd065 100644 --- a/core/producer/stage.cpp +++ b/core/producer/stage.cpp @@ -96,15 +96,18 @@ struct stage::implementation : public std::enable_shared_from_this layers_; + std::map> layers_; tbb::concurrent_unordered_map> transforms_; + + monitor::subject monitor_subject_; - executor executor_; + executor executor_; public: implementation(const safe_ptr& graph, const safe_ptr& target, const video_format_desc& format_desc) : graph_(graph) , format_desc_(format_desc) , target_(target) + , monitor_subject_("/stage") , executor_(L"stage") { graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f, 0.8)); @@ -125,10 +128,10 @@ public: std::map> frames; - BOOST_FOREACH(auto& layer, layers_) - frames[layer.first] = basic_frame::empty(); + for(auto it = layers_.begin(); it != layers_.end(); ++it) + frames[it->first] = basic_frame::empty(); - tbb::parallel_for_each(layers_.begin(), layers_.end(), [&](std::map::value_type& layer) + tbb::parallel_for_each(layers_.begin(), layers_.end(), [&](std::map>::value_type& layer) { auto transform = transforms_[layer.first].fetch_and_tick(1); @@ -142,7 +145,7 @@ public: if(transform.is_key) hints |= frame_producer::ALPHA_HINT; - auto frame = layer.second.receive(hints); + auto frame = layer.second->receive(hints); auto frame1 = make_safe(frame); frame1->get_frame_transform() = transform; @@ -228,11 +231,22 @@ public: }, high_priority); } + layer& get_layer(int index) + { + auto it = layers_.find(index); + if(it == std::end(layers_)) + { + it = layers_.insert(std::make_pair(index, std::make_shared(index))).first; + it->second->monitor_output().link_target(&monitor_subject_); + } + return *it->second; + } + void load(int index, const safe_ptr& producer, bool preview, int auto_play_delta) { executor_.begin_invoke([=] { - layers_[index].load(producer, preview, auto_play_delta); + get_layer(index).load(producer, preview, auto_play_delta); }, high_priority); } @@ -240,7 +254,7 @@ public: { executor_.begin_invoke([=] { - layers_[index].pause(); + get_layer(index).pause(); }, high_priority); } @@ -248,7 +262,7 @@ public: { executor_.begin_invoke([=] { - layers_[index].play(); + get_layer(index).play(); }, high_priority); } @@ -256,7 +270,7 @@ public: { executor_.begin_invoke([=] { - layers_[index].stop(); + get_layer(index).stop(); }, high_priority); } @@ -280,47 +294,77 @@ public: { return std::move(*executor_.invoke([=] { - return std::make_shared>(std::move(layers_[index].call(foreground, param))); + return std::make_shared>(std::move(get_layer(index).call(foreground, param))); }, high_priority)); } - void swap_layers(const safe_ptr& other) + void swap_layers(stage& other) { - if(other->impl_.get() == this) + auto other_impl = other.impl_; + + if(other_impl.get() == this) return; auto func = [=] { - std::swap(layers_, other->impl_->layers_); + auto layers = layers_ | boost::adaptors::map_values; + auto other_layers = other_impl->layers_ | boost::adaptors::map_values; + + BOOST_FOREACH(auto& layer, layers) + layer->monitor_output().unlink_target(&monitor_subject_); + + BOOST_FOREACH(auto& layer, other_layers) + layer->monitor_output().unlink_target(&monitor_subject_); + + std::swap(layers_, other_impl->layers_); + + BOOST_FOREACH(auto& layer, layers) + layer->monitor_output().link_target(&monitor_subject_); + + BOOST_FOREACH(auto& layer, other_layers) + layer->monitor_output().link_target(&monitor_subject_); }; + executor_.begin_invoke([=] { - other->impl_->executor_.invoke(func, high_priority); - }, high_priority); + other_impl->executor_.invoke(func, task_priority::high_priority); + }, task_priority::high_priority); } - void swap_layer(int index, size_t other_index) + void swap_layer(int index, int other_index) { executor_.begin_invoke([=] { - std::swap(layers_[index], layers_[other_index]); - }, high_priority); + std::swap(get_layer(index), get_layer(other_index)); + }, task_priority::high_priority); } - void swap_layer(int index, size_t other_index, const safe_ptr& other) + void swap_layer(int index, int other_index, stage& other) { - if(other->impl_.get() == this) + auto other_impl = other.impl_; + + if(other_impl.get() == this) swap_layer(index, other_index); else { auto func = [=] { - std::swap(layers_[index], other->impl_->layers_[other_index]); + auto& my_layer = get_layer(index); + auto& other_layer = other_impl->get_layer(other_index); + + my_layer.monitor_output().unlink_target(&monitor_subject_); + other_layer.monitor_output().unlink_target(&other_impl->monitor_subject_); + + std::swap(my_layer, other_layer); + + my_layer.monitor_output().link_target(&monitor_subject_); + other_layer.monitor_output().link_target(&other_impl->monitor_subject_); }; + executor_.begin_invoke([=] { - other->impl_->executor_.invoke(func, high_priority); - }, high_priority); + other_impl->executor_.invoke(func, task_priority::high_priority); + }, task_priority::high_priority); } } @@ -328,7 +372,7 @@ public: { return executor_.begin_invoke([=] { - return layers_[index].foreground(); + return get_layer(index).foreground(); }, high_priority); } @@ -336,7 +380,7 @@ public: { return executor_.begin_invoke([=] { - return layers_[index].background(); + return get_layer(index).background(); }, high_priority); } @@ -354,7 +398,7 @@ public: { boost::property_tree::wptree info; BOOST_FOREACH(auto& layer, layers_) - info.add_child(L"layers.layer", layer.second.info()) + info.add_child(L"layers.layer", layer.second->info()) .add(L"index", layer.first); return info; }, high_priority)); @@ -364,12 +408,13 @@ public: { return std::move(executor_.begin_invoke([=]() -> boost::property_tree::wptree { - return layers_[index].info(); + return get_layer(index).info(); }, high_priority)); } }; -stage::stage(const safe_ptr& graph, const safe_ptr& target, const video_format_desc& format_desc) : impl_(new implementation(graph, target, format_desc)){} +stage::stage(const safe_ptr& graph, const safe_ptr& target, const video_format_desc& format_desc) + : impl_(new implementation(graph, target, format_desc)){} void stage::apply_transforms(const std::vector& transforms){impl_->apply_transforms(transforms);} void stage::apply_transform(int index, const std::function& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);} void stage::clear_transforms(int index){impl_->clear_transforms(index);} @@ -381,13 +426,14 @@ void stage::play(int index){impl_->play(index);} void stage::stop(int index){impl_->stop(index);} void stage::clear(int index){impl_->clear(index);} void stage::clear(){impl_->clear();} -void stage::swap_layers(const safe_ptr& other){impl_->swap_layers(other);} +void stage::swap_layers(const safe_ptr& other){impl_->swap_layers(*other);} void stage::swap_layer(int index, size_t other_index){impl_->swap_layer(index, other_index);} -void stage::swap_layer(int index, size_t other_index, const safe_ptr& other){impl_->swap_layer(index, other_index, other);} +void stage::swap_layer(int index, size_t other_index, const safe_ptr& other){impl_->swap_layer(index, other_index, *other);} boost::unique_future> stage::foreground(int index) {return impl_->foreground(index);} boost::unique_future> stage::background(int index) {return impl_->background(index);} boost::unique_future stage::call(int index, bool foreground, const std::wstring& param){return impl_->call(index, foreground, param);} void stage::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);} boost::unique_future stage::info() const{return impl_->info();} boost::unique_future stage::info(int index) const{return impl_->info(index);} +monitor::source& stage::monitor_output(){return impl_->monitor_subject_;} }} \ No newline at end of file diff --git a/core/producer/stage.h b/core/producer/stage.h index e7178caa0..403498d90 100644 --- a/core/producer/stage.h +++ b/core/producer/stage.h @@ -23,6 +23,8 @@ #include "frame_producer.h" +#include "../monitor/monitor.h" + #include #include #include @@ -41,13 +43,18 @@ struct frame_transform; class stage : boost::noncopyable { public: + + // Static Members + typedef std::function transform_func_t; typedef std::tuple transform_tuple_t; typedef target>, std::shared_ptr>> target_t; + // Constructors + explicit stage(const safe_ptr& graph, const safe_ptr& target, const video_format_desc& format_desc); - // stage + // Methods void apply_transforms(const std::vector& transforms); void apply_transform(int index, const transform_func_t& transform, unsigned int mix_duration = 0, const std::wstring& tween = L"linear"); @@ -67,6 +74,9 @@ public: void swap_layer(int index, size_t other_index, const safe_ptr& other); boost::unique_future call(int index, bool foreground, const std::wstring& param); + + // Properties + boost::unique_future> foreground(int index); boost::unique_future> background(int index); @@ -74,6 +84,8 @@ public: boost::unique_future info(int layer) const; void set_video_format_desc(const video_format_desc& format_desc); + + monitor::source& monitor_output(); private: struct implementation; diff --git a/core/producer/transition/transition_producer.cpp b/core/producer/transition/transition_producer.cpp index 88f56b9de..1375ed0e6 100644 --- a/core/producer/transition/transition_producer.cpp +++ b/core/producer/transition/transition_producer.cpp @@ -38,6 +38,8 @@ namespace caspar { namespace core { struct transition_producer : public frame_producer { + monitor::subject monitor_subject_; + const field_mode::type mode_; unsigned int current_frame_; @@ -54,7 +56,10 @@ struct transition_producer : public frame_producer , info_(info) , dest_producer_(dest) , source_producer_(frame_producer::empty()) - , last_frame_(basic_frame::empty()){} + , last_frame_(basic_frame::empty()) + { + dest->monitor_output().link_target(&monitor_subject_); + } // frame_producer @@ -95,6 +100,20 @@ struct transition_producer : public frame_producer source = source_producer_->last_frame(); }); + monitor_subject_ << monitor::message("/transition/frame") % static_cast(current_frame_) % static_cast(info_.duration) + << monitor::message("/transition/type") % [&]() -> std::string + { + switch(info_.type) + { + case transition::mix: return "mix"; + case transition::wipe: return "wipe"; + case transition::slide: return "slide"; + case transition::push: return "push"; + case transition::cut: return "cut"; + default: return "n/a"; + } + }(); + return compose(dest, source); } @@ -186,6 +205,11 @@ struct transition_producer : public frame_producer return basic_frame::combine(s_frame, d_frame); } + + monitor::source& monitor_output() + { + return monitor_subject_; + } }; safe_ptr create_transition_producer(const field_mode::type& mode, const safe_ptr& destination, const transition_info& info) diff --git a/core/video_channel.cpp b/core/video_channel.cpp index 4cad33609..d8f864ee4 100644 --- a/core/video_channel.cpp +++ b/core/video_channel.cpp @@ -41,6 +41,7 @@ namespace caspar { namespace core { struct video_channel::implementation : boost::noncopyable { + video_channel& self_; const int index_; video_format_desc format_desc_; const safe_ptr ogl_; @@ -49,15 +50,19 @@ struct video_channel::implementation : boost::noncopyable const safe_ptr output_; const safe_ptr mixer_; const safe_ptr stage_; + + monitor::subject monitor_subject_; public: - implementation(int index, const video_format_desc& format_desc, const safe_ptr& ogl) - : index_(index) + implementation(video_channel& self, int index, const video_format_desc& format_desc, const safe_ptr& ogl) + : self_(self) + , index_(index) , format_desc_(format_desc) , ogl_(ogl) , output_(new caspar::core::output(graph_, format_desc, index)) , mixer_(new caspar::core::mixer(graph_, output_, format_desc, ogl)) , stage_(new caspar::core::stage(graph_, mixer_, format_desc)) + , monitor_subject_("/channel/" + boost::lexical_cast(index)) { graph_->set_text(print()); diagnostics::register_graph(graph_); @@ -65,6 +70,8 @@ public: for(int n = 0; n < std::max(1, env::properties().get(L"configuration.pipeline-tokens", 2)); ++n) stage_->spawn_token(); + stage_->monitor_output().link_target(&monitor_subject_); + CASPAR_LOG(info) << print() << " Successfully Initialized."; } @@ -116,7 +123,8 @@ public: } }; -video_channel::video_channel(int index, const video_format_desc& format_desc, const safe_ptr& ogl) : impl_(new implementation(index, format_desc, ogl)){} +video_channel::video_channel(int index, const video_format_desc& format_desc, const safe_ptr& ogl) + : impl_(new implementation(*this, index, format_desc, ogl)){} safe_ptr video_channel::stage() { return impl_->stage_;} safe_ptr video_channel::mixer() { return impl_->mixer_;} safe_ptr video_channel::output() { return impl_->output_;} @@ -124,5 +132,5 @@ video_format_desc video_channel::get_video_format_desc() const{return impl_->for void video_channel::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);} boost::property_tree::wptree video_channel::info() const{return impl_->info();} int video_channel::index() const {return impl_->index_;} - +monitor::source& video_channel::monitor_output(){return impl_->monitor_subject_;} }} \ No newline at end of file diff --git a/core/video_channel.h b/core/video_channel.h index c69bd636e..89312db1d 100644 --- a/core/video_channel.h +++ b/core/video_channel.h @@ -21,12 +21,15 @@ #pragma once +#include "monitor/monitor.h" + #include #include - #include +#include + namespace caspar { namespace core { class stage; @@ -38,8 +41,17 @@ struct video_format_desc; class video_channel : boost::noncopyable { public: + + // Static Members + + // Constructors + explicit video_channel(int index, const video_format_desc& format_desc, const safe_ptr& ogl); + // Methods + + // Properties + safe_ptr stage(); safe_ptr mixer(); safe_ptr output(); @@ -50,6 +62,8 @@ public: boost::property_tree::wptree info() const; int index() const; + + monitor::source& monitor_output(); private: struct implementation; diff --git a/modules/decklink/producer/decklink_producer.cpp b/modules/decklink/producer/decklink_producer.cpp index 2723a6489..b6b288594 100644 --- a/modules/decklink/producer/decklink_producer.cpp +++ b/modules/decklink/producer/decklink_producer.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -79,6 +80,7 @@ namespace caspar { namespace decklink { class decklink_producer : boost::noncopyable, public IDeckLinkInputCallback { + core::monitor::subject monitor_subject_; safe_ptr graph_; boost::timer tick_timer_; boost::timer frame_timer_; @@ -272,6 +274,11 @@ public: { return model_name_ + L" [" + boost::lexical_cast(device_index_) + L"|" + format_desc_.name + L"]"; } + + core::monitor::source& monitor_output() + { + return monitor_subject_; + } }; class decklink_producer_proxy : public core::frame_producer @@ -320,6 +327,11 @@ public: info.add(L"type", L"decklink-producer"); return info; } + + core::monitor::source& monitor_output() + { + return context_->monitor_output(); + } }; safe_ptr create_producer(const safe_ptr& frame_factory, const std::vector& params) diff --git a/modules/ffmpeg/producer/ffmpeg_producer.cpp b/modules/ffmpeg/producer/ffmpeg_producer.cpp index e5b034321..f3936f14c 100644 --- a/modules/ffmpeg/producer/ffmpeg_producer.cpp +++ b/modules/ffmpeg/producer/ffmpeg_producer.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,7 @@ namespace caspar { namespace ffmpeg { struct ffmpeg_producer : public core::frame_producer { + core::monitor::subject monitor_subject_; const std::wstring filename_; const safe_ptr graph_; @@ -88,7 +90,7 @@ struct ffmpeg_producer : public core::frame_producer int64_t frame_number_; uint32_t file_frame_number_; - + public: explicit ffmpeg_producer(const safe_ptr& frame_factory, const std::wstring& filename, const std::wstring& filter, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode) : filename_(filename) @@ -190,10 +192,20 @@ public: graph_->set_text(print()); last_frame_ = frame.first; + + monitor_subject_ << core::monitor::message("/profiler/time") % frame_timer_.elapsed() % (1.0/format_desc_.fps); + + monitor_subject_ << core::monitor::message("/file/time") % (file_frame_number()/fps_) + % (file_nb_frames()/fps_) + << core::monitor::message("/file/frame") % static_cast(file_frame_number()) + % static_cast(file_nb_frames()) + << core::monitor::message("/file/fps") % fps_ + << core::monitor::message("/file/path") % filename_ + << core::monitor::message("/loop") % input_.loop(); return frame; } - + safe_ptr render_specific_frame(uint32_t file_position, int hints) { // Some trial and error and undeterministic stuff here @@ -285,6 +297,11 @@ public: return make_safe(frames); } + + uint32_t file_frame_number() const + { + return video_decoder_ ? video_decoder_->file_frame_number() : 0; + } virtual uint32_t nb_frames() const override { @@ -420,6 +437,11 @@ public: for(auto frame = muxer_->poll(); frame; frame = muxer_->poll()) frame_buffer_.push(std::make_pair(make_safe_ptr(frame), file_frame_number)); } + + core::monitor::source& monitor_output() + { + return monitor_subject_; + } }; safe_ptr create_producer(const safe_ptr& frame_factory, const std::vector& params) diff --git a/modules/flash/producer/cg_producer.cpp b/modules/flash/producer/cg_producer.cpp index e49cf2fd7..2432d955e 100644 --- a/modules/flash/producer/cg_producer.cpp +++ b/modules/flash/producer/cg_producer.cpp @@ -191,6 +191,11 @@ public: return result.get(); return L""; } + + core::monitor::source& monitor_output() + { + return flash_producer_->monitor_output(); + } }; safe_ptr get_default_cg_producer(const safe_ptr& video_channel, int render_layer) @@ -263,5 +268,5 @@ std::wstring cg_producer::invoke(int layer, const std::wstring& label){return im std::wstring cg_producer::description(int layer){return impl_->timed_description(layer);} std::wstring cg_producer::template_host_info(){return impl_->timed_template_host_info();} boost::property_tree::wptree cg_producer::info() const{return impl_->info();} - +core::monitor::source& cg_producer::monitor_output(){return impl_->monitor_output();} }} \ No newline at end of file diff --git a/modules/flash/producer/cg_producer.h b/modules/flash/producer/cg_producer.h index 875b3cf30..2a6920d48 100644 --- a/modules/flash/producer/cg_producer.h +++ b/modules/flash/producer/cg_producer.h @@ -60,6 +60,8 @@ public: std::wstring description(int layer); std::wstring template_host_info(); + core::monitor::source& monitor_output(); + private: struct implementation; std::shared_ptr impl_; diff --git a/modules/flash/producer/flash_producer.cpp b/modules/flash/producer/flash_producer.cpp index 7afb1bdfd..0294155f8 100644 --- a/modules/flash/producer/flash_producer.cpp +++ b/modules/flash/producer/flash_producer.cpp @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -320,6 +321,7 @@ public: struct flash_producer : public core::frame_producer { + core::monitor::subject monitor_subject_; const std::wstring filename_; const safe_ptr frame_factory_; const int width_; @@ -379,6 +381,12 @@ public: next(); else graph_->set_tag("late-frame"); + + monitor_subject_ << core::monitor::message("/host/path") % filename_ + << core::monitor::message("/host/width") % width_ + << core::monitor::message("/host/height") % height_ + << core::monitor::message("/host/fps") % fps_ + << core::monitor::message("/buffer") % output_buffer_.size() % buffer_size_; return frame; } @@ -488,6 +496,11 @@ public: }); return frame; } + + core::monitor::source& monitor_output() + { + return monitor_subject_; + } }; safe_ptr create_producer(const safe_ptr& frame_factory, const std::vector& params) diff --git a/modules/image/producer/image_producer.cpp b/modules/image/producer/image_producer.cpp index 192a5304a..4b240bc3e 100644 --- a/modules/image/producer/image_producer.cpp +++ b/modules/image/producer/image_producer.cpp @@ -25,6 +25,7 @@ #include +#include #include #include #include @@ -44,7 +45,8 @@ namespace caspar { namespace image { struct image_producer : public core::frame_producer { - const std::wstring filename_; + core::monitor::subject monitor_subject_; + const std::wstring filename_; safe_ptr frame_; explicit image_producer(const safe_ptr& frame_factory, const std::wstring& filename) @@ -68,6 +70,8 @@ struct image_producer : public core::frame_producer virtual safe_ptr receive(int) override { + monitor_subject_ << core::monitor::message("/file/path") % filename_; + return frame_; } @@ -93,6 +97,11 @@ struct image_producer : public core::frame_producer info.add(L"filename", filename_); return info; } + + core::monitor::source& monitor_output() + { + return monitor_subject_; + } }; safe_ptr create_raw_producer(const safe_ptr& frame_factory, const std::vector& params) diff --git a/modules/image/producer/image_scroll_producer.cpp b/modules/image/producer/image_scroll_producer.cpp index 558900e73..c7e365ae7 100644 --- a/modules/image/producer/image_scroll_producer.cpp +++ b/modules/image/producer/image_scroll_producer.cpp @@ -28,6 +28,7 @@ #include +#include #include #include #include @@ -57,6 +58,7 @@ namespace caspar { namespace image { struct image_scroll_producer : public core::frame_producer { + core::monitor::subject monitor_subject_; const std::wstring filename_; std::vector> frames_; core::video_format_desc format_desc_; @@ -381,6 +383,11 @@ struct image_scroll_producer : public core::frame_producer return static_cast(length / std::abs(speed_));// + length % std::abs(delta_)); } } + + core::monitor::source& monitor_output() + { + return monitor_subject_; + } }; safe_ptr create_scroll_producer(const safe_ptr& frame_factory, const std::vector& params) diff --git a/protocol/osc/oscpack/MessageMappingOscPacketListener.h b/protocol/osc/oscpack/MessageMappingOscPacketListener.h new file mode 100644 index 000000000..ade00e5b7 --- /dev/null +++ b/protocol/osc/oscpack/MessageMappingOscPacketListener.h @@ -0,0 +1,73 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H +#define INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H + +#include +#include + +#include "OscPacketListener.h" + + + +namespace osc{ + +template< class T > +class MessageMappingOscPacketListener : public OscPacketListener{ +public: + typedef void (T::*function_type)(const osc::ReceivedMessage&, const IpEndpointName&); + +protected: + void RegisterMessageFunction( const char *addressPattern, function_type f ) + { + functions_.insert( std::make_pair( addressPattern, f ) ); + } + + virtual void ProcessMessage( const osc::ReceivedMessage& m, + const IpEndpointName& remoteEndpoint ) + { + typename function_map_type::iterator i = functions_.find( m.AddressPattern() ); + if( i != functions_.end() ) + (dynamic_cast(this)->*(i->second))( m, remoteEndpoint ); + } + +private: + struct cstr_compare{ + bool operator()( const char *lhs, const char *rhs ) const + { return strcmp( lhs, rhs ) < 0; } + }; + + typedef std::map function_map_type; + function_map_type functions_; +}; + +} // namespace osc + +#endif /* INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H */ \ No newline at end of file diff --git a/protocol/osc/oscpack/OscException.h b/protocol/osc/oscpack/OscException.h new file mode 100644 index 000000000..8a2d35e31 --- /dev/null +++ b/protocol/osc/oscpack/OscException.h @@ -0,0 +1,54 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSC_EXCEPTION_H +#define INCLUDED_OSC_EXCEPTION_H + +#include + +namespace osc{ + +class Exception : public std::exception { + const char *what_; + +public: + Exception() throw() {} + Exception( const Exception& src ) throw() + : what_( src.what_ ) {} + Exception( const char *w ) throw() + : what_( w ) {} + Exception& operator=( const Exception& src ) throw() + { what_ = src.what_; return *this; } + virtual ~Exception() throw() {} + virtual const char* what() const throw() { return what_; } +}; + +} // namespace osc + +#endif /* INCLUDED_OSC_EXCEPTION_H */ diff --git a/protocol/osc/oscpack/OscHostEndianness.h b/protocol/osc/oscpack/OscHostEndianness.h new file mode 100644 index 000000000..1ff7bf589 --- /dev/null +++ b/protocol/osc/oscpack/OscHostEndianness.h @@ -0,0 +1,76 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef OSC_HOSTENDIANNESS_H +#define OSC_HOSTENDIANNESS_H + +//#define OSC_HOST_BIG_ENDIAN 1 + +//* +// Make sure either OSC_HOST_LITTLE_ENDIAN or OSC_HOST_BIG_ENDIAN is defined +// +// If you know a way to enhance the detection below for Linux and/or MacOSX +// please let me know! I've tried a few things which don't work. +//*/ +// +//#if defined(OSC_HOST_LITTLE_ENDIAN) || defined(OSC_HOST_BIG_ENDIAN) +// +// you can define one of the above symbols from the command line +// then you don't have to edit this file. +// +//#elif defined(__WIN32__) || defined(WIN32) || defined(WINCE) +// +// assume that __WIN32__ is only defined on little endian systems +// +//#define OSC_HOST_LITTLE_ENDIAN 1 +//#undef OSC_HOST_BIG_ENDIAN +// +//#elif defined(__APPLE__) +// +//#if defined(__LITTLE_ENDIAN__) +// +//#define OSC_HOST_LITTLE_ENDIAN 1 +//#undef OSC_HOST_BIG_ENDIAN +// +//#elif defined(__BIG_ENDIAN__) +// +//#define OSC_HOST_BIG_ENDIAN 1 +//#undef OSC_HOST_LITTLE_ENDIAN +// +//#endif +// +//#endif +// +//#if !defined(OSC_HOST_LITTLE_ENDIAN) && !defined(OSC_HOST_BIG_ENDIAN) +// +//#error please edit OSCHostEndianness.h to configure endianness +// +//#endif + +#endif /* OSC_HOSTENDIANNESS_H */ diff --git a/protocol/osc/oscpack/OscOutboundPacketStream.cpp b/protocol/osc/oscpack/OscOutboundPacketStream.cpp new file mode 100644 index 000000000..20bc386dd --- /dev/null +++ b/protocol/osc/oscpack/OscOutboundPacketStream.cpp @@ -0,0 +1,642 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#undef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS + +#include "OscOutboundPacketStream.h" + +#include +#include +#include + +#if defined(__WIN32__) || defined(WIN32) +#include // for alloca +#endif + +#include "OscHostEndianness.h" + + +namespace osc{ + +static void FromInt32( char *p, int32 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.i = x; + + p[3] = u.c[0]; + p[2] = u.c[1]; + p[1] = u.c[2]; + p[0] = u.c[3]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +static void FromUInt32( char *p, uint32 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint32 i; + char c[4]; + } u; + + u.i = x; + + p[3] = u.c[0]; + p[2] = u.c[1]; + p[1] = u.c[2]; + p[0] = u.c[3]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +static void FromInt64( char *p, int64 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int64 i; + char c[8]; + } u; + + u.i = x; + + p[7] = u.c[0]; + p[6] = u.c[1]; + p[5] = u.c[2]; + p[4] = u.c[3]; + p[3] = u.c[4]; + p[2] = u.c[5]; + p[1] = u.c[6]; + p[0] = u.c[7]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +static void FromUInt64( char *p, uint64 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint64 i; + char c[8]; + } u; + + u.i = x; + + p[7] = u.c[0]; + p[6] = u.c[1]; + p[5] = u.c[2]; + p[4] = u.c[3]; + p[3] = u.c[4]; + p[2] = u.c[5]; + p[1] = u.c[6]; + p[0] = u.c[7]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +static inline long RoundUp4( long x ) +{ + return ((x-1) & (~0x03L)) + 4; +} + + +OutboundPacketStream::OutboundPacketStream( char *buffer, unsigned long capacity ) + : data_( buffer ) + , end_( data_ + capacity ) + , typeTagsCurrent_( end_ ) + , messageCursor_( data_ ) + , argumentCurrent_( data_ ) + , elementSizePtr_( 0 ) + , messageIsInProgress_( false ) +{ + +} + + +OutboundPacketStream::~OutboundPacketStream() +{ + +} + + +char *OutboundPacketStream::BeginElement( char *beginPtr ) +{ + if( elementSizePtr_ == 0 ){ + + elementSizePtr_ = reinterpret_cast(data_); + + return beginPtr; + + }else{ + // store an offset to the old element size ptr in the element size slot + // we store an offset rather than the actual pointer to be 64 bit clean. + *reinterpret_cast(beginPtr) = + (uint32)(reinterpret_cast(elementSizePtr_) - data_); + + elementSizePtr_ = reinterpret_cast(beginPtr); + + return beginPtr + 4; + } +} + + +void OutboundPacketStream::EndElement( char *endPtr ) +{ + assert( elementSizePtr_ != 0 ); + + if( elementSizePtr_ == reinterpret_cast(data_) ){ + + elementSizePtr_ = 0; + + }else{ + // while building an element, an offset to the containing element's + // size slot is stored in the elements size slot (or a ptr to data_ + // if there is no containing element). We retrieve that here + uint32 *previousElementSizePtr = + (uint32*)(data_ + *reinterpret_cast(elementSizePtr_)); + + // then we store the element size in the slot, note that the element + // size does not include the size slot, hence the - 4 below. + uint32 elementSize = + static_cast(endPtr - reinterpret_cast(elementSizePtr_)) - 4; + FromUInt32( reinterpret_cast(elementSizePtr_), elementSize ); + + // finally, we reset the element size ptr to the containing element + elementSizePtr_ = previousElementSizePtr; + } +} + + +bool OutboundPacketStream::ElementSizeSlotRequired() const +{ + return (elementSizePtr_ != 0); +} + + +void OutboundPacketStream::CheckForAvailableBundleSpace() +{ + unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + 16; + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern ) +{ + // plus 4 for at least four bytes of type tag + unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + + RoundUp4(static_cast(strlen(addressPattern)) + 1) + 4; + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::CheckForAvailableArgumentSpace( long argumentLength ) +{ + // plus three for extra type tag, comma and null terminator + unsigned long required = static_cast(argumentCurrent_ - data_) + argumentLength + + RoundUp4( static_cast(end_ - typeTagsCurrent_) + 3 ); + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::Clear() +{ + typeTagsCurrent_ = end_; + messageCursor_ = data_; + argumentCurrent_ = data_; + elementSizePtr_ = 0; + messageIsInProgress_ = false; +} + + +unsigned int OutboundPacketStream::Capacity() const +{ + return static_cast(end_ - data_); +} + + +unsigned int OutboundPacketStream::Size() const +{ + unsigned int result = static_cast(argumentCurrent_ - data_); + if( IsMessageInProgress() ){ + // account for the length of the type tag string. the total type tag + // includes an initial comma, plus at least one terminating \0 + result += RoundUp4( static_cast(end_ - typeTagsCurrent_) + 2 ); + } + + return result; +} + + +const char *OutboundPacketStream::Data() const +{ + return data_; +} + + +bool OutboundPacketStream::IsReady() const +{ + return (!IsMessageInProgress() && !IsBundleInProgress()); +} + + +bool OutboundPacketStream::IsMessageInProgress() const +{ + return messageIsInProgress_; +} + + +bool OutboundPacketStream::IsBundleInProgress() const +{ + return (elementSizePtr_ != 0); +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs ) +{ + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + CheckForAvailableBundleSpace(); + + messageCursor_ = BeginElement( messageCursor_ ); + + memcpy( messageCursor_, "#bundle\0", 8 ); + FromUInt64( messageCursor_ + 8, rhs.timeTag ); + + messageCursor_ += 16; + argumentCurrent_ = messageCursor_; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs ) +{ + (void) rhs; + + if( !IsBundleInProgress() ) + throw BundleNotInProgressException(); + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + EndElement( messageCursor_ ); + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs ) +{ + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + CheckForAvailableMessageSpace( rhs.addressPattern ); + + messageCursor_ = BeginElement( messageCursor_ ); + + strcpy( messageCursor_, rhs.addressPattern ); + unsigned long rhsLength = static_cast(strlen(rhs.addressPattern)); + messageCursor_ += rhsLength + 1; + + // zero pad to 4-byte boundary + unsigned long i = rhsLength + 1; + while( i & 0x3 ){ + *messageCursor_++ = '\0'; + ++i; + } + + argumentCurrent_ = messageCursor_; + typeTagsCurrent_ = end_; + + messageIsInProgress_ = true; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs ) +{ + (void) rhs; + + if( !IsMessageInProgress() ) + throw MessageNotInProgressException(); + + int typeTagsCount = static_cast(end_ - typeTagsCurrent_); + + if( typeTagsCount ){ + + char *tempTypeTags = (char*)alloca(typeTagsCount); + memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount ); + + // slot size includes comma and null terminator + int typeTagSlotSize = RoundUp4( typeTagsCount + 2 ); + + uint32 argumentsSize = static_cast(argumentCurrent_ - messageCursor_); + + memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize ); + + messageCursor_[0] = ','; + // copy type tags in reverse (really forward) order + for( int i=0; i < typeTagsCount; ++i ) + messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ]; + + char *p = messageCursor_ + 1 + typeTagsCount; + for( int i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i ) + *p++ = '\0'; + + typeTagsCurrent_ = end_; + + // advance messageCursor_ for next message + messageCursor_ += typeTagSlotSize + argumentsSize; + + }else{ + // send an empty type tags string + memcpy( messageCursor_, ",\0\0\0", 4 ); + + // advance messageCursor_ for next message + messageCursor_ += 4; + } + + argumentCurrent_ = messageCursor_; + + EndElement( messageCursor_ ); + + messageIsInProgress_ = false; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs ) +{ + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG); + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = NIL_TYPE_TAG; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = INT32_TYPE_TAG; + FromInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( float rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = FLOAT_TYPE_TAG; + +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + float f; + char c[4]; + } u; + + u.f = rhs; + + argumentCurrent_[3] = u.c[0]; + argumentCurrent_[2] = u.c[1]; + argumentCurrent_[1] = u.c[2]; + argumentCurrent_[0] = u.c[3]; +#else + *reinterpret_cast(argumentCurrent_) = rhs; +#endif + + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( char rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = CHAR_TYPE_TAG; + FromInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = INT64_TYPE_TAG; + FromInt64( argumentCurrent_, rhs ); + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG; + FromUInt64( argumentCurrent_, rhs ); + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( double rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG; + +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + double f; + char c[8]; + } u; + + u.f = rhs; + + argumentCurrent_[7] = u.c[0]; + argumentCurrent_[6] = u.c[1]; + argumentCurrent_[5] = u.c[2]; + argumentCurrent_[4] = u.c[3]; + argumentCurrent_[3] = u.c[4]; + argumentCurrent_[2] = u.c[5]; + argumentCurrent_[1] = u.c[6]; + argumentCurrent_[0] = u.c[7]; +#else + *reinterpret_cast(argumentCurrent_) = rhs; +#endif + + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs ) +{ + CheckForAvailableArgumentSpace( RoundUp4(static_cast(strlen(rhs)) + 1) ); + + *(--typeTagsCurrent_) = STRING_TYPE_TAG; + strcpy( argumentCurrent_, rhs ); + unsigned long rhsLength = static_cast(strlen(rhs)); + argumentCurrent_ += rhsLength + 1; + + // zero pad to 4-byte boundary + unsigned long i = rhsLength + 1; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs ) +{ + CheckForAvailableArgumentSpace( RoundUp4(static_cast(strlen(rhs)) + 1) ); + + *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG; + strcpy( argumentCurrent_, rhs ); + unsigned long rhsLength = static_cast(strlen(rhs)); + argumentCurrent_ += rhsLength + 1; + + // zero pad to 4-byte boundary + unsigned long i = rhsLength + 1; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs ) +{ + CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) ); + + *(--typeTagsCurrent_) = BLOB_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs.size ); + argumentCurrent_ += 4; + + memcpy( argumentCurrent_, rhs.data, rhs.size ); + argumentCurrent_ += rhs.size; + + // zero pad to 4-byte boundary + unsigned long i = rhs.size; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + +} // namespace osc + + diff --git a/protocol/osc/oscpack/OscOutboundPacketStream.h b/protocol/osc/oscpack/OscOutboundPacketStream.h new file mode 100644 index 000000000..5faefb901 --- /dev/null +++ b/protocol/osc/oscpack/OscOutboundPacketStream.h @@ -0,0 +1,142 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCOUTBOUNDPACKET_H +#define INCLUDED_OSCOUTBOUNDPACKET_H + +#include "OscTypes.h" +#include "OscException.h" + + +namespace osc{ + +class OutOfBufferMemoryException : public Exception{ +public: + OutOfBufferMemoryException( const char *w="out of buffer memory" ) + : Exception( w ) {} +}; + +class BundleNotInProgressException : public Exception{ +public: + BundleNotInProgressException( + const char *w="call to EndBundle when bundle is not in progress" ) + : Exception( w ) {} +}; + +class MessageInProgressException : public Exception{ +public: + MessageInProgressException( + const char *w="opening or closing bundle or message while message is in progress" ) + : Exception( w ) {} +}; + +class MessageNotInProgressException : public Exception{ +public: + MessageNotInProgressException( + const char *w="call to EndMessage when message is not in progress" ) + : Exception( w ) {} +}; + + +class OutboundPacketStream{ +public: + OutboundPacketStream( char *buffer, unsigned long capacity ); + ~OutboundPacketStream(); + + void Clear(); + + unsigned int Capacity() const; + + // invariant: size() is valid even while building a message. + unsigned int Size() const; + + const char *Data() const; + + // indicates that all messages have been closed with a matching EndMessage + // and all bundles have been closed with a matching EndBundle + bool IsReady() const; + + bool IsMessageInProgress() const; + bool IsBundleInProgress() const; + + OutboundPacketStream& operator<<( const BundleInitiator& rhs ); + OutboundPacketStream& operator<<( const BundleTerminator& rhs ); + + OutboundPacketStream& operator<<( const BeginMessage& rhs ); + OutboundPacketStream& operator<<( const MessageTerminator& rhs ); + + OutboundPacketStream& operator<<( bool rhs ); + OutboundPacketStream& operator<<( const NilType& rhs ); + OutboundPacketStream& operator<<( const InfinitumType& rhs ); + OutboundPacketStream& operator<<( int32 rhs ); + +//#ifndef __x86_64__ +// OutboundPacketStream& operator<<( int rhs ) +// { *this << (int32)rhs; return *this; } +//#endif + + OutboundPacketStream& operator<<( float rhs ); + OutboundPacketStream& operator<<( char rhs ); + OutboundPacketStream& operator<<( const RgbaColor& rhs ); + OutboundPacketStream& operator<<( const MidiMessage& rhs ); + OutboundPacketStream& operator<<( int64 rhs ); + OutboundPacketStream& operator<<( const TimeTag& rhs ); + OutboundPacketStream& operator<<( double rhs ); + OutboundPacketStream& operator<<( const char* rhs ); + OutboundPacketStream& operator<<( const Symbol& rhs ); + OutboundPacketStream& operator<<( const Blob& rhs ); + +private: + + char *BeginElement( char *beginPtr ); + void EndElement( char *endPtr ); + + bool ElementSizeSlotRequired() const; + void CheckForAvailableBundleSpace(); + void CheckForAvailableMessageSpace( const char *addressPattern ); + void CheckForAvailableArgumentSpace( long argumentLength ); + + char *data_; + char *end_; + + char *typeTagsCurrent_; // stored in reverse order + char *messageCursor_; + char *argumentCurrent_; + + // elementSizePtr_ has two special values: 0 indicates that a bundle + // isn't open, and elementSizePtr_==data_ indicates that a bundle is + // open but that it doesn't have a size slot (ie the outermost bundle) + uint32 *elementSizePtr_; + + bool messageIsInProgress_; +}; + +} // namespace osc + +#endif /* INCLUDED_OSC_OUTBOUND_PACKET_H */ diff --git a/protocol/osc/oscpack/OscPacketListener.h b/protocol/osc/oscpack/OscPacketListener.h new file mode 100644 index 000000000..b2d19812b --- /dev/null +++ b/protocol/osc/oscpack/OscPacketListener.h @@ -0,0 +1,72 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCPACKETLISTENER_H +#define INCLUDED_OSCPACKETLISTENER_H + +#include "OscReceivedElements.h" +#include "../ip/PacketListener.h" + + +namespace osc{ + +class OscPacketListener : public PacketListener{ +protected: + virtual void ProcessBundle( const osc::ReceivedBundle& b, + const IpEndpointName& remoteEndpoint ) + { + // ignore bundle time tag for now + + for( ReceivedBundle::const_iterator i = b.ElementsBegin(); + i != b.ElementsEnd(); ++i ){ + if( i->IsBundle() ) + ProcessBundle( ReceivedBundle(*i), remoteEndpoint ); + else + ProcessMessage( ReceivedMessage(*i), remoteEndpoint ); + } + } + + virtual void ProcessMessage( const osc::ReceivedMessage& m, + const IpEndpointName& remoteEndpoint ) = 0; + +public: + virtual void ProcessPacket( const char *data, int size, + const IpEndpointName& remoteEndpoint ) + { + osc::ReceivedPacket p( data, size ); + if( p.IsBundle() ) + ProcessBundle( ReceivedBundle(p), remoteEndpoint ); + else + ProcessMessage( ReceivedMessage(p), remoteEndpoint ); + } +}; + +} // namespace osc + +#endif /* INCLUDED_OSCPACKETLISTENER_H */ diff --git a/protocol/osc/oscpack/OscPrintReceivedElements.cpp b/protocol/osc/oscpack/OscPrintReceivedElements.cpp new file mode 100644 index 000000000..128cc59ff --- /dev/null +++ b/protocol/osc/oscpack/OscPrintReceivedElements.cpp @@ -0,0 +1,247 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#undef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS + +#include "OscPrintReceivedElements.h" + +#include +#include +#include +#include + + +namespace osc{ + + +std::ostream& operator<<( std::ostream & os, + const ReceivedMessageArgument& arg ) +{ + switch( arg.TypeTag() ){ + case TRUE_TYPE_TAG: + os << "bool:true"; + break; + + case FALSE_TYPE_TAG: + os << "bool:false"; + break; + + case NIL_TYPE_TAG: + os << "(Nil)"; + break; + + case INFINITUM_TYPE_TAG: + os << "(Infinitum)"; + break; + + case INT32_TYPE_TAG: + os << "int32:" << arg.AsInt32Unchecked(); + break; + + case FLOAT_TYPE_TAG: + os << "float32:" << arg.AsFloatUnchecked(); + break; + + case CHAR_TYPE_TAG: + { + char s[2] = {0}; + s[0] = arg.AsCharUnchecked(); + os << "char:'" << s << "'"; + } + break; + + case RGBA_COLOR_TYPE_TAG: + { + uint32 color = arg.AsRgbaColorUnchecked(); + + os << "RGBA:0x" + << std::hex << std::setfill('0') + << std::setw(2) << (int)((color>>24) & 0xFF) + << std::setw(2) << (int)((color>>16) & 0xFF) + << std::setw(2) << (int)((color>>8) & 0xFF) + << std::setw(2) << (int)(color & 0xFF) + << std::setfill(' '); + os.unsetf(std::ios::basefield); + } + break; + + case MIDI_MESSAGE_TYPE_TAG: + { + uint32 m = arg.AsMidiMessageUnchecked(); + os << "midi (port, status, data1, data2):<<" + << std::hex << std::setfill('0') + << "0x" << std::setw(2) << (int)((m>>24) & 0xFF) + << " 0x" << std::setw(2) << (int)((m>>16) & 0xFF) + << " 0x" << std::setw(2) << (int)((m>>8) & 0xFF) + << " 0x" << std::setw(2) << (int)(m & 0xFF) + << std::setfill(' ') << ">>"; + os.unsetf(std::ios::basefield); + } + break; + + case INT64_TYPE_TAG: + os << "int64:" << arg.AsInt64Unchecked(); + break; + + case TIME_TAG_TYPE_TAG: + { + os << "OSC-timetag:" << arg.AsTimeTagUnchecked(); + + std::time_t t = + (unsigned long)( arg.AsTimeTagUnchecked() >> 32 ); + + // strip trailing newline from string returned by ctime + const char *timeString = std::ctime( &t ); + size_t len = strlen( timeString ); + char *s = new char[ len + 1 ]; + strcpy( s, timeString ); + if( len ) + s[ len - 1 ] = '\0'; + + os << " " << s; + } + break; + + case DOUBLE_TYPE_TAG: + os << "double:" << arg.AsDoubleUnchecked(); + break; + + case STRING_TYPE_TAG: + os << "OSC-string:`" << arg.AsStringUnchecked() << "'"; + break; + + case SYMBOL_TYPE_TAG: + os << "OSC-string (symbol):`" << arg.AsSymbolUnchecked() << "'"; + break; + + case BLOB_TYPE_TAG: + { + unsigned long size; + const void *data; + arg.AsBlobUnchecked( data, size ); + os << "OSC-blob:<<" << std::hex << std::setfill('0'); + unsigned char *p = (unsigned char*)data; + for( unsigned long i = 0; i < size; ++i ){ + os << "0x" << std::setw(2) << int(p[i]); + if( i != size-1 ) + os << ' '; + } + os.unsetf(std::ios::basefield); + os << ">>" << std::setfill(' '); + } + break; + + default: + os << "unknown"; + } + + return os; +} + + +std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m ) +{ + os << "["; + if( m.AddressPatternIsUInt32() ) + os << m.AddressPatternAsUInt32(); + else + os << m.AddressPattern(); + + bool first = true; + for( ReceivedMessage::const_iterator i = m.ArgumentsBegin(); + i != m.ArgumentsEnd(); ++i ){ + if( first ){ + os << " "; + first = false; + }else{ + os << ", "; + } + + os << *i; + } + + os << "]"; + + return os; +} + + +std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b ) +{ + static int indent = 0; + + for( int j=0; j < indent; ++j ) + os << " "; + os << "{ ( "; + if( b.TimeTag() == 1 ) + os << "immediate"; + else + os << b.TimeTag(); + os << " )\n"; + + ++indent; + + for( ReceivedBundle::const_iterator i = b.ElementsBegin(); + i != b.ElementsEnd(); ++i ){ + if( i->IsBundle() ){ + ReceivedBundle b(*i); + os << b << "\n"; + }else{ + ReceivedMessage m(*i); + for( int j=0; j < indent; ++j ) + os << " "; + os << m << "\n"; + } + } + + --indent; + + for( int j=0; j < indent; ++j ) + os << " "; + os << "}"; + + return os; +} + + +std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p ) +{ + if( p.IsBundle() ){ + ReceivedBundle b(p); + os << b << "\n"; + }else{ + ReceivedMessage m(p); + os << m << "\n"; + } + + return os; +} + +} // namespace osc diff --git a/protocol/osc/oscpack/OscPrintReceivedElements.h b/protocol/osc/oscpack/OscPrintReceivedElements.h new file mode 100644 index 000000000..7ec747c81 --- /dev/null +++ b/protocol/osc/oscpack/OscPrintReceivedElements.h @@ -0,0 +1,49 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCPRINTRECEIVEDELEMENTS_H +#define INCLUDED_OSCPRINTRECEIVEDELEMENTS_H + +#include + +#ifndef INCLUDED_OSCRECEIVEDELEMENTS_H +#include "OscReceivedElements.h" +#endif /* INCLUDED_OSCRECEIVEDELEMENTS_H */ + + +namespace osc{ + +std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p ); +std::ostream& operator<<( std::ostream & os, const ReceivedMessageArgument& arg ); +std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m ); +std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b ); + +} // namespace osc + +#endif /* INCLUDED_OSCPRINTRECEIVEDELEMENTS_H */ diff --git a/protocol/osc/oscpack/OscReceivedElements.cpp b/protocol/osc/oscpack/OscReceivedElements.cpp new file mode 100644 index 000000000..cca1e1353 --- /dev/null +++ b/protocol/osc/oscpack/OscReceivedElements.cpp @@ -0,0 +1,725 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#undef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS + +#include "OscReceivedElements.h" + +#include + +#include "OscHostEndianness.h" + + +namespace osc{ + + +// return the first 4 byte boundary after the end of a str4 +// be careful about calling this version if you don't know whether +// the string is terminated correctly. +static inline const char* FindStr4End( const char *p ) +{ + if( p[0] == '\0' ) // special case for SuperCollider integer address pattern + return p + 4; + + p += 3; + + while( *p ) + p += 4; + + return p + 1; +} + + +// return the first 4 byte boundary after the end of a str4 +// returns 0 if p == end or if the string is unterminated +static inline const char* FindStr4End( const char *p, const char *end ) +{ + if( p >= end ) + return 0; + + if( p[0] == '\0' ) // special case for SuperCollider integer address pattern + return p + 4; + + p += 3; + end -= 1; + + while( p < end && *p ) + p += 4; + + if( *p ) + return 0; + else + return p + 1; +} + + +static inline unsigned long RoundUp4( unsigned long x ) +{ + unsigned long remainder = x & 0x3UL; + if( remainder ) + return x + (4 - remainder); + else + return x; +} + + +static inline int32 ToInt32( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.c[0] = p[3]; + u.c[1] = p[2]; + u.c[2] = p[1]; + u.c[3] = p[0]; + + return u.i; +#else + return *(int32*)p; +#endif +} + + +static inline uint32 ToUInt32( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint32 i; + char c[4]; + } u; + + u.c[0] = p[3]; + u.c[1] = p[2]; + u.c[2] = p[1]; + u.c[3] = p[0]; + + return u.i; +#else + return *(uint32*)p; +#endif +} + + +int64 ToInt64( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int64 i; + char c[8]; + } u; + + u.c[0] = p[7]; + u.c[1] = p[6]; + u.c[2] = p[5]; + u.c[3] = p[4]; + u.c[4] = p[3]; + u.c[5] = p[2]; + u.c[6] = p[1]; + u.c[7] = p[0]; + + return u.i; +#else + return *(int64*)p; +#endif +} + + +uint64 ToUInt64( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint64 i; + char c[8]; + } u; + + u.c[0] = p[7]; + u.c[1] = p[6]; + u.c[2] = p[5]; + u.c[3] = p[4]; + u.c[4] = p[3]; + u.c[5] = p[2]; + u.c[6] = p[1]; + u.c[7] = p[0]; + + return u.i; +#else + return *(uint64*)p; +#endif +} + +//------------------------------------------------------------------------------ + +bool ReceivedPacket::IsBundle() const +{ + return (Size() > 0 && Contents()[0] == '#'); +} + +//------------------------------------------------------------------------------ + +bool ReceivedBundleElement::IsBundle() const +{ + return (Size() > 0 && Contents()[0] == '#'); +} + + +int32 ReceivedBundleElement::Size() const +{ + return ToUInt32( size_ ); +} + +//------------------------------------------------------------------------------ + +bool ReceivedMessageArgument::AsBool() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == TRUE_TYPE_TAG ) + return true; + else if( *typeTag_ == FALSE_TYPE_TAG ) + return false; + else + throw WrongArgumentTypeException(); +} + + +bool ReceivedMessageArgument::AsBoolUnchecked() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == TRUE_TYPE_TAG ) + return true; + else + return false; +} + + +int32 ReceivedMessageArgument::AsInt32() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == INT32_TYPE_TAG ) + return AsInt32Unchecked(); + else + throw WrongArgumentTypeException(); +} + + +int32 ReceivedMessageArgument::AsInt32Unchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.c[0] = argument_[3]; + u.c[1] = argument_[2]; + u.c[2] = argument_[1]; + u.c[3] = argument_[0]; + + return u.i; +#else + return *(int32*)argument_; +#endif +} + + +float ReceivedMessageArgument::AsFloat() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == FLOAT_TYPE_TAG ) + return AsFloatUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +float ReceivedMessageArgument::AsFloatUnchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + float f; + char c[4]; + } u; + + u.c[0] = argument_[3]; + u.c[1] = argument_[2]; + u.c[2] = argument_[1]; + u.c[3] = argument_[0]; + + return u.f; +#else + return *(float*)argument_; +#endif +} + + +char ReceivedMessageArgument::AsChar() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == CHAR_TYPE_TAG ) + return AsCharUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +char ReceivedMessageArgument::AsCharUnchecked() const +{ + return (char)ToInt32( argument_ ); +} + + +uint32 ReceivedMessageArgument::AsRgbaColor() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == RGBA_COLOR_TYPE_TAG ) + return AsRgbaColorUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const +{ + return ToUInt32( argument_ ); +} + + +uint32 ReceivedMessageArgument::AsMidiMessage() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == MIDI_MESSAGE_TYPE_TAG ) + return AsMidiMessageUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const +{ + return ToUInt32( argument_ ); +} + + +int64 ReceivedMessageArgument::AsInt64() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == INT64_TYPE_TAG ) + return AsInt64Unchecked(); + else + throw WrongArgumentTypeException(); +} + + +int64 ReceivedMessageArgument::AsInt64Unchecked() const +{ + return ToInt64( argument_ ); +} + + +uint64 ReceivedMessageArgument::AsTimeTag() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == TIME_TAG_TYPE_TAG ) + return AsTimeTagUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const +{ + return ToUInt64( argument_ ); +} + + +double ReceivedMessageArgument::AsDouble() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == DOUBLE_TYPE_TAG ) + return AsDoubleUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +double ReceivedMessageArgument::AsDoubleUnchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + double d; + char c[8]; + } u; + + u.c[0] = argument_[7]; + u.c[1] = argument_[6]; + u.c[2] = argument_[5]; + u.c[3] = argument_[4]; + u.c[4] = argument_[3]; + u.c[5] = argument_[2]; + u.c[6] = argument_[1]; + u.c[7] = argument_[0]; + + return u.d; +#else + return *(double*)argument_; +#endif +} + + +const char* ReceivedMessageArgument::AsString() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == STRING_TYPE_TAG ) + return argument_; + else + throw WrongArgumentTypeException(); +} + + +const char* ReceivedMessageArgument::AsSymbol() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == SYMBOL_TYPE_TAG ) + return argument_; + else + throw WrongArgumentTypeException(); +} + + +void ReceivedMessageArgument::AsBlob( const void*& data, unsigned long& size ) const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == BLOB_TYPE_TAG ) + AsBlobUnchecked( data, size ); + else + throw WrongArgumentTypeException(); +} + + +void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, unsigned long& size ) const +{ + size = ToUInt32( argument_ ); + data = (void*)(argument_+4); +} + +//------------------------------------------------------------------------------ + +void ReceivedMessageArgumentIterator::Advance() +{ + if( !value_.typeTag_ ) + return; + + switch( *value_.typeTag_++ ){ + case '\0': + // don't advance past end + --value_.typeTag_; + break; + + case TRUE_TYPE_TAG: + case FALSE_TYPE_TAG: + case NIL_TYPE_TAG: + case INFINITUM_TYPE_TAG: + + // zero length + break; + + case INT32_TYPE_TAG: + case FLOAT_TYPE_TAG: + case CHAR_TYPE_TAG: + case RGBA_COLOR_TYPE_TAG: + case MIDI_MESSAGE_TYPE_TAG: + + value_.argument_ += 4; + break; + + case INT64_TYPE_TAG: + case TIME_TAG_TYPE_TAG: + case DOUBLE_TYPE_TAG: + + value_.argument_ += 8; + break; + + case STRING_TYPE_TAG: + case SYMBOL_TYPE_TAG: + + // we use the unsafe function FindStr4End(char*) here because all of + // the arguments have already been validated in + // ReceivedMessage::Init() below. + + value_.argument_ = FindStr4End( value_.argument_ ); + break; + + case BLOB_TYPE_TAG: + { + uint32 blobSize = ToUInt32( value_.argument_ ); + value_.argument_ = value_.argument_ + 4 + RoundUp4( (unsigned long)blobSize ); + } + break; + + default: // unknown type tag + // don't advance + --value_.typeTag_; + break; + + + // not handled: + // [ Indicates the beginning of an array. The tags following are for + // data in the Array until a close brace tag is reached. + // ] Indicates the end of an array. + } +} + +//------------------------------------------------------------------------------ + +ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet ) + : addressPattern_( packet.Contents() ) +{ + Init( packet.Contents(), packet.Size() ); +} + + +ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement ) + : addressPattern_( bundleElement.Contents() ) +{ + Init( bundleElement.Contents(), bundleElement.Size() ); +} + + +bool ReceivedMessage::AddressPatternIsUInt32() const +{ + return (addressPattern_[0] == '\0'); +} + + +uint32 ReceivedMessage::AddressPatternAsUInt32() const +{ + return ToUInt32( addressPattern_ ); +} + + +void ReceivedMessage::Init( const char *message, unsigned long size ) +{ + if( size == 0 ) + throw MalformedMessageException( "zero length messages not permitted" ); + + if( (size & 0x03L) != 0 ) + throw MalformedMessageException( "message size must be multiple of four" ); + + const char *end = message + size; + + typeTagsBegin_ = FindStr4End( addressPattern_, end ); + if( typeTagsBegin_ == 0 ){ + // address pattern was not terminated before end + throw MalformedMessageException( "unterminated address pattern" ); + } + + if( typeTagsBegin_ == end ){ + // message consists of only the address pattern - no arguments or type tags. + typeTagsBegin_ = 0; + typeTagsEnd_ = 0; + arguments_ = 0; + + }else{ + if( *typeTagsBegin_ != ',' ) + throw MalformedMessageException( "type tags not present" ); + + if( *(typeTagsBegin_ + 1) == '\0' ){ + // zero length type tags + typeTagsBegin_ = 0; + typeTagsEnd_ = 0; + arguments_ = 0; + + }else{ + // check that all arguments are present and well formed + + arguments_ = FindStr4End( typeTagsBegin_, end ); + if( arguments_ == 0 ){ + throw MalformedMessageException( "type tags were not terminated before end of message" ); + } + + ++typeTagsBegin_; // advance past initial ',' + + const char *typeTag = typeTagsBegin_; + const char *argument = arguments_; + + do{ + switch( *typeTag ){ + case TRUE_TYPE_TAG: + case FALSE_TYPE_TAG: + case NIL_TYPE_TAG: + case INFINITUM_TYPE_TAG: + + // zero length + break; + + case INT32_TYPE_TAG: + case FLOAT_TYPE_TAG: + case CHAR_TYPE_TAG: + case RGBA_COLOR_TYPE_TAG: + case MIDI_MESSAGE_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument += 4; + if( argument > end ) + throw MalformedMessageException( "arguments exceed message size" ); + break; + + case INT64_TYPE_TAG: + case TIME_TAG_TYPE_TAG: + case DOUBLE_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument += 8; + if( argument > end ) + throw MalformedMessageException( "arguments exceed message size" ); + break; + + case STRING_TYPE_TAG: + case SYMBOL_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument = FindStr4End( argument, end ); + if( argument == 0 ) + throw MalformedMessageException( "unterminated string argument" ); + break; + + case BLOB_TYPE_TAG: + { + if( argument + 4 > end ) + MalformedMessageException( "arguments exceed message size" ); + + uint32 blobSize = ToUInt32( argument ); + argument = argument + 4 + RoundUp4( (unsigned long)blobSize ); + if( argument > end ) + MalformedMessageException( "arguments exceed message size" ); + } + break; + + default: + throw MalformedMessageException( "unknown type tag" ); + + // not handled: + // [ Indicates the beginning of an array. The tags following are for + // data in the Array until a close brace tag is reached. + // ] Indicates the end of an array. + } + + }while( *++typeTag != '\0' ); + typeTagsEnd_ = typeTag; + } + } +} + +//------------------------------------------------------------------------------ + +ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet ) + : elementCount_( 0 ) +{ + Init( packet.Contents(), packet.Size() ); +} + + +ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement ) + : elementCount_( 0 ) +{ + Init( bundleElement.Contents(), bundleElement.Size() ); +} + + +void ReceivedBundle::Init( const char *bundle, unsigned long size ) +{ + if( size < 16 ) + throw MalformedBundleException( "packet too short for bundle" ); + + if( (size & 0x03L) != 0 ) + throw MalformedBundleException( "bundle size must be multiple of four" ); + + if( bundle[0] != '#' + || bundle[1] != 'b' + || bundle[2] != 'u' + || bundle[3] != 'n' + || bundle[4] != 'd' + || bundle[5] != 'l' + || bundle[6] != 'e' + || bundle[7] != '\0' ) + throw MalformedBundleException( "bad bundle address pattern" ); + + end_ = bundle + size; + + timeTag_ = bundle + 8; + + const char *p = timeTag_ + 8; + + while( p < end_ ){ + if( p + 4 > end_ ) + throw MalformedBundleException( "packet too short for elementSize" ); + + uint32 elementSize = ToUInt32( p ); + if( (elementSize & 0x03L) != 0 ) + throw MalformedBundleException( "bundle element size must be multiple of four" ); + + p += 4 + elementSize; + if( p > end_ ) + throw MalformedBundleException( "packet too short for bundle element" ); + + ++elementCount_; + } + + if( p != end_ ) + throw MalformedBundleException( "bundle contents " ); +} + + +uint64 ReceivedBundle::TimeTag() const +{ + return ToUInt64( timeTag_ ); +} + + +} // namespace osc + diff --git a/protocol/osc/oscpack/OscReceivedElements.h b/protocol/osc/oscpack/OscReceivedElements.h new file mode 100644 index 000000000..483640610 --- /dev/null +++ b/protocol/osc/oscpack/OscReceivedElements.h @@ -0,0 +1,488 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCRECEIVEDELEMENTS_H +#define INCLUDED_OSCRECEIVEDELEMENTS_H + +#include + +#include "OscTypes.h" +#include "OscException.h" + + +namespace osc{ + + +class MalformedMessageException : public Exception{ +public: + MalformedMessageException( const char *w="malformed message" ) + : Exception( w ) {} +}; + +class MalformedBundleException : public Exception{ +public: + MalformedBundleException( const char *w="malformed bundle" ) + : Exception( w ) {} +}; + +class WrongArgumentTypeException : public Exception{ +public: + WrongArgumentTypeException( const char *w="wrong argument type" ) + : Exception( w ) {} +}; + +class MissingArgumentException : public Exception{ +public: + MissingArgumentException( const char *w="missing argument" ) + : Exception( w ) {} +}; + +class ExcessArgumentException : public Exception{ +public: + ExcessArgumentException( const char *w="too many arguments" ) + : Exception( w ) {} +}; + + +class ReceivedPacket{ +public: + ReceivedPacket( const char *contents, int32 size ) + : contents_( contents ) + , size_( size ) {} + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + int32 Size() const { return size_; } + const char *Contents() const { return contents_; } + +private: + const char *contents_; + int32 size_; +}; + + +class ReceivedBundleElement{ +public: + ReceivedBundleElement( const char *size ) + : size_( size ) {} + + friend class ReceivedBundleElementIterator; + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + int32 Size() const; + const char *Contents() const { return size_ + 4; } + +private: + const char *size_; +}; + + +class ReceivedBundleElementIterator{ +public: + ReceivedBundleElementIterator( const char *sizePtr ) + : value_( sizePtr ) {} + + ReceivedBundleElementIterator operator++() + { + Advance(); + return *this; + } + + ReceivedBundleElementIterator operator++(int) + { + ReceivedBundleElementIterator old( *this ); + Advance(); + return old; + } + + const ReceivedBundleElement& operator*() const { return value_; } + + const ReceivedBundleElement* operator->() const { return &value_; } + + friend bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ); + +private: + ReceivedBundleElement value_; + + void Advance() { value_.size_ = value_.Contents() + value_.Size(); } + + bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const + { + return value_.size_ == rhs.value_.size_; + } +}; + +inline bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgument{ +public: + ReceivedMessageArgument( const char *typeTag, const char *argument ) + : typeTag_( typeTag ) + , argument_( argument ) {} + + friend class ReceivedMessageArgumentIterator; + + const char TypeTag() const { return *typeTag_; } + + // the unchecked methods below don't check whether the argument actually + // is of the specified type. they should only be used if you've already + // checked the type tag or the associated IsType() method. + + bool IsBool() const + { return *typeTag_ == TRUE_TYPE_TAG || *typeTag_ == FALSE_TYPE_TAG; } + bool AsBool() const; + bool AsBoolUnchecked() const; + + bool IsNil() const { return *typeTag_ == NIL_TYPE_TAG; } + bool IsInfinitum() const { return *typeTag_ == INFINITUM_TYPE_TAG; } + + bool IsInt32() const { return *typeTag_ == INT32_TYPE_TAG; } + int32 AsInt32() const; + int32 AsInt32Unchecked() const; + + bool IsFloat() const { return *typeTag_ == FLOAT_TYPE_TAG; } + float AsFloat() const; + float AsFloatUnchecked() const; + + bool IsChar() const { return *typeTag_ == CHAR_TYPE_TAG; } + char AsChar() const; + char AsCharUnchecked() const; + + bool IsRgbaColor() const { return *typeTag_ == RGBA_COLOR_TYPE_TAG; } + uint32 AsRgbaColor() const; + uint32 AsRgbaColorUnchecked() const; + + bool IsMidiMessage() const { return *typeTag_ == MIDI_MESSAGE_TYPE_TAG; } + uint32 AsMidiMessage() const; + uint32 AsMidiMessageUnchecked() const; + + bool IsInt64() const { return *typeTag_ == INT64_TYPE_TAG; } + int64 AsInt64() const; + int64 AsInt64Unchecked() const; + + bool IsTimeTag() const { return *typeTag_ == TIME_TAG_TYPE_TAG; } + uint64 AsTimeTag() const; + uint64 AsTimeTagUnchecked() const; + + bool IsDouble() const { return *typeTag_ == DOUBLE_TYPE_TAG; } + double AsDouble() const; + double AsDoubleUnchecked() const; + + bool IsString() const { return *typeTag_ == STRING_TYPE_TAG; } + const char* AsString() const; + const char* AsStringUnchecked() const { return argument_; } + + bool IsSymbol() const { return *typeTag_ == SYMBOL_TYPE_TAG; } + const char* AsSymbol() const; + const char* AsSymbolUnchecked() const { return argument_; } + + bool IsBlob() const { return *typeTag_ == BLOB_TYPE_TAG; } + void AsBlob( const void*& data, unsigned long& size ) const; + void AsBlobUnchecked( const void*& data, unsigned long& size ) const; + +private: + const char *typeTag_; + const char *argument_; +}; + + +class ReceivedMessageArgumentIterator{ +public: + ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments ) + : value_( typeTags, arguments ) {} + + ReceivedMessageArgumentIterator operator++() + { + Advance(); + return *this; + } + + ReceivedMessageArgumentIterator operator++(int) + { + ReceivedMessageArgumentIterator old( *this ); + Advance(); + return old; + } + + const ReceivedMessageArgument& operator*() const { return value_; } + + const ReceivedMessageArgument* operator->() const { return &value_; } + + friend bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ); + +private: + ReceivedMessageArgument value_; + + void Advance(); + + bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const + { + return value_.typeTag_ == rhs.value_.typeTag_; + } +}; + +inline bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgumentStream{ + friend class ReceivedMessage; + ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin, + const ReceivedMessageArgumentIterator& end ) + : p_( begin ) + , end_( end ) {} + + ReceivedMessageArgumentIterator p_, end_; + +public: + + // end of stream + bool Eos() const { return p_ == end_; } + + ReceivedMessageArgumentStream& operator>>( bool& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsBool(); + return *this; + } + + // not sure if it would be useful to stream Nil and Infinitum + // for now it's not possible + + ReceivedMessageArgumentStream& operator>>( int32& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt32(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( float& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsFloat(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( char& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsChar(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsRgbaColor(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsMidiMessage(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( int64& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt64(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( TimeTag& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsTimeTag(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( double& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsDouble(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Blob& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + (*p_++).AsBlob( rhs.data, rhs.size ); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( const char*& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsString(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Symbol& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsSymbol(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs ) + { + if( !Eos() ) + throw ExcessArgumentException(); + + return *this; + } +}; + + +class ReceivedMessage{ + void Init( const char *bundle, unsigned long size ); +public: + explicit ReceivedMessage( const ReceivedPacket& packet ); + explicit ReceivedMessage( const ReceivedBundleElement& bundleElement ); + + const char *AddressPattern() const { return addressPattern_; } + + // Support for non-standad SuperCollider integer address patterns: + bool AddressPatternIsUInt32() const; + uint32 AddressPatternAsUInt32() const; + + unsigned long ArgumentCount() const { return static_cast(typeTagsEnd_ - typeTagsBegin_); } + + const char *TypeTags() const { return typeTagsBegin_; } + + + typedef ReceivedMessageArgumentIterator const_iterator; + + ReceivedMessageArgumentIterator ArgumentsBegin() const + { + return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ ); + } + + ReceivedMessageArgumentIterator ArgumentsEnd() const + { + return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 ); + } + + ReceivedMessageArgumentStream ArgumentStream() const + { + return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() ); + } + +private: + const char *addressPattern_; + const char *typeTagsBegin_; + const char *typeTagsEnd_; + const char *arguments_; +}; + + +class ReceivedBundle{ + void Init( const char *message, unsigned long size ); +public: + explicit ReceivedBundle( const ReceivedPacket& packet ); + explicit ReceivedBundle( const ReceivedBundleElement& bundleElement ); + + uint64 TimeTag() const; + + unsigned long ElementCount() const { return elementCount_; } + + typedef ReceivedBundleElementIterator const_iterator; + + ReceivedBundleElementIterator ElementsBegin() const + { + return ReceivedBundleElementIterator( timeTag_ + 8 ); + } + + ReceivedBundleElementIterator ElementsEnd() const + { + return ReceivedBundleElementIterator( end_ ); + } + +private: + const char *timeTag_; + const char *end_; + unsigned long elementCount_; +}; + + +} // namespace osc + + +#endif /* INCLUDED_OSCRECEIVEDELEMENTS_H */ diff --git a/protocol/osc/oscpack/OscTypes.cpp b/protocol/osc/oscpack/OscTypes.cpp new file mode 100644 index 000000000..cf8f69863 --- /dev/null +++ b/protocol/osc/oscpack/OscTypes.cpp @@ -0,0 +1,40 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "OscTypes.h" + +namespace osc{ + +BundleInitiator BeginBundleImmediate(1); +BundleTerminator EndBundle; +MessageTerminator EndMessage; +NilType Nil; +InfinitumType Infinitum; + +} // namespace osc diff --git a/protocol/osc/oscpack/OscTypes.h b/protocol/osc/oscpack/OscTypes.h new file mode 100644 index 000000000..b31911143 --- /dev/null +++ b/protocol/osc/oscpack/OscTypes.h @@ -0,0 +1,183 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCTYPES_H +#define INCLUDED_OSCTYPES_H + + +namespace osc{ + +// basic types + +#if defined(__BORLANDC__) || defined(_MSC_VER) + +typedef __int64 int64; +typedef unsigned __int64 uint64; + +#elif defined(__x86_64__) || defined(_M_X64) + +typedef long int64; +typedef unsigned long uint64; + +#else + +typedef long long int64; +typedef unsigned long long uint64; + +#endif + + + +#if defined(__x86_64__) || defined(_M_X64) + +typedef signed int int32; +typedef unsigned int uint32; + +#else + +typedef signed long int32; +typedef unsigned long uint32; + +#endif + + + +enum TypeTagValues { + TRUE_TYPE_TAG = 'T', + FALSE_TYPE_TAG = 'F', + NIL_TYPE_TAG = 'N', + INFINITUM_TYPE_TAG = 'I', + INT32_TYPE_TAG = 'i', + FLOAT_TYPE_TAG = 'f', + CHAR_TYPE_TAG = 'c', + RGBA_COLOR_TYPE_TAG = 'r', + MIDI_MESSAGE_TYPE_TAG = 'm', + INT64_TYPE_TAG = 'h', + TIME_TAG_TYPE_TAG = 't', + DOUBLE_TYPE_TAG = 'd', + STRING_TYPE_TAG = 's', + SYMBOL_TYPE_TAG = 'S', + BLOB_TYPE_TAG = 'b' +}; + + + +// i/o manipulators used for streaming interfaces + +struct BundleInitiator{ + explicit BundleInitiator( uint64 timeTag_ ) : timeTag( timeTag_ ) {} + uint64 timeTag; +}; + +extern BundleInitiator BeginBundleImmediate; + +inline BundleInitiator BeginBundle( uint64 timeTag=1 ) +{ + return BundleInitiator(timeTag); +} + + +struct BundleTerminator{ +}; + +extern BundleTerminator EndBundle; + +struct BeginMessage{ + explicit BeginMessage( const char *addressPattern_ ) : addressPattern( addressPattern_ ) {} + const char *addressPattern; +}; + +struct MessageTerminator{ +}; + +extern MessageTerminator EndMessage; + + +// osc specific types. they are defined as structs so they can be used +// as separately identifiable types with the streaming operators. + +struct NilType{ +}; + +extern NilType Nil; + + +struct InfinitumType{ +}; + +extern InfinitumType Infinitum; + +struct RgbaColor{ + RgbaColor() {} + explicit RgbaColor( uint32 value_ ) : value( value_ ) {} + uint32 value; + + operator uint32() const { return value; } +}; + + +struct MidiMessage{ + MidiMessage() {} + explicit MidiMessage( uint32 value_ ) : value( value_ ) {} + uint32 value; + + operator uint32() const { return value; } +}; + + +struct TimeTag{ + TimeTag() {} + explicit TimeTag( uint64 value_ ) : value( value_ ) {} + uint64 value; + + operator uint64() const { return value; } +}; + + +struct Symbol{ + Symbol() {} + explicit Symbol( const char* value_ ) : value( value_ ) {} + const char* value; + + operator const char *() const { return value; } +}; + + +struct Blob{ + Blob() {} + explicit Blob( const void* data_, unsigned long size_ ) + : data( data_ ), size( size_ ) {} + const void* data; + unsigned long size; +}; + +} // namespace osc + + +#endif /* INCLUDED_OSCTYPES_H */ diff --git a/protocol/osc/server.cpp b/protocol/osc/server.cpp new file mode 100644 index 000000000..b444abe61 --- /dev/null +++ b/protocol/osc/server.cpp @@ -0,0 +1,122 @@ +#include "..\stdafx.h" + +#include "server.h" + +#include "oscpack/oscOutboundPacketStream.h" + +#include + +#include +#include + +#include +#include +#include + +using namespace boost::asio::ip; + +namespace caspar { namespace protocol { namespace osc { + +template +struct param_visitor : public boost::static_visitor +{ + T& o; + + param_visitor(T& o) + : o(o) + { + } + + void operator()(const bool value) {o << value;} + void operator()(const int32_t value) {o << static_cast(value);} + void operator()(const uint32_t value) {o << static_cast(value);} + void operator()(const int64_t value) {o << static_cast(value);} + void operator()(const uint64_t value) {o << static_cast(value);} + void operator()(const float value) {o << value;} + void operator()(const double value) {o << static_cast(value);} + void operator()(const std::string& value) {o << value.c_str();} + void operator()(const std::wstring& value) {o << narrow(value).c_str();} + void operator()(const std::vector& value) {o << ::osc::Blob(value.data(), static_cast(value.size()));} +}; + +std::vector write_osc_event(const core::monitor::message& e) +{ + std::array buffer; + ::osc::OutboundPacketStream o(buffer.data(), static_cast(buffer.size())); + + o << ::osc::BeginMessage(e.path().c_str()); + + param_visitor pd_visitor(o); + BOOST_FOREACH(auto data, e.data()) + boost::apply_visitor(pd_visitor, data); + + o << ::osc::EndMessage; + + return std::vector(o.Data(), o.Data() + o.Size()); +} + +struct server::impl +{ + boost::asio::io_service service_; + + udp::endpoint endpoint_; + udp::socket socket_; + + boost::thread thread_; + + Concurrency::call on_next_; + +public: + impl(udp::endpoint endpoint, + Concurrency::ISource& source) + : endpoint_(endpoint) + , socket_(service_, endpoint_.protocol()) + , thread_(std::bind(&boost::asio::io_service::run, &service_)) + , on_next_([this](const core::monitor::message& msg){ on_next(msg); }) + { + source.link_target(&on_next_); + } + + ~impl() + { + thread_.join(); + } + + void on_next(const core::monitor::message& msg) + { + auto data_ptr = make_safe>(write_osc_event(msg)); + + socket_.async_send_to(boost::asio::buffer(*data_ptr), + endpoint_, + boost::bind(&impl::handle_send_to, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + + void handle_send_to(const boost::system::error_code& /*error*/, size_t /*bytes_sent*/) + { + } +}; + +server::server(udp::endpoint endpoint, + Concurrency::ISource& source) + : impl_(new impl(endpoint, source)) +{ +} + +server::server(server&& other) + : impl_(std::move(other.impl_)) +{ +} + +server& server::operator=(server&& other) +{ + impl_ = std::move(other.impl_); + return *this; +} + +server::~server() +{ +} + +}}} \ No newline at end of file diff --git a/protocol/osc/server.h b/protocol/osc/server.h new file mode 100644 index 000000000..1ca4f30cf --- /dev/null +++ b/protocol/osc/server.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include + +namespace caspar { namespace protocol { namespace osc { + +class server +{ + server(const server&); + server& operator=(const server&); +public: + + // Static Members + + // Constructors + + server(boost::asio::ip::udp::endpoint endpoint, + Concurrency::ISource& source); + + server(server&&); + + ~server(); + + // Methods + + server& operator=(server&&); + + // Properties + +private: + struct impl; + std::unique_ptr impl_; +}; + +}}} \ No newline at end of file diff --git a/protocol/protocol.vcxproj b/protocol/protocol.vcxproj index 67fb832e6..ea2043b97 100644 --- a/protocol/protocol.vcxproj +++ b/protocol/protocol.vcxproj @@ -43,6 +43,15 @@ + + + + + + + + + @@ -100,6 +109,36 @@ ../StdAfx.h ../StdAfx.h + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + ../StdAfx.h + ../StdAfx.h + ../StdAfx.h + ../StdAfx.h + Create Create diff --git a/protocol/protocol.vcxproj.filters b/protocol/protocol.vcxproj.filters index 9f2e7d2fa..9481cb915 100644 --- a/protocol/protocol.vcxproj.filters +++ b/protocol/protocol.vcxproj.filters @@ -16,6 +16,12 @@ {93331f26-581b-4d15-81a6-0aae31ad3958} + + {b2150993-f000-4bf8-bb4f-e97d2b54a479} + + + {6d9a82d4-6805-4de0-b400-6212fac06109} + @@ -67,6 +73,33 @@ source\util + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc + @@ -106,5 +139,20 @@ source\util + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc\oscpack + + + source\osc + \ No newline at end of file diff --git a/shell/casparcg.config b/shell/casparcg.config index 9f4c5cffc..aa5c72bd4 100644 --- a/shell/casparcg.config +++ b/shell/casparcg.config @@ -1,11 +1,11 @@ - media\ - log\ - data\ - templates\ - thumbnails\ + M:\\_media\\ + M:\\_log\\ + M:\\_data\\ + M:\\_template\\ + M:\\_thumbnails\\ @@ -23,6 +23,11 @@ 5250 AMCP + +
127.0.0.1
+ 5253 + OSC +
diff --git a/shell/main.cpp b/shell/main.cpp index a41f3857b..294e4d9ac 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -267,6 +268,7 @@ int main(int argc, wchar_t* argv[]) // Create server object which initializes channels, protocols and controllers. caspar::server caspar_server(shutdown_server_now); + // Use separate thread for the blocking console input, will be terminated // anyway when the main thread terminates. diff --git a/shell/server.cpp b/shell/server.cpp index e36fda83a..d9335a6ad 100644 --- a/shell/server.cpp +++ b/shell/server.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -70,9 +71,11 @@ using namespace protocol; struct server::implementation : boost::noncopyable { + core::monitor::subject monitor_subject_; boost::promise& shutdown_server_now_; safe_ptr ogl_; std::vector> async_servers_; + std::vector osc_servers_; std::vector> channels_; std::shared_ptr thumbnail_generator_; @@ -129,6 +132,8 @@ struct server::implementation : boost::noncopyable channels_.push_back(make_safe(channels_.size()+1, format_desc, ogl_)); + channels_.back()->monitor_output().link_target(&monitor_subject_); + BOOST_FOREACH(auto& xml_consumer, xml_channel.second.get_child(L"consumers")) { try @@ -176,6 +181,13 @@ struct server::implementation : boost::noncopyable asyncbootstrapper->Start(); async_servers_.push_back(asyncbootstrapper); } + else if(name == L"udp") + { + const auto address = xml_controller.second.get(L"address", L"127.0.0.1"); + const auto port = xml_controller.second.get(L"port", 5253); + + osc_servers_.push_back(osc::server(boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::from_string(narrow(address)), port), monitor_subject_)); + } else CASPAR_LOG(warning) << "Invalid controller: " << widen(name); } @@ -237,4 +249,9 @@ std::shared_ptr server::get_thumbnail_generator() const return impl_->thumbnail_generator_; } +core::monitor::source& server::monitor_output() +{ + return impl_->monitor_subject_; +} + } \ No newline at end of file diff --git a/shell/server.h b/shell/server.h index 13948ee0b..01ed69b81 100644 --- a/shell/server.h +++ b/shell/server.h @@ -24,6 +24,8 @@ #include +#include + #include #include @@ -42,6 +44,9 @@ public: server(boost::promise& shutdown_server_now); const std::vector> get_channels() const; std::shared_ptr get_thumbnail_generator() const; + + core::monitor::source& monitor_output(); + private: struct implementation; safe_ptr impl_;