]> git.sesse.net Git - casparcg/commitdiff
3563178: Added KILL command to shutdown the server without console access.
authorhellgore <hellgore@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Tue, 19 Mar 2013 15:29:51 +0000 (15:29 +0000)
committerhellgore <hellgore@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Tue, 19 Mar 2013 15:29:51 +0000 (15:29 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/trunk@3783 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

CHANGES.txt
protocol/amcp/AMCPCommand.h
protocol/amcp/AMCPCommandsImpl.cpp
protocol/amcp/AMCPCommandsImpl.h
protocol/amcp/AMCPProtocolStrategy.cpp
protocol/amcp/AMCPProtocolStrategy.h
shell/main.cpp
shell/server.cpp
shell/server.h

index af29ef3e946bbc8baee2396642f0227f00391497..7c33485f723142ae02a80e842f14e77a9eaac2d2 100644 (file)
@@ -1,5 +1,5 @@
 CasparCG 2.0.4 Stable (w.r.t 2.0.3 Stable)\r
-=========================================\r
+==========================================\r
 \r
 General\r
 -------\r
@@ -48,6 +48,7 @@ AMCP
     the thumbnail feature.\r
   o ADD 1 FILE output.mov SEPARETE_KEY activates the separate key feature of the\r
     ffmpeg consumer creating an additional output_a.mov containing only the key.\r
+  o Added KILL command for shutting down the server without console access.\r
 \r
 CasparCG 2.0.3 Stable (w.r.t 2.0.3 Alpha)\r
 =========================================\r
index 6fc9fffb1c838443be63cd86a0ee4bf49b1bf673..b0200a95a6930b557845a570bf170e87035211aa 100644 (file)
@@ -68,6 +68,9 @@ namespace amcp {
                void SetThumbGenerator(const std::shared_ptr<core::thumbnail_generator>& thumb_gen) {thumb_gen_ = thumb_gen;}\r
                std::shared_ptr<core::thumbnail_generator> GetThumbGenerator() { return thumb_gen_; }\r
 \r
+               void SetShutdownServerNow(boost::promise<bool>& shutdown_server_now) {shutdown_server_now_ = &shutdown_server_now;}\r
+               boost::promise<bool>& GetShutdownServerNow() { return *shutdown_server_now_; }\r
+\r
                void SetChannelIndex(unsigned int channelIndex){channelIndex_ = channelIndex;}\r
                unsigned int GetChannelIndex(){return channelIndex_;}\r
 \r
@@ -97,6 +100,7 @@ namespace amcp {
                std::shared_ptr<core::video_channel> pChannel_;\r
                std::vector<safe_ptr<core::video_channel>> channels_;\r
                std::shared_ptr<core::thumbnail_generator> thumb_gen_;\r
+               boost::promise<bool>* shutdown_server_now_;\r
                AMCPCommandScheduling scheduling_;\r
                std::wstring replyString_;\r
        };\r
index 2483ac2197362b6d82bfc2f7b09ff34483231584..19b7ae3fc1e244d8b6a1372c61644799dc20445b 100644 (file)
@@ -1844,6 +1844,11 @@ bool SetCommand::DoExecute()
        return true;\r
 }\r
 \r
+bool KillCommand::DoExecute()\r
+{\r
+       GetShutdownServerNow().set_value(false); // False for not waiting until a key is pressed before terminating.\r
+       return true;\r
+}\r
 \r
 }      //namespace amcp\r
 }}     //namespace caspar
\ No newline at end of file
index e1f5b519c93fbd7199cd5703fa2b4f1a933a82c9..37c4f2c5dec7601c19b585eee65f2e41dda5205b 100644 (file)
@@ -205,23 +205,11 @@ class SetCommand : public AMCPCommandBase<true, AddToQueue, 2>
        bool DoExecute();\r
 };\r
 \r
-//class KillCommand : public AMCPCommand\r
-//{\r
-//public:\r
-//     KillCommand() {}\r
-//     virtual bool DoExecute();\r
-//     virtual AMCPCommandCondition CheckConditions();\r
-//\r
-//     virtual bool NeedChannel() {\r
-//             return false;\r
-//     }\r
-//     virtual AMCPCommandScheduling GetDefaultScheduling() {\r
-//             return AddToQueue;\r
-//     }\r
-//     virtual int GetMinimumParameters() {\r
-//             return 0;\r
-//     }\r
-//};\r
+class KillCommand : public AMCPCommandBase<false, AddToQueue, 0>\r
+{\r
+       std::wstring print() const { return L"KillCommand";}\r
+       bool DoExecute();\r
+};\r
 \r
 }      //namespace amcp\r
 }}     //namespace caspar\r
