]> git.sesse.net Git - casparcg/blobdiff - core/producer/layer.cpp
2.0. layer: Fixed potential arethmetic overflows.
[casparcg] / core / producer / layer.cpp
index b50d08a4dcdb177d580132bf405e771a2e8a8962..f940d2a9eb6bda491fa0253bc9b376dcbc47fa50 100644 (file)
+/*\r
+* copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
+*\r
+*  This file is part of CasparCG.\r
+*\r
+*    CasparCG is free software: you can redistribute it and/or modify\r
+*    it under the terms of the GNU General Public License as published by\r
+*    the Free Software Foundation, either version 3 of the License, or\r
+*    (at your option) any later version.\r
+*\r
+*    CasparCG is distributed in the hope that it will be useful,\r
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+*    GNU General Public License for more details.\r
+\r
+*    You should have received a copy of the GNU General Public License\r
+*    along with CasparCG.  If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+*/\r
+\r
 #include "../stdafx.h"\r
 \r
 #include "layer.h"\r
-#include "frame_producer.h"\r
-\r
-#include <common/concurrency/executor.h>\r
-#include <common/exception/exceptions.h>\r
-#include <common/utility/assert.h>\r
-#include <common/utility/printer.h>\r
-\r
-#include "../producer/frame/basic_frame.h"\r
-#include "../producer/frame/audio_transform.h"\r
 \r