index 42d635675849fd6c9a98da73595b36126a885e33..4398459695271037fabb85eecb2118833334e8e6 100644 (file)
@@ -53,9 +53,14 @@ inline std::shared_ptr<core::video_channel> GetChannelSafe(unsigned int index, c
        return index < channels.size() ? std::shared_ptr<core::video_channel>(channels[index]) : nullptr;\r
 }\r
 \r
-AMCPProtocolStrategy::AMCPProtocolStrategy(const std::vector<safe_ptr<core::video_channel>>& channels, const std::shared_ptr<core::thumbnail_generator>& thumb_gen)\r
-               : channels_(channels)\r
-               , thumb_gen_(thumb_gen) {\r
+AMCPProtocolStrategy::AMCPProtocolStrategy(\r
+               const std::vector<safe_ptr<core::video_channel>>& channels,\r
+               const std::shared_ptr<core::thumbnail_generator>& thumb_gen,\r
+               boost::promise<bool>& shutdown_server_now)\r
+       : channels_(channels)\r
+       , thumb_gen_(thumb_gen)\r
+       , shutdown_server_now_(shutdown_server_now)\r
+{\r
        AMCPCommandQueuePtr pGeneralCommandQueue(new AMCPCommandQueue());\r
        commandQueues_.push_back(pGeneralCommandQueue);\r
 \r
@@ -190,6 +195,7 @@ AMCPCommandPtr AMCPProtocolStrategy::InterpretCommandString(const std::wstring&
                        {\r
                                pCommand->SetChannels(channels_);\r
                                pCommand->SetThumbGenerator(thumb_gen_);\r
+                               pCommand->SetShutdownServerNow(shutdown_server_now_);\r
                                //Set scheduling\r
                                if(commandSwitch.size() > 0) {\r
                                        transform(commandSwitch.begin(), commandSwitch.end(), commandSwitch.begin(), toupper);\r
@@ -331,10 +337,7 @@ AMCPCommandPtr AMCPProtocolStrategy::CommandFactory(const std::wstring& str)
        //{\r
        //      result = AMCPCommandPtr(new MonitorCommand());\r
        //}\r
-       //else if(s == TEXT("KILL"))\r
-       //{\r
-       //      result = AMCPCommandPtr(new KillCommand());\r
-       //}\r
+       else if(s == TEXT("KILL"))                      return std::make_shared<KillCommand>();\r
        return nullptr;\r
 }\r
 \r
index 624d5ff07df967265a99906a3a53d090d3ff029b..46f00ecbcd83b2d2b0ecf84f0572d813f9b718a1 100644 (file)
@@ -29,6 +29,7 @@
 #include "AMCPCommandQueue.h"\r
 \r
 #include <boost/noncopyable.hpp>\r
+#include <boost/thread/future.hpp>\r
 \r
 namespace caspar { namespace protocol { namespace amcp {\r
 \r
@@ -47,7 +48,10 @@ class AMCPProtocolStrategy : public IO::IProtocolStrategy, boost::noncopyable
        AMCPProtocolStrategy& operator=(const AMCPProtocolStrategy&);\r
 \r
 public:\r
-       AMCPProtocolStrategy(const std::vector<safe_ptr<core::video_channel>>& channels, const std::shared_ptr<core::thumbnail_generator>& thumb_gen);\r
+       AMCPProtocolStrategy(\r
+                       const std::vector<safe_ptr<core::video_channel>>& channels,\r
+                       const std::shared_ptr<core::thumbnail_generator>& thumb_gen,\r
+                       boost::promise<bool>& shutdown_server_now);\r
        virtual ~AMCPProtocolStrategy();\r
 \r
        virtual void Parse(const TCHAR* pData, int charCount, IO::ClientInfoPtr pClientInfo);\r
@@ -68,6 +72,7 @@ private:
 \r
        std::vector<safe_ptr<core::video_channel>> channels_;\r
        std::shared_ptr<core::thumbnail_generator> thumb_gen_;\r
+       boost::promise<bool>& shutdown_server_now_;\r
        std::vector<AMCPCommandQueuePtr> commandQueues_;\r
        static const std::wstring MessageDelimiter;\r
 };\r
index 4e40290e0f8ffd2ba599f27804bc9d5764c50b49..a41f3857b28e91b74c528cbdd2af90700e63ffa7 100644 (file)
 #include <boost/property_tree/detail/file_parser_error.hpp>\r
 #include <boost/property_tree/xml_parser.hpp>\r
 #include <boost/foreach.hpp>\r
+#include <boost/thread.hpp>\r
+#include <boost/thread/future.hpp>\r
 #include <boost/locale.hpp>\r
+#include <boost/algorithm/string/case_conv.hpp>\r
 \r
 // NOTE: This is needed in order to make CComObject work since this is not a real ATL project.\r
 CComModule _AtlModule;\r
@@ -174,6 +177,11 @@ LONG WINAPI UserUnhandledExceptionFilter(EXCEPTION_POINTERS* info)
     return EXCEPTION_EXECUTE_HANDLER;\r
 }\r
 \r
+void make_upper_case(std::wstring& str)\r
+{\r
+       boost::to_upper(str);\r
+}\r
+\r
 int main(int argc, wchar_t* argv[])\r
 {      \r
        static_assert(sizeof(void*) == 4, "64-bit code generation is not supported.");\r
@@ -251,90 +259,110 @@ int main(int argc, wchar_t* argv[])
                boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);\r
                boost::property_tree::write_xml(str, caspar::env::properties(), w);\r
                CASPAR_LOG(info) << L"casparcg.config:\n-----------------------------------------\n" << str.str().c_str() << L"-----------------------------------------";\r
-                               \r
+               bool wait_for_keypress;\r
+\r
                {\r
-                       // Create server object which initializes channels, protocols and controllers.\r
-                       caspar::server caspar_server;\r
-                               \r
-                       // Create a amcp parser for console commands.\r
-                       caspar::protocol::amcp::AMCPProtocolStrategy amcp(caspar_server.get_channels(), caspar_server.get_thumbnail_generator());\r
+                       boost::promise<bool> shutdown_server_now;\r
+                       boost::unique_future<bool> shutdown_server = shutdown_server_now.get_future();\r
 \r
-                       // Create a dummy client which prints amcp responses to console.\r
-                       auto console_client = std::make_shared<caspar::IO::ConsoleClientInfo>();\r
+                       // Create server object which initializes channels, protocols and controllers.\r
+                       caspar::server caspar_server(shutdown_server_now);\r
 \r
-                       std::wstring wcmd;\r
-                       while(true)\r
+                       // Use separate thread for the blocking console input, will be terminated \r
+                       // anyway when the main thread terminates.\r
+                       boost::thread stdin_thread([&caspar_server, &shutdown_server_now]\r
                        {\r
-                               std::getline(std::wcin, wcmd); // TODO: It's blocking...\r
+                               // Create a amcp parser for console commands.\r
+                               caspar::protocol::amcp::AMCPProtocolStrategy amcp(\r
+                                               caspar_server.get_channels(),\r
+                                               caspar_server.get_thumbnail_generator(),\r
+                                               shutdown_server_now);\r
+\r
+                               // Create a dummy client which prints amcp responses to console.\r
+                               auto console_client = std::make_shared<caspar::IO::ConsoleClientInfo>();\r
+                               std::wstring wcmd;\r
+       \r
+                               while(true)\r
+                               {\r
+                                       std::getline(std::wcin, wcmd); // TODO: It's blocking...\r
                                \r
-                               boost::to_upper(wcmd);\r
+                                       //boost::to_upper(wcmd);  // TODO COMPILER crashes on this line, Strange!\r
+                                       make_upper_case(wcmd);\r
 \r
-                               if(wcmd == L"EXIT" || wcmd == L"Q" || wcmd == L"QUIT" || wcmd == L"BYE")\r
-                                       break;\r
-                               \r
-                               try\r
-                               {\r
-                                       // This is just dummy code for testing.\r
-                                       if(wcmd.substr(0, 1) == L"1")\r
-                                               wcmd = L"LOADBG 1-1 " + wcmd.substr(1, wcmd.length()-1) + L" SLIDE 100 LOOP \r\nPLAY 1-1";\r
-                                       else if(wcmd.substr(0, 1) == L"2")\r
-                                               wcmd = L"MIXER 1-0 VIDEO IS_KEY 1";\r
-                                       else if(wcmd.substr(0, 1) == L"3")\r
-                                               wcmd = L"CG 1-2 ADD 1 BBTELEFONARE 1";\r
-                                       else if(wcmd.substr(0, 1) == L"4")\r
-                                               wcmd = L"PLAY 1-1 DV FILTER yadif=1:-1 LOOP";\r
-                                       else if(wcmd.substr(0, 1) == L"5")\r
+                                       if(wcmd == L"EXIT" || wcmd == L"Q" || wcmd == L"QUIT" || wcmd == L"BYE")\r
                                        {\r
-                                               auto file = wcmd.substr(2, wcmd.length()-1);\r
-                                               wcmd = L"PLAY 1-1 " + file + L" LOOP\r\n" \r
-                                                               L"PLAY 1-2 " + file + L" LOOP\r\n" \r
-                                                               L"PLAY 1-3 " + file + L" LOOP\r\n"\r
-                                                               L"PLAY 2-1 " + file + L" LOOP\r\n" \r
-                                                               L"PLAY 2-2 " + file + L" LOOP\r\n" \r
-                                                               L"PLAY 2-3 " + file + L" LOOP\r\n";\r
+                                               shutdown_server_now.set_value(true); // True to wait for keypress\r
+                                               break;\r
                                        }\r
-                                       else if(wcmd.substr(0, 1) == L"X")\r
+                               \r
+                                       try\r
                                        {\r
-                                               int num = 0;\r
-                                               std::wstring file;\r
-                                               try\r
-                                               {\r
-                                                       num = boost::lexical_cast<int>(wcmd.substr(1, 2));\r
-                                                       file = wcmd.substr(4, wcmd.length()-1);\r
-                                               }\r
-                                               catch(...)\r
+                                               // This is just dummy code for testing.\r
+                                               if(wcmd.substr(0, 1) == L"1")\r
+                                                       wcmd = L"LOADBG 1-1 " + wcmd.substr(1, wcmd.length()-1) + L" SLIDE 100 LOOP \r\nPLAY 1-1";\r
+                                               else if(wcmd.substr(0, 1) == L"2")\r
+                                                       wcmd = L"MIXER 1-0 VIDEO IS_KEY 1";\r
+                                               else if(wcmd.substr(0, 1) == L"3")\r
+                                                       wcmd = L"CG 1-2 ADD 1 BBTELEFONARE 1";\r
+                                               else if(wcmd.substr(0, 1) == L"4")\r
+                                                       wcmd = L"PLAY 1-1 DV FILTER yadif=1:-1 LOOP";\r
+                                               else if(wcmd.substr(0, 1) == L"5")\r
                                                {\r
-                                                       num = boost::lexical_cast<int>(wcmd.substr(1, 1));\r
-                                                       file = wcmd.substr(3, wcmd.length()-1);\r
+                                                       auto file = wcmd.substr(2, wcmd.length()-1);\r
+                                                       wcmd = L"PLAY 1-1 " + file + L" LOOP\r\n" \r
+                                                                       L"PLAY 1-2 " + file + L" LOOP\r\n" \r
+                                                                       L"PLAY 1-3 " + file + L" LOOP\r\n"\r
+                                                                       L"PLAY 2-1 " + file + L" LOOP\r\n" \r
+                                                                       L"PLAY 2-2 " + file + L" LOOP\r\n" \r
+                                                                       L"PLAY 2-3 " + file + L" LOOP\r\n";\r
                                                }\r
-\r
-                                               int n = 0;\r
-                                               int num2 = num;\r
-                                               while(num2 > 0)\r
+                                               else if(wcmd.substr(0, 1) == L"X")\r
                                                {\r
-                                                       num2 >>= 1;\r
-                                                       n++;\r
+                                                       int num = 0;\r
+                                                       std::wstring file;\r
+                                                       try\r
+                                                       {\r
+                                                               num = boost::lexical_cast<int>(wcmd.substr(1, 2));\r
+                                                               file = wcmd.substr(4, wcmd.length()-1);\r
+                                                       }\r
+                                                       catch(...)\r
+                                                       {\r
+                                                               num = boost::lexical_cast<int>(wcmd.substr(1, 1));\r
+                                                               file = wcmd.substr(3, wcmd.length()-1);\r
+                                                       }\r
+\r
+                                                       int n = 0;\r
+                                                       int num2 = num;\r
+                                                       while(num2 > 0)\r
+                                                       {\r
+                                                               num2 >>= 1;\r
+                                                               n++;\r
+                                                       }\r
+\r
+                                                       wcmd = L"MIXER 1 GRID " + boost::lexical_cast<std::wstring>(n);\r
+\r
+                                                       for(int i = 1; i <= num; ++i)\r
+                                                               wcmd += L"\r\nPLAY 1-" + boost::lexical_cast<std::wstring>(i) + L" " + file + L" LOOP";// + L" SLIDE 100 LOOP";\r
                                                }\r
-\r
-                                               wcmd = L"MIXER 1 GRID " + boost::lexical_cast<std::wstring>(n);\r
-\r
-                                               for(int i = 1; i <= num; ++i)\r
-                                                       wcmd += L"\r\nPLAY 1-" + boost::lexical_cast<std::wstring>(i) + L" " + file + L" LOOP";// + L" SLIDE 100 LOOP";\r
                                        }\r
-                               }\r
-                               catch (...)\r
-                               {\r
-                                       CASPAR_LOG_CURRENT_EXCEPTION();\r
-                                       continue;\r
-                               }\r
+                                       catch (...)\r
+                                       {\r
+                                               CASPAR_LOG_CURRENT_EXCEPTION();\r
+                                               continue;\r
+                                       }\r
 \r
-                               wcmd += L"\r\n";\r
-                               amcp.Parse(wcmd.c_str(), wcmd.length(), console_client);\r
-                       }       \r
+                                       wcmd += L"\r\n";\r
+                                       amcp.Parse(wcmd.c_str(), wcmd.length(), console_client);\r
+                               }       \r
+                       });\r
+                       stdin_thread.detach();\r
+                       wait_for_keypress = shutdown_server.get();\r
                }\r
                Sleep(500);\r
                CASPAR_LOG(info) << "Successfully shutdown CasparCG Server.";\r
-               system("pause");        \r
+\r
+               if (wait_for_keypress)\r
+                       system("pause");        \r
        }\r
        catch(boost::property_tree::file_parser_error&)\r
        {\r
index d59493d15ce33f6512fdb2f2cbce8a426faa88d5..e36fda83a9d4eca0e77c49f25df7eac270e16c1d 100644 (file)
@@ -70,13 +70,15 @@ using namespace protocol;
 \r
 struct server::implementation : boost::noncopyable\r
 {\r
+       boost::promise<bool>&                                           shutdown_server_now_;\r
        safe_ptr<ogl_device>                                            ogl_;\r
        std::vector<safe_ptr<IO::AsyncEventServer>> async_servers_;     \r
        std::vector<safe_ptr<video_channel>>            channels_;\r
        std::shared_ptr<thumbnail_generator>            thumbnail_generator_;\r
 \r
-       implementation()                \r
-               : ogl_(ogl_device::create())\r
+       implementation(boost::promise<bool>& shutdown_server_now)\r
+               : shutdown_server_now_(shutdown_server_now)\r
+               , ogl_(ogl_device::create())\r
        {                       \r
                ffmpeg::init();\r
                CASPAR_LOG(info) << L"Initialized ffmpeg module.";\r
@@ -209,7 +211,7 @@ struct server::implementation : boost::noncopyable
        safe_ptr<IO::IProtocolStrategy> create_protocol(const std::wstring& name) const\r
        {\r
                if(boost::iequals(name, L"AMCP"))\r
-                       return make_safe<amcp::AMCPProtocolStrategy>(channels_, thumbnail_generator_);\r
+                       return make_safe<amcp::AMCPProtocolStrategy>(channels_, thumbnail_generator_, shutdown_server_now_);\r
                else if(boost::iequals(name, L"CII"))\r
                        return make_safe<cii::CIIProtocolStrategy>(channels_);\r
                else if(boost::iequals(name, L"CLOCK"))\r
@@ -223,7 +225,7 @@ struct server::implementation : boost::noncopyable
        }\r
 };\r
 \r
-server::server() : impl_(new implementation()){}\r
+server::server(boost::promise<bool>& shutdown_server_now) : impl_(new implementation(shutdown_server_now)){}\r
 \r
 const std::vector<safe_ptr<video_channel>> server::get_channels() const\r
 {\r
index 3d83d5f77f5d3548732b30dd4d504d6b088b79f7..13948ee0bd9adee92b0aa4cc57e39ee62fda9ec1 100644 (file)
@@ -25,6 +25,7 @@
 #include <common/memory/safe_ptr.h>\r
 \r
 #include <boost/noncopyable.hpp>\r
+#include <boost/thread/future.hpp>\r
 \r
 #include <vector>\r
 \r
@@ -38,7 +39,7 @@ namespace core {
 class server : boost::noncopyable\r
 {\r
 public:\r
-       server();\r
+       server(boost::promise<bool>& shutdown_server_now);\r
        const std::vector<safe_ptr<core::video_channel>> get_channels() const;\r
        std::shared_ptr<core::thumbnail_generator> get_thumbnail_generator() const;\r
 private:\r