-#include <tbb/spin_mutex.h>\r
+#include "frame_producer.h"\r
+#include "frame/basic_frame.h"\r
 \r
 namespace caspar { namespace core {\r
+       \r
+struct layer::implementation\r
+{                              \r
+       safe_ptr<frame_producer>        foreground_;\r
+       safe_ptr<frame_producer>        background_;\r
+       int64_t                                         frame_number_;\r
+       int                                                     auto_play_delta_;\r
+       bool                                            is_paused_;\r
 \r
-class frame_producer_remover\r
-{\r
-       executor executor_;\r
-\r
-       void do_remove(safe_ptr<frame_producer>& producer)\r
+public:\r
+       implementation() \r
+               : foreground_(frame_producer::empty())\r
+               , background_(frame_producer::empty())\r
+               , frame_number_(0)\r
+               , auto_play_delta_(-1)\r
+               , is_paused_(false)\r
        {\r
-               auto name = producer->print();\r
-               producer = frame_producer::empty();\r
-               CASPAR_LOG(info) << name << L" Removed.";\r
        }\r
-\r
-public:\r
-\r
-       frame_producer_remover() : executor_(L"frame_producer_remover")\r
+       \r
+       void pause()\r
        {\r
-               executor_.start();\r
+               is_paused_ = true;\r
        }\r
 \r
-       void remove(safe_ptr<frame_producer>&& producer)\r
+       void resume()\r
        {\r
-               CASPAR_VERIFY(producer.unique());\r
-               executor_.begin_invoke(std::bind(&frame_producer_remover::do_remove, this, std::move(producer)));\r
+               is_paused_ = false;\r
        }\r
-};\r
-\r
-frame_producer_remover g_remover;\r
 \r
-struct layer::implementation : boost::noncopyable\r
-{                              \r
-       mutable tbb::spin_mutex         printer_mutex_;\r
-       printer                                         parent_printer_;\r
-       int                                                     index_;\r
-       \r
-       safe_ptr<frame_producer>        foreground_;\r
-       safe_ptr<frame_producer>        background_;\r
-       safe_ptr<basic_frame>           last_frame_;\r
-       bool                                            is_paused_;\r
-public:\r
-       implementation(int index, const printer& parent_printer) \r
-               : parent_printer_(parent_printer)\r
-               , index_(index)\r
-               , foreground_(frame_producer::empty())\r
-               , background_(frame_producer::empty())\r
-               , last_frame_(basic_frame::empty())\r
-               , is_paused_(false){}\r
-       \r
-       void load(const safe_ptr<frame_producer>& frame_producer, bool play_on_load, bool preview)\r
+       void load(const safe_ptr<frame_producer>& producer, bool preview, int auto_play_delta)\r
        {               \r
-               background_ = frame_producer;\r
-               is_paused_ = false;\r
+               background_              = producer;\r
+               auto_play_delta_ = auto_play_delta;\r
 \r
-               if(preview)\r
-               {\r
+               if(preview) // Play the first frame and pause.\r
+               {                       \r
                        play();\r
                        receive();\r
                        pause();\r
                }\r
-\r
-               if(play_on_load)\r
-                       play();                         \r
        }\r
-\r
+       \r
        void play()\r
        {                       \r
-               if(!is_paused_)                 \r
+               if(background_ != frame_producer::empty())\r
                {\r
                        background_->set_leading_producer(foreground_);\r
-                       foreground_ = background_;\r
-                       CASPAR_LOG(info) << foreground_->print() << L" Added.";\r
-                       background_ = frame_producer::empty();\r
+                       \r
+                       foreground_                     = background_;\r
+                       background_                     = frame_producer::empty();\r
+                       frame_number_           = 0;\r
+                       auto_play_delta_        = -1;   \r
                }\r
-               is_paused_ = false;\r
-       }\r
 \r
-       void pause()\r
-       {\r
-               is_paused_ = true;\r
+               is_paused_                      = false;\r
        }\r
-\r
+       \r
        void stop()\r
        {\r
-               pause();\r
-               last_frame_ = basic_frame::empty();\r
-               foreground_ = frame_producer::empty();\r
+               foreground_                     = frame_producer::empty();\r
+               background_                     = background_;\r
+               frame_number_           = 0;\r
+               auto_play_delta_        = -1;\r
+\r
+               is_paused_                      = true;\r
        }\r
                \r
        safe_ptr<basic_frame> receive()\r
        {               \r
-               if(is_paused_)\r
-               {\r
-                       last_frame_->get_audio_transform().set_gain(0.0);\r
-                       return last_frame_;\r
-               }\r
-\r
                try\r
                {\r
-                       last_frame_ = foreground_->receive(); \r
-                       if(last_frame_ == basic_frame::eof())\r
-                       {\r
-                               CASPAR_VERIFY(foreground_ != frame_producer::empty());\r
-\r
-                               auto following = foreground_->get_following_producer();\r
-                               following->set_leading_producer(foreground_);\r
-                               following->set_parent_printer([=]{return print();});\r
-                               g_remover.remove(std::move(foreground_));\r
-                               foreground_ = following;\r
-                               CASPAR_LOG(info) << foreground_->print() << L" Added.";\r
+                       if(is_paused_)\r
+                               return disable_audio(foreground_->last_frame());\r
+               \r
+                       auto frame = receive_and_follow(foreground_, frame_producer::NO_HINT);\r
+                       if(frame == core::basic_frame::late())\r
+                               return foreground_->last_frame();\r
 \r
-                               last_frame_ = receive();\r
+                       auto frames_left = foreground_->nb_frames() - (++frame_number_) - auto_play_delta_;\r
+                       if(auto_play_delta_ > -1 && frames_left < 1)\r
+                       {\r
+                               play();\r
+                               return receive();\r
                        }\r
+                               \r
+                       return frame;\r
                }\r
                catch(...)\r
                {\r
-                       CASPAR_LOG(error) << print() << L" Unhandled Exception: ";\r
                        CASPAR_LOG_CURRENT_EXCEPTION();\r
                        stop();\r
+                       return core::basic_frame::empty();\r
                }\r
+       }\r
 \r
-               return last_frame_;\r
+       layer_status status() const\r
+       {\r
+               layer_status status;\r
+               status.foreground        = foreground_->print();\r
+               status.background        = background_->print();\r
+               status.is_paused         = is_paused_;\r
+               status.total_frames      = foreground_->nb_frames();\r
+               status.current_frame = frame_number_;\r
+\r
+               return status;\r
        }\r
-               \r
-       std::wstring print() const\r
+\r
+       bool empty() const\r
        {\r
-               tbb::spin_mutex::scoped_lock lock(printer_mutex_); // Child-producers may call print asynchronously to the producer thread.\r
-               return (parent_printer_ ? parent_printer_() + L"/" : L"") + L"layer[" + boost::lexical_cast<std::wstring>(index_) + L"]";\r
+               return background_ == core::frame_producer::empty() && foreground_ == core::frame_producer::empty();\r
        }\r
 };\r
 \r
-layer::layer(int index, const printer& parent_printer) : impl_(new implementation(index, parent_printer)){}\r
+layer::layer() : impl_(new implementation()){}\r
 layer::layer(layer&& other) : impl_(std::move(other.impl_)){}\r
 layer& layer::operator=(layer&& other)\r
 {\r
        impl_ = std::move(other.impl_);\r
        return *this;\r
 }\r
-void layer::swap(layer& other)\r
+layer::layer(const layer& other) : impl_(new implementation(*other.impl_)){}\r
+layer& layer::operator=(const layer& other)\r
 {\r
+       layer temp(other);\r
+       temp.swap(*this);\r
+       return *this;\r
+}\r
+void layer::swap(layer& other)\r
+{      \r
        impl_.swap(other.impl_);\r
-       // Printer state is not swapped.\r
-       tbb::spin_mutex::scoped_lock lock(impl_->printer_mutex_);\r
-       std::swap(impl_->parent_printer_, other.impl_->parent_printer_);\r
-       std::swap(impl_->index_, other.impl_->index_);\r
 }\r
-void layer::load(const safe_ptr<frame_producer>& frame_producer, bool play_on_load, bool preview){return impl_->load(frame_producer, play_on_load, preview);}  \r
+void layer::load(const safe_ptr<frame_producer>& frame_producer, bool preview, int auto_play_delta){return impl_->load(frame_producer, preview, auto_play_delta);}     \r
 void layer::play(){impl_->play();}\r
 void layer::pause(){impl_->pause();}\r
 void layer::stop(){impl_->stop();}\r
+bool layer::is_paused() const{return impl_->is_paused_;}\r
+int64_t layer::frame_number() const{return impl_->frame_number_;}\r
+layer_status layer::status() const {return impl_->status();}\r
 safe_ptr<basic_frame> layer::receive() {return impl_->receive();}\r
 safe_ptr<frame_producer> layer::foreground() const { return impl_->foreground_;}\r
 safe_ptr<frame_producer> layer::background() const { return impl_->background_;}\r
-std::wstring layer::print() const { return impl_->print();}\r
+bool layer::empty() const {return impl_->empty();}\r
 }}
\ No newline at end of file