<Filter Include="source\compiler\vs">\r
<UniqueIdentifier>{28c25c8a-1277-4d2c-9e85-5af33f9938ea}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="source\com">\r
+ <UniqueIdentifier>{f9a52080-be84-43f9-939d-855c6fd02414}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="exception\win32_exception.cpp">\r
-/*\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
#pragma once\r
\r
#include <memory>\r
+#include <stdexcept>\r
#include <type_traits>\r
-#include <exception>\r
\r
-namespace caspar {\r
- \r
+namespace caspar\r
+{\r
+\r
template<typename T>\r
class safe_ptr\r
-{ \r
- std::shared_ptr<T> impl_;\r
- template <typename> friend class safe_ptr;\r
+{ \r
+ template <typename> friend class safe_ptr;\r
public:\r
- typedef T element_type;\r
- \r
- safe_ptr() : impl_(std::make_shared<T>()){} \r
- \r
- safe_ptr(const safe_ptr<T>& other) : impl_(other.impl_){} // noexcept\r
- safe_ptr(safe_ptr<T>&& other) : impl_(std::move(other.impl_)){}\r
-\r
- template<typename U>\r
- safe_ptr(const safe_ptr<U>& other, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) : impl_(other.impl_){} // noexcept\r
- \r
- template<typename U> \r
- safe_ptr(const U& impl, typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, T*>::value, void>::type* = 0)\r
- : impl_(std::make_shared<U>(impl)) {}\r
- \r
- template<typename U, typename D> \r
- safe_ptr(const U& impl, D dtor, typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, T*>::value, void>::type* = 0)\r
- : impl_(new U(impl), dtor) {}\r
-\r
- template<typename U> \r
- safe_ptr(U&& impl, typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, T*>::value, void>::type* = 0)\r
- : impl_(std::make_shared<U>(std::forward<U>(impl))) {}\r
-\r
- template<typename U, typename D> \r
- safe_ptr(U&& impl, D dtor, typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, T*>::value, void>::type* = 0)\r
- : impl_(new U(std::forward<U>(impl)), dtor) {}\r
- \r
- template<typename U> \r
- explicit safe_ptr(const std::shared_ptr<U>& impl, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) : impl_(impl)\r
- {\r
- if(!impl_)\r
- throw std::invalid_argument("impl");\r
- }\r
- \r
- template<typename U> \r
- explicit safe_ptr(std::shared_ptr<U>&& impl, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) : impl_(std::move(impl))\r
- {\r
- if(!impl_)\r
- throw std::invalid_argument("impl");\r
- }\r
-\r
- template<typename U> \r
- explicit safe_ptr(U* impl, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) : impl_(impl)\r
- {\r
- if(!impl_)\r
- throw std::invalid_argument("impl");\r
- }\r
-\r
- template<typename U, typename D> \r
- explicit safe_ptr(U* impl, D dtor, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) : impl_(impl, dtor)\r
- {\r
- if(!impl_)\r
- throw std::invalid_argument("impl");\r
- }\r
-\r
- template<typename U>\r
- typename std::enable_if<std::is_convertible<U*, T*>::value, safe_ptr<T>&>::type\r
- operator=(const safe_ptr<U>& other)\r
- {\r
- safe_ptr<T> temp(other);\r
- temp.swap(*this);\r
- return *this;\r
- }\r
-\r
- template <typename U>\r
- typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, T*>::value, safe_ptr<T>&>::type\r
- operator=(U&& impl)\r
- {\r
- safe_ptr<T> temp(std::forward<T>(impl));\r
- temp.swap(*this);\r
- return *this;\r
- }\r
-\r
- T& operator*() const // noexcept\r
- {\r
- return *impl_.get();\r
- } \r
-\r
- T* operator->() const // noexcept\r
- {\r
- return impl_.get();\r
- } \r
-\r
- T* get() const // noexcept\r
- {\r
- return impl_.get();\r
- } \r
-\r
- bool unique() const { return impl_.unique();} // noexcept\r
-\r
- long use_count() const { return impl_.use_count();} // noexcept\r
- \r
- void swap(safe_ptr& other) { impl_.swap(other.impl_); } // noexcept\r
- \r
- operator const std::shared_ptr<T>&() const { return impl_;} // noexcept\r
-\r
- template<class U>\r
- bool owner_before(const safe_ptr<T>& ptr){ return impl_.owner_before(ptr.impl_); } // noexcept\r
-\r
- template<class U>\r
- bool owner_before(const std::shared_ptr<U>& ptr){ return impl_.owner_before(ptr); } // noexcept\r
- \r
- template<class D, class U> \r
- D* get_deleter(safe_ptr<U> const& ptr) { return impl_.get_deleter(); } // noexcept\r
+ typedef T element_type;\r
+\r
+ safe_ptr(); // will construct new T object using make_safe<T>()\r
+\r
+ safe_ptr(const safe_ptr& other) \r
+ : p_(other.p_)\r
+ {\r
+ }\r
+\r
+ template<typename U>\r
+ safe_ptr(const safe_ptr<U>& other, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) \r
+ : p_(other.p_)\r
+ {\r
+ }\r
+\r
+ safe_ptr(safe_ptr&& other) \r
+ : p_(other.p_)\r
+ {\r
+ }\r
+\r
+ template<typename U>\r
+ safe_ptr(safe_ptr<U>&& other, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) \r
+ : p_(other.p_)\r
+ {\r
+ }\r
+\r
+ template<typename U> \r
+ safe_ptr(U&& v, typename std::enable_if<std::is_convertible<U*, T*>::value, void>::type* = 0)\r
+ : p_(std::make_shared<U>(std::forward<U>(v))) \r
+ {\r
+ }\r
+\r
+ template<typename U, typename D> \r
+ safe_ptr(U&& v, D d, typename std::enable_if<std::is_convertible<U*, T*>::value, void>::type* = 0)\r
+ : p_(new U(std::forward<U>(v)), d) \r
+ {\r
+ }\r
+\r
+ template<typename U> \r
+ explicit safe_ptr(const std::shared_ptr<U>& p, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) \r
+ : p_(p)\r
+ {\r
+ if(!p)\r
+ throw std::invalid_argument("p");\r
+ }\r
+\r
+ template<typename U> \r
+ explicit safe_ptr(std::shared_ptr<U>&& p, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) \r
+ : p_(std::move(p))\r
+ {\r
+ if(!p_)\r
+ throw std::invalid_argument("p");\r
+ }\r
+\r
+ template<typename U> \r
+ explicit safe_ptr(U* p, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) \r
+ : p_(p)\r
+ {\r
+ if(!p)\r
+ throw std::invalid_argument("p");\r
+ }\r
+\r
+ template<typename U, typename D> \r
+ explicit safe_ptr(U* p, D d, typename std::enable_if<std::is_convertible<U*, T*>::value, void*>::type = 0) \r
+ : p_(p, d)\r
+ {\r
+ if(!p)\r
+ throw std::invalid_argument("p");\r
+ }\r
+ \r
+ template<typename U>\r
+ typename std::enable_if<std::is_convertible<U*, T*>::value, safe_ptr&>::type\r
+ operator=(const safe_ptr<U>& other)\r
+ {\r
+ safe_ptr(other).swap(*this);\r
+ return *this;\r
+ }\r
+\r
+ template<typename U>\r
+ typename std::enable_if<std::is_convertible<U*, T*>::value, safe_ptr&>::type\r
+ operator=(safe_ptr<U>&& other)\r
+ {\r
+ safe_ptr<T>(std::move(other)).swap(*this);\r
+ return *this;\r
+ }\r
+\r
+ template <typename U>\r
+ typename std::enable_if<std::is_convertible<U*, T*>::value, safe_ptr&>::type\r
+ operator=(U&& v)\r
+ {\r
+ safe_ptr(std::forward<U>(v)).swap(*this);\r
+ return *this;\r
+ }\r
+\r
+ T& operator*() const \r
+ { \r
+ return *p_.get();\r
+ }\r
+\r
+ T* operator->() const \r
+ { \r
+ return p_.get();\r
+ }\r
+\r
+ T* get() const \r
+ { \r
+ return p_.get();\r
+ }\r
+\r
+ bool unique() const \r
+ { \r
+ return p_.unique();\r
+ }\r
+\r
+ long use_count() const \r
+ {\r
+ return p_.use_count();\r
+ }\r
+\r
+ void swap(safe_ptr& other) \r
+ { \r
+ p_.swap(other.p_); \r
+ } \r
+\r
+ operator std::shared_ptr<T>() const \r
+ { \r
+ return p_;\r
+ }\r
+\r
+ operator std::weak_ptr<T>() const \r
+ { \r
+ return std::weak_ptr<T>(p_);\r
+ }\r
+ \r
+ template<class U>\r
+ bool owner_before(const safe_ptr& ptr)\r
+ { \r
+ return p_.owner_before(ptr.p_); \r
+ }\r
+\r
+ template<class U>\r
+ bool owner_before(const std::shared_ptr<U>& ptr)\r
+ { \r
+ return p_.owner_before(ptr); \r
+ }\r
+\r
+ template<class D, class U> \r
+ D* get_deleter(safe_ptr<U> const& ptr) \r
+ { \r
+ return p_.get_deleter(); \r
+ }\r
+\r
+private: \r
+ std::shared_ptr<T> p_;\r
};\r
\r
template<class T, class U>\r
-bool operator==(const std::shared_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator==(const safe_ptr<T>& a, const safe_ptr<U>& b)\r
+{\r
+ return a.get() == b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator==(const std::shared_ptr<T>& a, const safe_ptr<U>& b)\r
+{\r
+ return a.get() == b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator==(const safe_ptr<T>& a, const std::shared_ptr<U>& b)\r
+{\r
+ return a.get() == b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator!=(const safe_ptr<T>& a, const safe_ptr<U>& b)\r
+{\r
+ return a.get() != b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator!=(const std::shared_ptr<T>& a, const safe_ptr<U>& b)\r
+{\r
+ return a.get() != b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator!=(const safe_ptr<T>& a, const std::shared_ptr<U>& b)\r
{\r
- return a.get() == b.get();\r
+ return a.get() != b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator==(const safe_ptr<T>& a, const std::shared_ptr<U>& b) // noexcept\r
+bool operator<(const safe_ptr<T>& a, const safe_ptr<U>& b)\r
{\r
- return a.get() == b.get();\r
+ return a.get() < b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator==(const safe_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator<(const std::shared_ptr<T>& a, const safe_ptr<U>& b)\r
{\r
- return a.get() == b.get();\r
+ return a.get() < b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator!=(const std::shared_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator<(const safe_ptr<T>& a, const std::shared_ptr<U>& b)\r
{\r
- return a.get() != b.get();\r
+ return a.get() < b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator!=(const safe_ptr<T>& a, const std::shared_ptr<U>& b) // noexcept\r
+bool operator>(const safe_ptr<T>& a, const safe_ptr<U>& b)\r
{\r
- return a.get() != b.get();\r
+ return a.get() > b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator!=(const safe_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator>(const std::shared_ptr<T>& a, const safe_ptr<U>& b)\r
{\r
- return a.get() != b.get();\r
+ return a.get() > b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator<(const safe_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator>(const safe_ptr<T>& a, const std::shared_ptr<U>& b)\r
{\r
- return a.get() < b.get();\r
+ return a.get() > b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator>(const safe_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator>=(const safe_ptr<T>& a, const safe_ptr<U>& b)\r
{\r
- return a.get() > b.get();\r
+ return a.get() >= b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator>=(const safe_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator>=(const std::shared_ptr<T>& a, const safe_ptr<U>& b)\r
{\r
- return a.get() >= b.get();\r
+ return a.get() >= b.get();\r
}\r
\r
template<class T, class U>\r
-bool operator<=(const safe_ptr<T>& a, const safe_ptr<U>& b) // noexcept\r
+bool operator>=(const safe_ptr<T>& a, const std::shared_ptr<U>& b)\r
{\r
- return a.get() <= b.get();\r
+ return a.get() >= b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator<=(const safe_ptr<T>& a, const safe_ptr<U>& b)\r
+{\r
+ return a.get() <= b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator<=(const std::shared_ptr<T>& a, const safe_ptr<U>& b)\r
+{\r
+ return a.get() <= b.get();\r
+}\r
+\r
+template<class T, class U>\r
+bool operator<=(const safe_ptr<T>& a, const std::shared_ptr<U>& b)\r
+{\r
+ return a.get() <= b.get();\r
}\r
\r
template<class E, class T, class U>\r
-std::basic_ostream<E, T>& operator<<(std::basic_ostream<E, T>& out, const safe_ptr<U>& p)\r
+std::basic_ostream<E, T>& operator<<(std::basic_ostream<E, T>& out, const safe_ptr<U>& p)\r
{\r
- return out << p.get();\r
+ return out << p.get();\r
}\r
\r
template<class T> \r
-void swap(safe_ptr<T>& a, safe_ptr<T>& b) // noexcept\r
+void swap(safe_ptr<T>& a, safe_ptr<T>& b)\r
{\r
- a.swap(b);\r
+ a.swap(b);\r
}\r
\r
template<class T> \r
-T* get_pointer(safe_ptr<T> const& p) // noexcept\r
+T* get_pointer(safe_ptr<T> const& p)\r
{\r
- return p.get();\r
+ return p.get();\r
}\r
\r
template <class T, class U>\r
-safe_ptr<T> static_pointer_cast(const safe_ptr<U>& p) // noexcept\r
+safe_ptr<T> static_pointer_cast(const safe_ptr<U>& p)\r
{\r
- return safe_ptr<T>(std::static_pointer_cast<T>(std::shared_ptr<U>(p)));\r
+ return safe_ptr<T>(std::static_pointer_cast<T>(std::shared_ptr<U>(p)));\r
}\r
\r
template <class T, class U>\r
-safe_ptr<T> const_pointer_cast(const safe_ptr<U>& p) // noexcept\r
+safe_ptr<T> const_pointer_cast(const safe_ptr<U>& p)\r
{\r
- return safe_ptr<T>(std::const_pointer_cast<T>(std::shared_ptr<U>(p)));\r
+ return safe_ptr<T>(std::const_pointer_cast<T>(std::shared_ptr<U>(p)));\r
}\r
\r
template <class T, class U>\r
safe_ptr<T> dynamic_pointer_cast(const safe_ptr<U>& p)\r
{\r
- auto temp = std::dynamic_pointer_cast<T>(std::shared_ptr<U>(p));\r
- if(!temp)\r
- throw std::bad_cast();\r
- return safe_ptr<T>(temp);\r
+ auto temp = std::dynamic_pointer_cast<T>(std::shared_ptr<U>(p));\r
+ if(!temp)\r
+ throw std::bad_cast();\r
+ return safe_ptr<T>(std::move(temp));\r
}\r
\r
+//\r
+// enable_safe_this \r
+//\r
+// A safe_ptr version of enable_shared_from_this.\r
+// So that an object may get safe_ptr objects to itself.\r
+//\r
+\r
+template<class T>\r
+class enable_safe_from_this : public std::enable_shared_from_this<T>\r
+{\r
+public:\r
+ safe_ptr<T> safe_from_this() \r
+ {\r
+ return safe_ptr<T>(this->shared_from_this());\r
+ }\r
+\r
+ safe_ptr<T const> safe_from_this() const \r
+ {\r
+ return safe_ptr<T const>(this->shared_from_this());\r
+ }\r
+protected:\r
+ enable_safe_from_this()\r
+ {\r
+ }\r
+ \r
+ enable_safe_from_this(const enable_safe_from_this&)\r
+ {\r
+ }\r
+ \r
+ enable_safe_from_this& operator=(const enable_safe_from_this&)\r
+ { \r
+ return *this;\r
+ }\r
+ \r
+ ~enable_safe_from_this ()\r
+ {\r
+ }\r
+};\r
+\r
+//\r
+// make_safe\r
+//\r
+// safe_ptr equivalents to make_shared\r
+//\r
+\r
template<typename T>\r
-safe_ptr<T> make_safe(const std::shared_ptr<T>& ptr)\r
+safe_ptr<T> make_safe_ptr(const std::shared_ptr<T>& ptr)\r
{\r
return safe_ptr<T>(ptr);\r
}\r
\r
template<typename T>\r
-safe_ptr<T> make_safe(std::shared_ptr<T>&& ptr)\r
+safe_ptr<T> make_safe_ptr(std::shared_ptr<T>&& ptr)\r
{\r
- return safe_ptr<T>(std::move(ptr));\r
+ return safe_ptr<T>(ptr);\r
}\r
\r
template<typename T>\r
safe_ptr<T> make_safe()\r
{\r
- return safe_ptr<T>();\r
+ return safe_ptr<T>(std::make_shared<T>());\r
}\r
\r
template<typename T, typename P0>\r
safe_ptr<T> make_safe(P0&& p0)\r
{\r
- return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0)));\r
+ return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0)));\r
}\r
\r
template<typename T, typename P0, typename P1>\r
safe_ptr<T> make_safe(P0&& p0, P1&& p1)\r
{\r
- return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1)));\r
+ return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1)));\r
}\r
\r
template<typename T, typename P0, typename P1, typename P2>\r
safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2)\r
{\r
- return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2)));\r
+ return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2)));\r
}\r
\r
template<typename T, typename P0, typename P1, typename P2, typename P3>\r
safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3)\r
{\r
- return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3)));\r
+ return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3)));\r
}\r
\r
template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4>\r
safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4)\r
{\r
- return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4)));\r
+ return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4)));\r
}\r
\r
template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>\r
safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5)\r
{\r
- return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4), std::forward<P5>(p5)));\r
+ return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4), std::forward<P5>(p5)));\r
}\r
\r
-}
\ No newline at end of file
+template<typename T>\r
+safe_ptr<T>::safe_ptr() \r
+ : p_(make_safe<T>())\r
+{\r
+} \r
+\r
+} // namespace
\ No newline at end of file
#pragma once\r
\r
#include "../log/log.h"\r
+#include "../exception/exceptions.h"\r
\r
#ifdef _MSC_VER\r
#define _CASPAR_DBG_BREAK _CrtDbgBreak()\r
\r
#define CASPAR_VERIFY_EXPR_STR(str) #str\r
\r
-#define CASPAR_VERIFY(expr) do{if(!(expr)){ CASPAR_LOG(warning) << "Assertion Failed: " << \\r
+#define CASPAR_VERIFY(expr, exception) do{if(!(expr)){ BOOST_THROW_EXCEPTION(exception << msg_info(CASPAR_VERIFY_EXPR_STR(expr)));}}while(0);\r
+\r
+#define CASPAR_ASSERT2(expr) do{if(!(expr)){ CASPAR_LOG(warning) << "Assertion Failed: " << \\r
CASPAR_VERIFY_EXPR_STR(expr) << " " \\r
<< "file:" << __FILE__ << " " \\r
<< "line:" << __LINE__ << " "; \\r
}}while(0);\r
\r
#ifdef _DEBUG\r
-#define CASPAR_ASSERT(expr) CASPAR_VERIFY(expr)\r
+#define CASPAR_ASSERT(expr) CASPAR_ASSERT2(expr)\r
#else\r
#define CASPAR_ASSERT(expr)\r
#endif
\ No newline at end of file
#include <string>\r
#include <vector>\r
\r
+#include <agents.h>\r
+#include <agents_extras.h>\r
+#include <concrt.h>\r
+#include <concrt_extras.h>\r
+#include <ppl.h>\r
+\r
#include <tbb/atomic.h>\r
#include <tbb/concurrent_queue.h>\r
#include <tbb/concurrent_unordered_map.h>\r
throw;\r
}\r
}\r
- return make_safe(buffer);\r
+ return make_safe_ptr(buffer);\r
}\r
\r
safe_ptr<device_buffer> ogl_device::create_device_buffer(size_t width, size_t height, size_t stride)\r
{\r
- CASPAR_VERIFY(stride > 0 && stride < 5);\r
- CASPAR_VERIFY(width > 0 && height > 0);\r
+ CASPAR_VERIFY(stride > 0 && stride < 5, invalid_argument());\r
+ CASPAR_VERIFY(width > 0 && height > 0, invalid_argument());\r
auto& pool = device_pools_[stride-1][((width << 16) & 0xFFFF0000) | (height & 0x0000FFFF)];\r
std::shared_ptr<device_buffer> buffer;\r
if(!pool->items.try_pop(buffer)) \r
}\r
}\r
\r
- return make_safe(buffer);\r
+ return make_safe_ptr(buffer);\r
}\r
\r
safe_ptr<host_buffer> ogl_device::create_host_buffer(size_t size, host_buffer::usage_t usage)\r
{\r
- CASPAR_VERIFY(usage == host_buffer::write_only || usage == host_buffer::read_only);\r
- CASPAR_VERIFY(size > 0);\r
+ CASPAR_VERIFY(usage == host_buffer::write_only || usage == host_buffer::read_only, invalid_argument());\r
+ CASPAR_VERIFY(size > 0, invalid_argument());\r
auto& pool = host_pools_[usage][size];\r
std::shared_ptr<host_buffer> buffer;\r
if(!pool->items.try_pop(buffer)) \r
if(g_shader)\r
{\r
blend_modes = g_blend_modes;\r
- return make_safe(g_shader);\r
+ return make_safe_ptr(g_shader);\r
}\r
\r
try\r
}\r
\r
blend_modes = g_blend_modes;\r
- return make_safe(g_shader);\r
+ return make_safe_ptr(g_shader);\r
}\r
\r
}}
\ No newline at end of file
#include <common/exception/exceptions.h>\r
#include <common/utility/move_on_copy.h>\r
\r
+#include <concrt_extras.h>\r
+\r
namespace caspar { namespace core {\r
\r
std::vector<const producer_factory_t> g_factories;\r
\r
+struct destruction_context\r
+{\r
+ std::shared_ptr<frame_producer> producer;\r
+ Concurrency::event event;\r
+\r
+ destruction_context(std::shared_ptr<frame_producer>&& producer) : producer(producer){}\r
+};\r
+\r
+void __cdecl destroy_producer(LPVOID lpParam)\r
+{\r
+ auto destruction = std::unique_ptr<destruction_context>(static_cast<destruction_context*>(lpParam));\r
+ \r
+ try\r
+ { \r
+ if(destruction->producer.unique())\r
+ {\r
+ Concurrency::scoped_oversubcription_token oversubscribe;\r
+ destruction->producer.reset();\r
+ }\r
+ else\r
+ CASPAR_LOG(warning) << destruction->producer->print() << " Not destroyed asynchronously."; \r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ }\r
+ \r
+ destruction->event.set();\r
+}\r
+\r
+void __cdecl destroy_and_wait_producer(LPVOID lpParam)\r
+{\r
+ try\r
+ {\r
+ auto destruction = static_cast<destruction_context*>(lpParam);\r
+ Concurrency::CurrentScheduler::ScheduleTask(destroy_producer, lpParam);\r
+ if(destruction->event.wait(1000) == Concurrency::COOPERATIVE_WAIT_TIMEOUT)\r
+ CASPAR_LOG(warning) << " Potential destruction deadlock detected. Might leak resources.";\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ }\r
+}\r
+\r
class destroy_producer_proxy : public frame_producer\r
{\r
- safe_ptr<frame_producer> producer_;\r
- executor& destroy_context_;\r
+ std::shared_ptr<frame_producer> producer_;\r
public:\r
- destroy_producer_proxy(executor& destroy_context, const safe_ptr<frame_producer>& producer) \r
- : producer_(producer)\r
- , destroy_context_(destroy_context){}\r
+ destroy_producer_proxy(const std::shared_ptr<frame_producer>& producer) \r
+ : producer_(producer){}\r
\r
~destroy_producer_proxy()\r
{ \r
- if(destroy_context_.size() > 4)\r
- CASPAR_LOG(error) << L" Potential destroyer deadlock.";\r
-\r
- // Hacks to bypass compiler bugs.\r
- auto mov_producer = make_move_on_copy<safe_ptr<frame_producer>>(std::move(producer_));\r
- auto empty_producer = frame_producer::empty();\r
- destroy_context_.begin_invoke([=]\r
- { \r
- //if(!mov_producer.value.unique())\r
- // CASPAR_LOG(debug) << mov_producer.value->print() << L" Not destroyed on safe asynchronous destruction thread.";\r
- //else\r
- // CASPAR_LOG(debug) << mov_producer.value->print() << L" Destroying on safe asynchronous destruction thread.";\r
- \r
- mov_producer.value = empty_producer;\r
- });\r
+ Concurrency::CurrentScheduler::ScheduleTask(destroy_producer, new destruction_context(std::move(producer_)));\r
}\r
\r
virtual safe_ptr<basic_frame> receive(int hints) {return producer_->receive(hints);}\r
virtual int64_t nb_frames() const {return producer_->nb_frames();}\r
};\r
\r
-safe_ptr<core::frame_producer> create_destroy_producer_proxy(executor& destroy_context, const safe_ptr<frame_producer>& producer)\r
+safe_ptr<core::frame_producer> create_destroy_producer_proxy(const safe_ptr<frame_producer>& producer)\r
{\r
- return make_safe<destroy_producer_proxy>(destroy_context, producer);\r
+ return make_safe<destroy_producer_proxy>(producer);\r
}\r
\r
class last_frame_producer : public frame_producer\r
void register_producer_factory(const producer_factory_t& factory); // Not thread-safe.\r
safe_ptr<core::frame_producer> create_producer(const safe_ptr<frame_factory>&, const std::vector<std::wstring>& params);\r
\r
-safe_ptr<core::frame_producer> create_destroy_producer_proxy(executor& destroy_context, const safe_ptr<frame_producer>& producer);\r
+safe_ptr<core::frame_producer> create_destroy_producer_proxy(const safe_ptr<frame_producer>& producer);\r
\r
template<typename T>\r
typename std::decay<T>::type get_param(const std::wstring& name, const std::vector<std::wstring>& params, T fail_value)\r
\r
#include <core/producer/frame/basic_frame.h>\r
\r
-#include <tbb/parallel_invoke.h>\r
+#include <ppl.h>\r
\r
namespace caspar { namespace core { \r
\r
\r
virtual safe_ptr<basic_frame> receive(int hints)\r
{\r
- tbb::parallel_invoke(\r
+ Concurrency::parallel_invoke(\r
[&]\r
{\r
if(fill_ == core::basic_frame::late())\r
\r
#include <boost/foreach.hpp>\r
\r
-#include <tbb/parallel_for_each.h>\r
+#include <ppl.h>\r
\r
#include <map>\r
#include <set>\r
BOOST_FOREACH(auto& layer, layers_) \r
frames[layer.first] = basic_frame::empty(); \r
\r
- tbb::parallel_for_each(layers_.begin(), layers_.end(), [&](std::map<int, layer>::value_type& layer) \r
+ Concurrency::parallel_for_each(layers_.begin(), layers_.end(), [&](std::map<int, layer>::value_type& layer) \r
{\r
frames[layer.first] = layer.second.receive(); \r
});\r
{\r
channel_.execution().invoke([&]\r
{\r
- layers_[index].load(create_destroy_producer_proxy(channel_.destruction(), producer), preview, auto_play_delta);\r
+ layers_[index].load(create_destroy_producer_proxy(producer), preview, auto_play_delta);\r
}, high_priority);\r
}\r
\r
#include <core/producer/frame/basic_frame.h>\r
#include <core/producer/frame/frame_transform.h>\r
\r
-#include <tbb/parallel_invoke.h>\r
+#include <ppl.h>\r
\r
#include <boost/assign.hpp>\r
\r
auto dest = basic_frame::empty();\r
auto source = basic_frame::empty();\r
\r
- tbb::parallel_invoke(\r
+ Concurrency::parallel_invoke(\r
[&]\r
{\r
dest = receive_and_follow(dest_producer_, hints);\r
// Stop context before destroying devices.\r
context_.execution().stop();\r
context_.execution().join();\r
- context_.destruction().stop();\r
- context_.destruction().join();\r
}\r
\r
void tick()\r
video_channel::video_channel(int index, const video_format_desc& format_desc, ogl_device& ogl) : impl_(new implementation(index, format_desc, ogl)){}\r
video_channel::video_channel(video_channel&& other) : impl_(std::move(other.impl_)){}\r
safe_ptr<stage> video_channel::stage() { return impl_->stage_;} \r
-safe_ptr<mixer> video_channel::mixer() { return make_safe(impl_->mixer_);} \r
+safe_ptr<mixer> video_channel::mixer() { return make_safe_ptr(impl_->mixer_);} \r
safe_ptr<output> video_channel::output() { return impl_->output_;} \r
video_format_desc video_channel::get_video_format_desc() const{return impl_->context_.get_format_desc();}\r
void video_channel::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
const int index_;\r
video_format_desc format_desc_;\r
executor execution_;\r
- executor destruction_;\r
ogl_device& ogl_;\r
\r
implementation(int index, ogl_device& ogl, const video_format_desc& format_desc)\r
: index_(index)\r
, format_desc_(format_desc)\r
, execution_(print() + L"/execution")\r
- , destruction_(print() + L"/destruction")\r
, ogl_(ogl)\r
{\r
execution_.set_priority_class(above_normal_priority_class);\r
- destruction_.set_priority_class(below_normal_priority_class);\r
}\r
\r
std::wstring print() const\r
impl_->format_desc_ = format_desc;\r
}\r
\r
-executor& video_channel_context::execution() {return impl_->execution_;}\r
-executor& video_channel_context::destruction() {return impl_->destruction_;}\r
-ogl_device& video_channel_context::ogl() { return impl_->ogl_;}\r
+executor& video_channel_context::execution()\r
+{\r
+ return impl_->execution_;\r
+}\r
+\r
+ogl_device& video_channel_context::ogl()\r
+{\r
+ return impl_->ogl_;\r
+}\r
\r
std::wstring video_channel_context::print() const\r
{\r
video_format_desc get_format_desc();\r
void set_format_desc(const video_format_desc& format_desc);\r
executor& execution();\r
- executor& destruction();\r
ogl_device& ogl();\r
std::wstring print() const;\r
private:\r
\r
std::shared_ptr<core::read_frame> frame; \r
video_frame_buffer_.pop(frame); \r
- schedule_next_video(make_safe(frame)); \r
+ schedule_next_video(make_safe_ptr(frame)); \r
}\r
catch(...)\r
{\r
{\r
std::shared_ptr<core::read_frame> frame;\r
audio_frame_buffer_.pop(frame);\r
- schedule_next_audio(make_safe(frame)); \r
+ schedule_next_audio(make_safe_ptr(frame)); \r
}\r
}\r
catch(...)\r
\r
\r
/* File created by MIDL compiler version 7.00.0555 */\r
-/* at Tue Oct 18 13:46:11 2011\r
+/* at Tue Oct 18 16:33:45 2011\r
*/\r
/* Compiler settings for interop\DeckLinkAPI.idl:\r
Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555 \r
\r
\r
/* File created by MIDL compiler version 7.00.0555 */\r
-/* at Tue Oct 18 13:46:11 2011\r
+/* at Tue Oct 18 16:33:45 2011\r
*/\r
/* Compiler settings for interop\DeckLinkAPI.idl:\r
Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555 \r
#include <core/producer/frame/frame_transform.h>\r
#include <core/producer/frame/frame_factory.h>\r
\r
-#include <tbb/concurrent_queue.h>\r
+#include <agents.h>\r
+#include <agents_extras.h>\r
\r
#include <boost/algorithm/string.hpp>\r
#include <boost/foreach.hpp>\r
\r
namespace caspar { namespace decklink {\r
\r
+typedef std::pair<CComPtr<IDeckLinkVideoInputFrame>, CComPtr<IDeckLinkAudioInputPacket>> frame_packet;\r
+\r
class decklink_producer : boost::noncopyable, public IDeckLinkInputCallback\r
{ \r
- CComPtr<IDeckLink> decklink_;\r
- CComQIPtr<IDeckLinkInput> input_;\r
- \r
- const std::wstring model_name_;\r
- const core::video_format_desc format_desc_;\r
- const size_t device_index_;\r
+ Concurrency::ITarget<frame_packet>& target_;\r
\r
- std::shared_ptr<diagnostics::graph> graph_;\r
- boost::timer tick_timer_;\r
- boost::timer frame_timer_;\r
- \r
- safe_ptr<core::frame_factory> frame_factory_;\r
-\r
- tbb::concurrent_bounded_queue<safe_ptr<core::basic_frame>> frame_buffer_;\r
+ CComPtr<IDeckLink> decklink_;\r
+ CComQIPtr<IDeckLinkInput> input_;\r
+ \r
+ const std::wstring model_name_;\r
+ const core::video_format_desc format_desc_;\r
+ const size_t device_index_;\r
\r
- std::exception_ptr exception_;\r
- ffmpeg::filter filter_;\r
- \r
- ffmpeg::frame_muxer muxer_;\r
+ std::shared_ptr<diagnostics::graph> graph_;\r
+ boost::timer tick_timer_;\r
+ boost::timer frame_timer_;\r
\r
public:\r
- decklink_producer(const core::video_format_desc& format_desc, size_t device_index, const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filter)\r
- : decklink_(get_device(device_index))\r
+ decklink_producer(Concurrency::ITarget<frame_packet>& target, const core::video_format_desc& format_desc, size_t device_index)\r
+ : target_(target)\r
+ , decklink_(get_device(device_index))\r
, input_(decklink_)\r
, model_name_(get_model_name(decklink_))\r
, format_desc_(format_desc)\r
, device_index_(device_index)\r
- , frame_factory_(frame_factory)\r
- , filter_(filter)\r
- , muxer_(ffmpeg::double_rate(filter) ? format_desc.fps * 2.0 : format_desc.fps, frame_factory)\r
- {\r
- frame_buffer_.set_capacity(2);\r
- \r
+ { \r
graph_ = diagnostics::create_graph(boost::bind(&decklink_producer::print, this));\r
graph_->add_guide("tick-time", 0.5);\r
graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); \r
graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));\r
graph_->set_color("output-buffer", diagnostics::color(0.0f, 1.0f, 0.0f));\r
- \r
+\r
auto display_mode = get_display_mode(input_, format_desc_.format, bmdFormat8BitYUV, bmdVideoInputFlagDefault);\r
\r
// NOTE: bmdFormat8BitARGB is currently not supported by any decklink card. (2011-05-08)\r
\r
~decklink_producer()\r
{\r
+ Concurrency::scoped_oversubcription_token oversubscribe;\r
if(input_ != nullptr) \r
{\r
input_->StopStreams();\r
}\r
}\r
\r
+\r
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID*) {return E_NOINTERFACE;}\r
virtual ULONG STDMETHODCALLTYPE AddRef () {return 1;}\r
virtual ULONG STDMETHODCALLTYPE Release () {return 1;}\r
\r
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame* video, IDeckLinkAudioInputPacket* audio)\r
{ \r
- if(!video)\r
- return S_OK;\r
-\r
- try\r
- {\r
- graph_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
- tick_timer_.restart();\r
-\r
- frame_timer_.restart();\r
-\r
- void* bytes = nullptr;\r
- if(FAILED(video->GetBytes(&bytes)) || !bytes)\r
- return S_OK;\r
- \r
- safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free); \r
- avcodec_get_frame_defaults(av_frame.get());\r
- \r
- av_frame->data[0] = reinterpret_cast<uint8_t*>(bytes);\r
- av_frame->linesize[0] = video->GetRowBytes(); \r
- av_frame->format = PIX_FMT_UYVY422;\r
- av_frame->width = video->GetWidth();\r
- av_frame->height = video->GetHeight();\r
- av_frame->interlaced_frame = format_desc_.field_mode != core::field_mode::progressive;\r
- av_frame->top_field_first = format_desc_.field_mode == core::field_mode::upper ? 1 : 0;\r
- \r
- BOOST_FOREACH(auto& av_frame2, filter_.execute(av_frame))\r
- muxer_.push(av_frame2); \r
- \r
- // It is assumed that audio is always equal or ahead of video.\r
- if(audio && SUCCEEDED(audio->GetBytes(&bytes)))\r
- {\r
- auto sample_frame_count = audio->GetSampleFrameCount();\r
- auto audio_data = reinterpret_cast<int32_t*>(bytes);\r
- muxer_.push(std::make_shared<core::audio_buffer>(audio_data, audio_data + sample_frame_count*format_desc_.audio_channels));\r
- }\r
- else\r
- muxer_.push(std::make_shared<core::audio_buffer>(frame_factory_->get_video_format_desc().audio_samples_per_frame, 0));\r
- \r
- muxer_.commit();\r
-\r
- while(!muxer_.empty())\r
- {\r
- if(!frame_buffer_.try_push(muxer_.pop()))\r
- graph_->add_tag("dropped-frame");\r
- }\r
-\r
- graph_->update_value("frame-time", frame_timer_.elapsed()*format_desc_.fps*0.5);\r
-\r
- graph_->set_value("output-buffer", static_cast<float>(frame_buffer_.size())/static_cast<float>(frame_buffer_.capacity())); \r
- }\r
- catch(...)\r
- {\r
- exception_ = std::current_exception();\r
- return E_FAIL;\r
- }\r
-\r
+ if(!Concurrency::asend(target_, frame_packet(CComPtr<IDeckLinkVideoInputFrame>(video), CComPtr<IDeckLinkAudioInputPacket>(audio))))\r
+ graph_->add_tag("dropped-frame");\r
return S_OK;\r
}\r
- \r
- safe_ptr<core::basic_frame> get_frame()\r
- {\r
- if(exception_ != nullptr)\r
- std::rethrow_exception(exception_);\r
-\r
- safe_ptr<core::basic_frame> frame = core::basic_frame::late();\r
- if(!frame_buffer_.try_pop(frame))\r
- graph_->add_tag("late-frame");\r
- graph_->set_value("output-buffer", static_cast<float>(frame_buffer_.size())/static_cast<float>(frame_buffer_.capacity())); \r
- return frame;\r
- }\r
- \r
+ \r
std::wstring print() const\r
{\r
return model_name_ + L" [" + boost::lexical_cast<std::wstring>(device_index_) + L"]";\r
}\r
};\r
\r
-class decklink_producer_proxy : public core::frame_producer\r
+class decklink_producer_proxy : public Concurrency::agent, public core::frame_producer\r
{ \r
- safe_ptr<core::basic_frame> last_frame_;\r
- com_context<decklink_producer> context_;\r
- const int64_t length_;\r
+ std::shared_ptr<Concurrency::bounded_buffer<std::shared_ptr<AVFrame>>> video_frames_;\r
+ std::shared_ptr<Concurrency::bounded_buffer<std::shared_ptr<core::audio_buffer>>> audio_buffers_;\r
+ Concurrency::bounded_buffer<std::shared_ptr<core::basic_frame>> muxed_frames_;\r
+\r
+ const core::video_format_desc format_desc_;\r
+ const size_t device_index_;\r
+\r
+ safe_ptr<core::basic_frame> last_frame_;\r
+ const int64_t length_;\r
+\r
+ ffmpeg::filter filter_;\r
+ \r
+ ffmpeg::frame_muxer2 muxer_;\r
+\r
+ mutable Concurrency::single_assignment<std::wstring> print_;\r
+\r
+ volatile bool is_running_;\r
public:\r
\r
explicit decklink_producer_proxy(const safe_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, size_t device_index, const std::wstring& filter_str, int64_t length)\r
- : context_(L"decklink_producer[" + boost::lexical_cast<std::wstring>(device_index) + L"]")\r
+ : video_frames_(std::make_shared<Concurrency::bounded_buffer<std::shared_ptr<AVFrame>>>(1))\r
+ , audio_buffers_(std::make_shared<Concurrency::bounded_buffer<std::shared_ptr<core::audio_buffer>>>(1))\r
+ , muxed_frames_(1)\r
+ , format_desc_(format_desc)\r
+ , device_index_(device_index)\r
, last_frame_(core::basic_frame::empty())\r
, length_(length)\r
+ , filter_(filter_str)\r
+ , muxer_(video_frames_, audio_buffers_, muxed_frames_, ffmpeg::double_rate(filter_str) ? format_desc.fps * 2.0 : format_desc.fps, frame_factory)\r
+ , is_running_(true)\r
{\r
- context_.reset([&]{return new decklink_producer(format_desc, device_index, frame_factory, filter_str);}); \r
+ agent::start();\r
}\r
\r
~decklink_producer_proxy()\r
{\r
- auto str = print();\r
- context_.reset();\r
- CASPAR_LOG(info) << str << L" Successfully Uninitialized."; \r
+ is_running_ = false;\r
+ agent::wait(this);\r
}\r
\r
virtual safe_ptr<core::basic_frame> receive(int)\r
{\r
- auto frame = context_->get_frame();\r
- if(frame != core::basic_frame::late())\r
- last_frame_ = frame;\r
+ auto frame = core::basic_frame::late();\r
+\r
+ try\r
+ {\r
+ last_frame_ = frame = safe_ptr<core::basic_frame>(Concurrency::receive(muxed_frames_));\r
+ }\r
+ catch(Concurrency::operation_timed_out&)\r
+ { \r
+ //graph_->add_tag("underflow"); \r
+ }\r
+\r
return frame;\r
}\r
\r
\r
std::wstring print() const\r
{\r
- return context_->print();\r
+ return print_.value();\r
+ }\r
+\r
+ virtual void run()\r
+ {\r
+ try\r
+ {\r
+ CoInitializeEx(NULL, COINIT_MULTITHREADED);\r
+ \r
+ Concurrency::bounded_buffer<frame_packet> input_buffer(2);\r
+\r
+ std::unique_ptr<decklink_producer> producer;\r
+ { \r
+ Concurrency::scoped_oversubcription_token oversubscribe;\r
+ producer.reset(new decklink_producer(input_buffer, format_desc_, device_index_));\r
+ }\r
+\r
+ Concurrency::send(print_, producer->print());\r
+\r
+ while(is_running_)\r
+ {\r
+ auto packet = Concurrency::receive(input_buffer);\r
+ auto video = packet.first;\r
+ auto audio = packet.second;\r
+ \r
+ void* bytes = nullptr;\r
+ if(FAILED(video->GetBytes(&bytes)) || !bytes)\r
+ continue;\r
+ \r
+ safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free); \r
+ avcodec_get_frame_defaults(av_frame.get());\r
+ \r
+ av_frame->data[0] = reinterpret_cast<uint8_t*>(bytes);\r
+ av_frame->linesize[0] = video->GetRowBytes(); \r
+ av_frame->format = PIX_FMT_UYVY422;\r
+ av_frame->width = video->GetWidth();\r
+ av_frame->height = video->GetHeight();\r
+ av_frame->interlaced_frame = format_desc_.field_mode != core::field_mode::progressive;\r
+ av_frame->top_field_first = format_desc_.field_mode == core::field_mode::upper ? 1 : 0;\r
+ \r
+ filter_.push(av_frame);\r
+\r
+ while(true)\r
+ {\r
+ auto frame = filter_.poll();\r
+ if(!frame)\r
+ break;\r
+ Concurrency::send<std::shared_ptr<AVFrame>>(*video_frames_, frame);\r
+ }\r
+ \r
+ // It is assumed that audio is always equal or ahead of video.\r
+ if(audio && SUCCEEDED(audio->GetBytes(&bytes)))\r
+ {\r
+ auto sample_frame_count = audio->GetSampleFrameCount();\r
+ auto audio_data = reinterpret_cast<int32_t*>(bytes);\r
+ Concurrency::send(*audio_buffers_, std::make_shared<core::audio_buffer>(audio_data, audio_data + sample_frame_count*format_desc_.audio_channels));\r
+ }\r
+ else\r
+ Concurrency::send(*audio_buffers_, ffmpeg::empty_audio()); \r
+ }\r
+\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ }\r
+\r
+ CoUninitialize();\r
+\r
+ CASPAR_LOG(info) << print() << L" Successfully Uninitialized."; \r
+\r
+ done();\r
}\r
};\r
\r
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">Create</PrecompiledHeader>\r
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>\r
</ClCompile>\r
- <ClCompile Include="tbb_avcodec.cpp" />\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="consumer\ffmpeg_consumer.h" />\r
<ClInclude Include="producer\util.h" />\r
<ClInclude Include="producer\video\video_decoder.h" />\r
<ClInclude Include="StdAfx.h" />\r
- <ClInclude Include="tbb_avcodec.h" />\r
</ItemGroup>\r
<ItemGroup>\r
<ProjectReference Include="..\..\common\common.vcxproj">\r
<ClCompile Include="producer\ffmpeg_producer.cpp">\r
<Filter>source\producer</Filter>\r
</ClCompile>\r
- <ClCompile Include="producer\input.cpp">\r
- <Filter>source\producer</Filter>\r
- </ClCompile>\r
<ClCompile Include="producer\video\video_decoder.cpp">\r
<Filter>source\producer\video</Filter>\r
</ClCompile>\r
<Filter>source\consumer</Filter>\r
</ClCompile>\r
<ClCompile Include="StdAfx.cpp" />\r
- <ClCompile Include="tbb_avcodec.cpp">\r
- <Filter>source</Filter>\r
- </ClCompile>\r
<ClCompile Include="ffmpeg.cpp">\r
<Filter>source</Filter>\r
</ClCompile>\r
<ClCompile Include="producer\audio\audio_resampler.cpp">\r
<Filter>source\producer\audio</Filter>\r
</ClCompile>\r
+ <ClCompile Include="producer\input.cpp">\r
+ <Filter>source\producer</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="producer\ffmpeg_producer.h">\r
<Filter>source\producer</Filter>\r
</ClInclude>\r
- <ClInclude Include="producer\input.h">\r
- <Filter>source\producer</Filter>\r
- </ClInclude>\r
<ClInclude Include="producer\video\video_decoder.h">\r
<Filter>source\producer\video</Filter>\r
</ClInclude>\r
<Filter>source\consumer</Filter>\r
</ClInclude>\r
<ClInclude Include="StdAfx.h" />\r
- <ClInclude Include="tbb_avcodec.h">\r
- <Filter>source</Filter>\r
- </ClInclude>\r
<ClInclude Include="ffmpeg_error.h">\r
<Filter>source</Filter>\r
</ClInclude>\r
<ClInclude Include="producer\audio\audio_resampler.h">\r
<Filter>source\producer\audio</Filter>\r
</ClInclude>\r
+ <ClInclude Include="producer\input.h">\r
+ <Filter>source\producer</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
namespace caspar { namespace ffmpeg {\r
\r
struct ffmpeg_error : virtual caspar_exception{};\r
+struct ffmpeg_stream_not_found : virtual ffmpeg_error{};\r
\r
static std::string av_error_str(int errn)\r
{\r
#define THROW_ON_ERROR_STR_(call) #call\r
#define THROW_ON_ERROR_STR(call) THROW_ON_ERROR_STR_(call)\r
\r
-#define THROW_ON_ERROR2(call, source) \\r
+#define THROW_ON_ERROR3(call, source, exception) \\r
[&]() -> int \\r
{ \\r
int ret = call; \\r
if(ret < 0) \\r
{ \\r
BOOST_THROW_EXCEPTION( \\r
- ffmpeg_error() << \\r
+ exception() << \\r
msg_info(av_error_str(ret)) << \\r
source_info(narrow(source)) << \\r
boost::errinfo_api_function(THROW_ON_ERROR_STR(call)) << \\r
} \\r
return ret; \\r
}();\r
+#define THROW_ON_ERROR2(call, source) THROW_ON_ERROR3(call, source, ffmpeg_error)\r
\r
}}
\ No newline at end of file
std::vector<int8_t, tbb::cache_aligned_allocator<int8_t>> buffer1_;\r
\r
int64_t nb_frames_;\r
+\r
public:\r
explicit implementation(audio_decoder::source_t& source,\r
audio_decoder::target_t& target,\r
, format_desc_(format_desc) \r
, nb_frames_(0)\r
{ \r
- try\r
- {\r
- AVCodec* dec;\r
- index_ = THROW_ON_ERROR2(av_find_best_stream(context.get(), AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0), "[audio_decoder]");\r
+ \r
+ AVCodec* dec;\r
+ index_ = THROW_ON_ERROR3(av_find_best_stream(context.get(), AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0), "[audio_decoder]", ffmpeg_stream_not_found);\r
+ CASPAR_VERIFY(index_ > -1, ffmpeg_error());\r
\r
- THROW_ON_ERROR2(avcodec_open(context->streams[index_]->codec, dec), "[audio_decoder]");\r
+ THROW_ON_ERROR2(avcodec_open(context->streams[index_]->codec, dec), "[audio_decoder]");\r
+ CASPAR_VERIFY(context->streams[index_]->codec, ffmpeg_error());\r
\r
- codec_context_.reset(context->streams[index_]->codec, avcodec_close);\r
+ codec_context_.reset(context->streams[index_]->codec, avcodec_close);\r
\r
- buffer1_.resize(AVCODEC_MAX_AUDIO_FRAME_SIZE*2);\r
-\r
- resampler_.reset(new audio_resampler(format_desc_.audio_channels, codec_context_->channels,\r
- format_desc_.audio_sample_rate, codec_context_->sample_rate,\r
- AV_SAMPLE_FMT_S32, codec_context_->sample_fmt)); \r
- }\r
- catch(...)\r
- {\r
- index_ = THROW_ON_ERROR2(av_find_best_stream(context.get(), AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0), "[audio_decoder]");\r
+ buffer1_.resize(AVCODEC_MAX_AUDIO_FRAME_SIZE*2);\r
\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- CASPAR_LOG(warning) << "[audio_decoder] Failed to open audio-stream. Running without audio."; \r
- }\r
+ resampler_.reset(new audio_resampler(format_desc_.audio_channels, codec_context_->channels,\r
+ format_desc_.audio_sample_rate, codec_context_->sample_rate,\r
+ AV_SAMPLE_FMT_S32, codec_context_->sample_fmt)); \r
+ CASPAR_VERIFY(resampler_, ffmpeg_error());\r
\r
start();\r
}\r
{\r
while(true)\r
{\r
- auto packet = Concurrency::receive(source_, 5000);\r
- if(packet == eof_packet()) \r
+ auto packet = Concurrency::receive(source_, [this](const std::shared_ptr<AVPacket>& packet)\r
+ {\r
+ return packet && packet->stream_index == index_;\r
+ });\r
+\r
+ if(packet == eof_packet(index_)) \r
break;\r
\r
- if(packet == loop_packet()) \r
+ if(packet == loop_packet(index_)) \r
{ \r
- if(codec_context_)\r
- avcodec_flush_buffers(codec_context_.get());\r
+ avcodec_flush_buffers(codec_context_.get());\r
Concurrency::send(target_, loop_audio());\r
} \r
else if(packet->stream_index == index_)\r
{\r
- if(!codec_context_)\r
- Concurrency::send(target_, empty_audio()); \r
- else \r
- Concurrency::send(target_, decode(*packet)); \r
+ Concurrency::send(target_, decode(*packet)); \r
+ Concurrency::wait(0);\r
}\r
}\r
}\r
const auto n_samples = buffer1_.size() / av_get_bytes_per_sample(AV_SAMPLE_FMT_S32);\r
const auto samples = reinterpret_cast<int32_t*>(buffer1_.data());\r
\r
- return std::make_shared<core::audio_buffer>(samples, samples + n_samples);\r
+ return n_samples > 0 ? std::make_shared<core::audio_buffer>(samples, samples + n_samples) : nullptr;\r
}\r
};\r
\r
L" audio_channels:" << input_channels <<\r
L" sample_fmt:" << input_sample_format;\r
\r
- if(resampler)\r
- resampler_.reset(resampler, audio_resample_close);\r
- else\r
- BOOST_THROW_EXCEPTION(caspar_exception());\r
+ CASPAR_VERIFY(resampler, caspar_exception());\r
+\r
+ resampler_.reset(resampler, audio_resample_close);\r
} \r
}\r
\r
#include "util.h"\r
#include "audio/audio_decoder.h"\r
#include "video/video_decoder.h"\r
+#include "../ffmpeg_error.h"\r
\r
#include <common/env.h>\r
#include <common/utility/assert.h>\r
using namespace Concurrency;\r
\r
namespace caspar { namespace ffmpeg {\r
- \r
+ \r
struct ffmpeg_producer : public core::frame_producer\r
{ \r
const std::wstring filename_;\r
const int start_;\r
const bool loop_;\r
const size_t length_;\r
-\r
- Concurrency::bounded_buffer<std::shared_ptr<AVPacket>> pre_video_packets_;\r
- Concurrency::bounded_buffer<std::shared_ptr<AVPacket>> post_video_packets_;\r
- Concurrency::bounded_buffer<std::shared_ptr<AVFrame>> video_frames_;\r
- Concurrency::bounded_buffer<std::shared_ptr<core::audio_buffer>> audio_buffers_;\r
- Concurrency::bounded_buffer<std::shared_ptr<core::basic_frame>> muxed_frames_;\r
+ \r
+ Concurrency::unbounded_buffer<std::shared_ptr<AVPacket>> packet_stream_;\r
+ std::shared_ptr<Concurrency::ISource<std::shared_ptr<AVFrame>>> video_stream_;\r
+ std::shared_ptr<Concurrency::ISource<std::shared_ptr<core::audio_buffer>>> audio_stream_;\r
+ Concurrency::bounded_buffer<std::shared_ptr<core::basic_frame>> frame_stream_;\r
\r
const safe_ptr<diagnostics::graph> graph_;\r
\r
input input_; \r
- video_decoder video_decoder_;\r
- audio_decoder audio_decoder_; \r
- frame_muxer2 muxer_;\r
+ std::unique_ptr<video_decoder> video_decoder_;\r
+ std::unique_ptr<audio_decoder> audio_decoder_; \r
+ std::unique_ptr<frame_muxer2> muxer_;\r
\r
safe_ptr<core::basic_frame> last_frame_;\r
\r
, start_(start)\r
, loop_(loop)\r
, length_(length)\r
- , pre_video_packets_(25)\r
- , post_video_packets_(25)\r
- , video_frames_(2)\r
- , audio_buffers_(2)\r
- , muxed_frames_(2)\r
+ , frame_stream_(2)\r
, graph_(diagnostics::create_graph([this]{return print();}, false))\r
- , input_(pre_video_packets_, graph_, filename_, loop, start, length)\r
- , video_decoder_(pre_video_packets_, post_video_packets_, video_frames_, input_.context(), frame_factory->get_video_format_desc().fps, filter)\r
- , audio_decoder_(post_video_packets_, audio_buffers_, input_.context(), frame_factory->get_video_format_desc())\r
- , muxer_(video_frames_, audio_buffers_, muxed_frames_, video_decoder_.fps(), frame_factory)\r
+ , input_(packet_stream_, graph_, filename_, loop, start, length)\r
, last_frame_(core::basic_frame::empty())\r
{\r
+ try\r
+ {\r
+ auto target = std::make_shared<Concurrency::bounded_buffer<std::shared_ptr<AVFrame>>>(2);\r
+ video_decoder_.reset(new video_decoder(packet_stream_, *target, input_.context(), frame_factory->get_video_format_desc().fps));\r
+ video_stream_ = target;\r
+ }\r
+ catch(ffmpeg_stream_not_found&)\r
+ {\r
+ CASPAR_LOG(warning) << "No video-stream found. Running without video."; \r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ CASPAR_LOG(warning) << "Failed to open video-stream. Running without video."; \r
+ }\r
+\r
+ try\r
+ {\r
+ auto target = std::make_shared<Concurrency::bounded_buffer<std::shared_ptr<core::audio_buffer>>>(25);\r
+ audio_decoder_.reset(new audio_decoder(packet_stream_, *target, input_.context(), frame_factory->get_video_format_desc()));\r
+ audio_stream_ = target;\r
+ }\r
+ catch(ffmpeg_stream_not_found&)\r
+ {\r
+ CASPAR_LOG(warning) << "No audio-stream found. Running without video."; \r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ CASPAR_LOG(warning) << "Failed to open audio-stream. Running without audio."; \r
+ }\r
+\r
+ CASPAR_VERIFY(video_decoder_ || audio_decoder_, ffmpeg_error());\r
+\r
+ muxer_.reset(new frame_muxer2(video_stream_, audio_stream_, frame_stream_, video_decoder_ ? video_decoder_->fps() : frame_factory->get_video_format_desc().fps, frame_factory));\r
+ \r
graph_->set_color("underflow", diagnostics::color(0.6f, 0.3f, 0.9f)); \r
graph_->start();\r
+\r
+ input_.start();\r
}\r
\r
~ffmpeg_producer()\r
{\r
- input_.stop();\r
- while(Concurrency::receive(muxed_frames_) != core::basic_frame::eof())\r
+ input_.stop(); \r
+ while(Concurrency::receive(frame_stream_) != core::basic_frame::eof())\r
{\r
}\r
}\r
\r
try\r
{ \r
- frame = last_frame_ = safe_ptr<core::basic_frame>(Concurrency::receive(muxed_frames_, 8));\r
+ frame = last_frame_ = safe_ptr<core::basic_frame>(Concurrency::receive(frame_stream_, 10));\r
}\r
catch(Concurrency::operation_timed_out&)\r
{ \r
int64_t nb_frames = input_.nb_frames();\r
if(input_.nb_loops() < 1) // input still hasn't counted all frames\r
{\r
- int64_t video_nb_frames = video_decoder_.nb_frames();\r
- int64_t audio_nb_frames = audio_decoder_.nb_frames();\r
+ int64_t video_nb_frames = video_decoder_->nb_frames();\r
+ int64_t audio_nb_frames = audio_decoder_->nb_frames();\r
\r
nb_frames = std::min(static_cast<int64_t>(length_), std::max(nb_frames, std::max(video_nb_frames, audio_nb_frames)));\r
}\r
\r
- nb_frames = muxer_.calc_nb_frames(nb_frames);\r
+ nb_frames = muxer_->calc_nb_frames(nb_frames);\r
\r
// TODO: Might need to scale nb_frames av frame_muxer transformations.\r
\r
\r
virtual std::wstring print() const\r
{\r
- return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"|" \r
- + boost::lexical_cast<std::wstring>(video_decoder_.width()) + L"x" + boost::lexical_cast<std::wstring>(video_decoder_.height())\r
- + (video_decoder_.is_progressive() ? L"p" : L"i") + boost::lexical_cast<std::wstring>(video_decoder_.is_progressive() ? video_decoder_.fps() : 2.0 * video_decoder_.fps())\r
- + L"]";\r
+ if(video_decoder_)\r
+ {\r
+ return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"|" \r
+ + boost::lexical_cast<std::wstring>(video_decoder_->width()) + L"x" + boost::lexical_cast<std::wstring>(video_decoder_->height())\r
+ + (video_decoder_->is_progressive() ? L"p" : L"i") + boost::lexical_cast<std::wstring>(video_decoder_->is_progressive() ? video_decoder_->fps() : 2.0 * video_decoder_->fps())\r
+ + L"]";\r
+ }\r
+ \r
+ return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"]";\r
}\r
};\r
\r
AVFilterContext* buffersrc_ctx_;\r
std::shared_ptr<void> parallel_yadif_ctx_;\r
std::vector<PixelFormat> pix_fmts_;\r
+ std::queue<std::shared_ptr<AVFrame>> bypass_;\r
\r
implementation(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) \r
: filters_(narrow(filters))\r
std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
}\r
\r
- std::vector<safe_ptr<AVFrame>> execute(const std::shared_ptr<AVFrame>& frame)\r
- {\r
+ void push(const std::shared_ptr<AVFrame>& frame)\r
+ { \r
if(!frame)\r
- return std::vector<safe_ptr<AVFrame>>();\r
+ return;\r
\r
if(filters_.empty())\r
- return boost::assign::list_of(frame);\r
-\r
- push(frame);\r
- return poll();\r
- }\r
+ {\r
+ bypass_.push(frame);\r
+ return;\r
+ }\r
\r
- void push(const std::shared_ptr<AVFrame>& frame)\r
- { \r
if(!graph_)\r
{\r
graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
parallel_yadif_ctx_ = make_parallel_yadif(graph_->filters[n]);\r
}\r
}\r
- \r
+ \r
THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]");\r
}\r
\r
- std::vector<safe_ptr<AVFrame>> poll()\r
+ std::shared_ptr<AVFrame> poll()\r
{\r
- std::vector<safe_ptr<AVFrame>> result;\r
+ if(filters_.empty())\r
+ {\r
+ if(bypass_.empty())\r
+ return nullptr;\r
+ auto frame = bypass_.front();\r
+ bypass_.pop();\r
+ return frame;\r
+ }\r
\r
if(!graph_)\r
- return result;\r
+ return nullptr;\r
\r
- while (avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
+ if(avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
{\r
AVFilterBufferRef *picref;\r
THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]");\r
frame->pict_type = picref->video->pict_type;\r
frame->sample_aspect_ratio = picref->video->sample_aspect_ratio;\r
\r
- result.push_back(frame);\r
+ return frame;\r
}\r
}\r
\r
- return result;\r
+ return nullptr;\r
}\r
};\r
\r
filter::filter(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) : impl_(new implementation(filters, pix_fmts)){}\r
filter::filter(filter&& other) : impl_(std::move(other.impl_)){}\r
filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}\r
-std::vector<safe_ptr<AVFrame>> filter::execute(const std::shared_ptr<AVFrame>& frame) {return impl_->execute(frame);}\r
+void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}\r
+std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}\r
\r
}}
\ No newline at end of file
filter(filter&& other);\r
filter& operator=(filter&& other);\r
\r
- std::vector<safe_ptr<AVFrame>> execute(const std::shared_ptr<AVFrame>& frame);\r
+ void push(const std::shared_ptr<AVFrame>& frame);\r
+ std::shared_ptr<AVFrame> poll();\r
\r
private:\r
struct implementation;\r
return display_mode::invalid;\r
}\r
\r
-struct frame_muxer::implementation : boost::noncopyable\r
-{ \r
- std::deque<std::queue<safe_ptr<write_frame>>> video_streams_;\r
- std::deque<core::audio_buffer> audio_streams_;\r
- std::deque<safe_ptr<basic_frame>> frame_buffer_;\r
- display_mode::type display_mode_;\r
- const double in_fps_;\r
- const video_format_desc format_desc_;\r
- bool auto_transcode_;\r
-\r
- size_t audio_sample_count_;\r
- size_t video_frame_count_;\r
- \r
- size_t processed_audio_sample_count_;\r
- size_t processed_video_frame_count_;\r
-\r
- filter filter_;\r
- safe_ptr<core::frame_factory> frame_factory_;\r
- \r
- implementation(double in_fps, const safe_ptr<core::frame_factory>& frame_factory)\r
- : video_streams_(1)\r
- , audio_streams_(1)\r
- , display_mode_(display_mode::invalid)\r
- , in_fps_(in_fps)\r
- , format_desc_(frame_factory->get_video_format_desc())\r
- , auto_transcode_(env::properties().get("configuration.producers.auto-transcode", false))\r
- , audio_sample_count_(0)\r
- , video_frame_count_(0)\r
- , frame_factory_(frame_factory)\r
- {\r
- }\r
-\r
- void push(const std::shared_ptr<AVFrame>& video_frame, int hints)\r
- { \r
- if(!video_frame)\r
- { \r
- CASPAR_LOG(debug) << L"video-frame-count: " << static_cast<float>(video_frame_count_);\r
- video_frame_count_ = 0;\r
- video_streams_.push_back(std::queue<safe_ptr<write_frame>>());\r
- return;\r
- }\r
-\r
- if(video_frame->data[0] == nullptr)\r
- {\r
- video_streams_.back().push(make_safe<core::write_frame>(this));\r
- ++video_frame_count_;\r
- display_mode_ = display_mode::simple;\r
- return;\r
- }\r
-\r
- if(display_mode_ == display_mode::invalid)\r
- {\r
- if(auto_transcode_)\r
- {\r
- auto in_mode = get_mode(*video_frame);\r
- display_mode_ = get_display_mode(in_mode, in_fps_, format_desc_.field_mode, format_desc_.fps);\r
- \r
- if(display_mode_ == display_mode::simple && in_mode != core::field_mode::progressive && format_desc_.field_mode != core::field_mode::progressive && video_frame->height != static_cast<int>(format_desc_.height))\r
- display_mode_ = display_mode::deinterlace_bob_reinterlace; // The frame will most likely be scaled, we need to deinterlace->reinterlace \r
- \r
- if(display_mode_ == display_mode::deinterlace)\r
- filter_ = filter(L"YADIF=0:-1");\r
- else if(display_mode_ == display_mode::deinterlace_bob || display_mode_ == display_mode::deinterlace_bob_reinterlace)\r
- filter_ = filter(L"YADIF=1:-1");\r
- }\r
- else\r
- display_mode_ = display_mode::simple;\r
-\r
- if(display_mode_ == display_mode::invalid)\r
- {\r
- CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";\r
- display_mode_ = display_mode::simple;\r
- }\r
-\r
- CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode_);\r
- }\r
-\r
- \r
- if(hints & core::frame_producer::ALPHA_HINT)\r
- video_frame->format = make_alpha_format(video_frame->format);\r
- \r
- auto format = video_frame->format;\r
- if(video_frame->format == CASPAR_PIX_FMT_LUMA) // CASPAR_PIX_FMT_LUMA is not valid for filter, change it to GRAY8\r
- video_frame->format = PIX_FMT_GRAY8;\r
-\r
- BOOST_FOREACH(auto& av_frame, filter_.execute(video_frame))\r
- {\r
- av_frame->format = format;\r
-\r
- auto frame = make_write_frame(this, av_frame, frame_factory_, hints);\r
-\r
- // Fix field-order if needed\r
- if(frame->get_type() == core::field_mode::lower && format_desc_.field_mode == core::field_mode::upper)\r
- frame->get_frame_transform().fill_translation[1] += 1.0/static_cast<double>(format_desc_.height);\r
- else if(frame->get_type() == core::field_mode::upper && format_desc_.field_mode == core::field_mode::lower)\r
- frame->get_frame_transform().fill_translation[1] -= 1.0/static_cast<double>(format_desc_.height);\r
-\r
- video_streams_.back().push(frame);\r
- ++video_frame_count_;\r
- }\r
-\r
- if(video_streams_.back().size() > 8)\r
- BOOST_THROW_EXCEPTION(invalid_operation() << source_info("frame_muxer") << msg_info("video-stream overflow. This can be caused by incorrect frame-rate. Check clip meta-data."));\r
- }\r
-\r
- void push(const std::shared_ptr<core::audio_buffer>& audio_samples)\r
- {\r
- if(!audio_samples) \r
- {\r
- CASPAR_LOG(debug) << L"audio-chunk-count: " << audio_sample_count_/format_desc_.audio_samples_per_frame;\r
- audio_streams_.push_back(core::audio_buffer());\r
- audio_sample_count_ = 0;\r
- return;\r
- }\r
-\r
- audio_sample_count_ += audio_samples->size();\r
-\r
- boost::range::push_back(audio_streams_.back(), *audio_samples);\r
-\r
- if(audio_streams_.back().size() > 8*format_desc_.audio_samples_per_frame)\r
- BOOST_THROW_EXCEPTION(invalid_operation() << source_info("frame_muxer") << msg_info("audio-stream overflow. This can be caused by incorrect frame-rate. Check clip meta-data."));\r
- }\r
-\r
- safe_ptr<basic_frame> pop()\r
- { \r
- auto frame = frame_buffer_.front();\r
- frame_buffer_.pop_front(); \r
- return frame;\r
- }\r
-\r
- size_t size() const\r
- {\r
- return frame_buffer_.size();\r
- }\r
-\r
- safe_ptr<core::write_frame> pop_video()\r
- {\r
- auto frame = video_streams_.front().front();\r
- video_streams_.front().pop();\r
- \r
- return frame;\r
- }\r
-\r
- core::audio_buffer pop_audio()\r
- {\r
- CASPAR_VERIFY(audio_streams_.front().size() >= format_desc_.audio_samples_per_frame);\r
-\r
- auto begin = audio_streams_.front().begin();\r
- auto end = begin + format_desc_.audio_samples_per_frame;\r
-\r
- auto samples = core::audio_buffer(begin, end);\r
- audio_streams_.front().erase(begin, end);\r
-\r
- return samples;\r
- }\r
- \r
- bool video_ready() const\r
- { \r
- return video_streams_.size() > 1 || (video_streams_.size() >= audio_streams_.size() && video_ready2());\r
- }\r
- \r
- bool audio_ready() const\r
- {\r
- return audio_streams_.size() > 1 || (audio_streams_.size() >= video_streams_.size() && audio_ready2());\r
- }\r
-\r
- bool video_ready2() const\r
- { \r
- switch(display_mode_)\r
- {\r
- case display_mode::deinterlace_bob_reinterlace: \r
- case display_mode::interlace: \r
- return video_streams_.front().size() >= 2;\r
- default: \r
- return !video_streams_.front().empty();\r
- }\r
- }\r
- \r
- bool audio_ready2() const\r
- {\r
- switch(display_mode_)\r
- {\r
- case display_mode::duplicate: \r
- return audio_streams_.front().size()/2 >= format_desc_.audio_samples_per_frame;\r
- default: \r
- return audio_streams_.front().size() >= format_desc_.audio_samples_per_frame;\r
- }\r
- }\r
- \r
- void commit()\r
- {\r
- if(video_streams_.size() > 1 && audio_streams_.size() > 1 && (!video_ready2() || !audio_ready2()))\r
- {\r
- if(!video_streams_.front().empty() || !audio_streams_.front().empty())\r
- CASPAR_LOG(debug) << "Truncating: " << video_streams_.front().size() << L" video-frames, " << audio_streams_.front().size() << L" audio-samples.";\r
-\r
- video_streams_.pop_front();\r
- audio_streams_.pop_front();\r
- }\r
-\r
- if(!video_ready2() || !audio_ready2())\r
- return;\r
- \r
- switch(display_mode_)\r
- {\r
- case display_mode::simple: return simple(frame_buffer_);\r
- case display_mode::duplicate: return duplicate(frame_buffer_);\r
- case display_mode::half: return half(frame_buffer_);\r
- case display_mode::interlace: return interlace(frame_buffer_);\r
- case display_mode::deinterlace_bob: return simple(frame_buffer_);\r
- case display_mode::deinterlace_bob_reinterlace: return interlace(frame_buffer_);\r
- case display_mode::deinterlace: return simple(frame_buffer_);\r
- default: BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));\r
- }\r
- }\r
- \r
- void simple(std::deque<safe_ptr<basic_frame>>& dest)\r
- { \r
- auto frame1 = pop_video();\r
- frame1->audio_data() = pop_audio();\r
-\r
- dest.push_back(frame1); \r
- }\r
-\r
- void duplicate(std::deque<safe_ptr<basic_frame>>& dest)\r
- { \r
- auto frame = pop_video();\r
-\r
- auto frame1 = make_safe<core::write_frame>(*frame); // make a copy\r
- frame1->audio_data() = pop_audio();\r
-\r
- auto frame2 = frame;\r
- frame2->audio_data() = pop_audio();\r
-\r
- dest.push_back(frame1);\r
- dest.push_back(frame2);\r
- }\r
-\r
- void half(std::deque<safe_ptr<basic_frame>>& dest)\r
- { \r
- auto frame1 = pop_video();\r
- frame1->audio_data() = pop_audio();\r
- \r
- video_streams_.front().pop(); // Throw away\r
-\r
- dest.push_back(frame1);\r
- }\r
- \r
- void interlace(std::deque<safe_ptr<basic_frame>>& dest)\r
- { \r
- auto frame1 = pop_video();\r
- frame1->audio_data() = pop_audio();\r
- \r
- auto frame2 = pop_video();\r
-\r
- dest.push_back(core::basic_frame::interlace(frame1, frame2, format_desc_.field_mode)); \r
- }\r
- \r
- int64_t calc_nb_frames(int64_t nb_frames) const\r
- {\r
- switch(display_mode_)\r
- {\r
- case display_mode::interlace:\r
- case display_mode::half:\r
- return nb_frames/2;\r
- case display_mode::duplicate:\r
- case display_mode::deinterlace_bob:\r
- return nb_frames*2;\r
- default:\r
- return nb_frames;\r
- }\r
- }\r
-};\r
-\r
-frame_muxer::frame_muxer(double in_fps, const safe_ptr<core::frame_factory>& frame_factory)\r
- : impl_(new implementation(in_fps, frame_factory)){}\r
-void frame_muxer::push(const std::shared_ptr<AVFrame>& video_frame, int hints){impl_->push(video_frame, hints);}\r
-void frame_muxer::push(const std::shared_ptr<core::audio_buffer>& audio_samples){return impl_->push(audio_samples);}\r
-void frame_muxer::commit(){impl_->commit();}\r
-safe_ptr<basic_frame> frame_muxer::pop(){return impl_->pop();}\r
-size_t frame_muxer::size() const {return impl_->size();}\r
-bool frame_muxer::empty() const {return impl_->size() == 0;}\r
-bool frame_muxer::video_ready() const{return impl_->video_ready();}\r
-bool frame_muxer::audio_ready() const{return impl_->audio_ready();}\r
-int64_t frame_muxer::calc_nb_frames(int64_t nb_frames) const {return impl_->calc_nb_frames(nb_frames);}\r
-\r
-\r
struct frame_muxer2::implementation : public Concurrency::agent, boost::noncopyable\r
{ \r
- frame_muxer2::video_source_t& video_source_;\r
- frame_muxer2::audio_source_t& audio_source_;\r
+ const std::shared_ptr<frame_muxer2::video_source_t>& video_source_;\r
+ const std::shared_ptr<frame_muxer2::audio_source_t>& audio_source_;\r
frame_muxer2::target_t& target_;\r
\r
std::deque<std::queue<safe_ptr<write_frame>>> video_streams_;\r
filter filter_;\r
safe_ptr<core::frame_factory> frame_factory_;\r
\r
- implementation(frame_muxer2::video_source_t& video_source,\r
- frame_muxer2::audio_source_t& audio_source,\r
+ implementation(const std::shared_ptr<frame_muxer2::video_source_t>& video_source,\r
+ const std::shared_ptr<frame_muxer2::audio_source_t>& audio_source,\r
frame_muxer2::target_t& target,\r
double in_fps, \r
const safe_ptr<core::frame_factory>& frame_factory)\r
{\r
while(!video_ready())\r
{\r
- auto video = Concurrency::receive(video_source_, 5000);\r
- if(video == eof_video())\r
- break;\r
- push(video, 0); \r
+ if(video_source_)\r
+ {\r
+ auto video = Concurrency::receive(*video_source_);\r
+ if(video == eof_video())\r
+ break;\r
+ push(video, 0); \r
+ }\r
+ else\r
+ push(empty_video(), 0); \r
}\r
},\r
[&]\r
{\r
while(!audio_ready())\r
{\r
- auto audio = Concurrency::receive(audio_source_, 5000);\r
- if(audio == eof_audio())\r
- break;\r
- push(audio); \r
+ if(audio_source_)\r
+ {\r
+ auto audio = Concurrency::receive(*audio_source_);\r
+ if(audio == eof_audio())\r
+ break;\r
+ push(audio); \r
+ }\r
+ else\r
+ push(empty_audio()); \r
} \r
});\r
\r
- if(!video_ready() || !audio_ready()) \r
+ if((video_source_ && !video_ready()) || (audio_source_ && !audio_ready())) \r
break; \r
\r
commit();\r
\r
void push(const std::shared_ptr<AVFrame>& video_frame, int hints)\r
{ \r
+ if(!video_frame)\r
+ return;\r
+\r
if(video_frame == loop_video())\r
{ \r
CASPAR_LOG(debug) << L"video-frame-count: " << static_cast<float>(video_frame_count_);\r
if(video_frame->format == CASPAR_PIX_FMT_LUMA) // CASPAR_PIX_FMT_LUMA is not valid for filter, change it to GRAY8\r
video_frame->format = PIX_FMT_GRAY8;\r
\r
- BOOST_FOREACH(auto& av_frame, filter_.execute(video_frame))\r
+ filter_.push(video_frame);\r
+\r
+ while(true)\r
{\r
+ auto av_frame = filter_.poll();\r
+ if(!av_frame)\r
+ break;\r
+ \r
av_frame->format = format;\r
\r
- auto frame = make_write_frame(this, av_frame, frame_factory_, hints);\r
+ auto frame = make_write_frame(this, make_safe_ptr(av_frame), frame_factory_, hints);\r
\r
// Fix field-order if needed\r
if(frame->get_type() == core::field_mode::lower && format_desc_.field_mode == core::field_mode::upper)\r
\r
void push(std::shared_ptr<core::audio_buffer> audio_samples)\r
{\r
+ if(!audio_samples)\r
+ return;\r
+\r
if(audio_samples == loop_audio()) \r
{\r
CASPAR_LOG(debug) << L"audio-chunk-count: " << audio_sample_count_/format_desc_.audio_samples_per_frame;\r
\r
core::audio_buffer pop_audio()\r
{\r
- CASPAR_VERIFY(audio_streams_.front().size() >= format_desc_.audio_samples_per_frame);\r
+ CASPAR_ASSERT(audio_streams_.front().size() >= format_desc_.audio_samples_per_frame);\r
\r
auto begin = audio_streams_.front().begin();\r
auto end = begin + format_desc_.audio_samples_per_frame;\r
}\r
};\r
\r
-frame_muxer2::frame_muxer2(video_source_t& video_source, \r
- audio_source_t& audio_source,\r
+frame_muxer2::frame_muxer2(const std::shared_ptr<video_source_t>& video_source, \r
+ const std::shared_ptr<audio_source_t>& audio_source,\r
target_t& target,\r
double in_fps, \r
const safe_ptr<core::frame_factory>& frame_factory)\r
\r
namespace ffmpeg {\r
\r
-class frame_muxer : boost::noncopyable\r
-{\r
-public:\r
- frame_muxer(double in_fps, const safe_ptr<core::frame_factory>& frame_factory);\r
- \r
- void push(const std::shared_ptr<AVFrame>& video_frame, int hints = 0);\r
- void push(const std::shared_ptr<core::audio_buffer>& audio_samples);\r
- \r
- void commit();\r
-\r
- bool video_ready() const;\r
- bool audio_ready() const;\r
-\r
- size_t size() const;\r
- bool empty() const;\r
-\r
- int64_t calc_nb_frames(int64_t nb_frames) const;\r
-\r
- safe_ptr<core::basic_frame> pop();\r
-private:\r
- struct implementation;\r
- safe_ptr<implementation> impl_;\r
-};\r
-\r
class frame_muxer2 : boost::noncopyable\r
{\r
public:\r
typedef Concurrency::ISource<std::shared_ptr<core::audio_buffer>> audio_source_t;\r
typedef Concurrency::ITarget<std::shared_ptr<core::basic_frame>> target_t;\r
\r
- frame_muxer2(video_source_t& video_source,\r
- audio_source_t& audio_source, \r
+ frame_muxer2(const std::shared_ptr<video_source_t>& video_source,\r
+ const std::shared_ptr<audio_source_t>& audio_source, \r
target_t& target,\r
double in_fps, \r
const safe_ptr<core::frame_factory>& frame_factory);\r
#include "input.h"\r
#include "util.h"\r
#include "../ffmpeg_error.h"\r
-#include "../tbb_avcodec.h"\r
\r
#include <core/video_format.h>\r
\r
\r
namespace caspar { namespace ffmpeg {\r
\r
-static const size_t MAX_BUFFER_COUNT = 100;\r
-static const size_t MIN_BUFFER_COUNT = 4;\r
-static const size_t MAX_BUFFER_SIZE = 16 * 1000000;\r
+static const size_t MAX_BUFFER_COUNT = 16;\r
\r
struct input::implementation : public Concurrency::agent, boost::noncopyable\r
{ \r
\r
tbb::atomic<size_t> nb_frames_;\r
tbb::atomic<size_t> nb_loops_;\r
-\r
- std::deque<std::shared_ptr<AVPacket>> buffer_;\r
- size_t buffer_size_;\r
+ \r
+ tbb::atomic<size_t> packets_count_;\r
\r
bool eof_;\r
bool stop_;\r
\r
+ Concurrency::unbounded_buffer<int> tickets_;\r
+\r
+ boost::iterator_range<AVStream**> streams_;\r
+\r
public:\r
explicit implementation(input::target_t& target,\r
const safe_ptr<diagnostics::graph>& graph, \r
, start_(start)\r
, length_(length)\r
, frame_number_(0)\r
- , buffer_size_(0)\r
, eof_(false)\r
, stop_(false)\r
{ \r
+ packets_count_ = 0;\r
nb_frames_ = 0;\r
nb_loops_ = 0;\r
\r
AVFormatContext* weak_format_context_ = nullptr;\r
THROW_ON_ERROR2(avformat_open_input(&weak_format_context_, narrow(filename).c_str(), nullptr, nullptr), print());\r
+ CASPAR_VERIFY(weak_format_context_, ffmpeg_error());\r
\r
format_context_.reset(weak_format_context_, av_close_input_file);\r
-\r
+ \r
av_dump_format(weak_format_context_, 0, narrow(filename).c_str(), 0);\r
\r
THROW_ON_ERROR2(avformat_find_stream_info(format_context_.get(), nullptr), print());\r
\r
+ streams_ = boost::make_iterator_range(format_context_->streams, format_context_->streams + format_context_->nb_streams);\r
default_stream_index_ = THROW_ON_ERROR2(av_find_default_stream_index(format_context_.get()), print());\r
\r
+ for(int n = 0; n < MAX_BUFFER_COUNT; ++n)\r
+ tickets_.enqueue(0);\r
+\r
if(start_ > 0) \r
seek_frame(start_);\r
- \r
- for(int n = 0; n < 16; ++n)\r
- read_next_packet();\r
- \r
+ \r
graph_->set_color("seek", diagnostics::color(1.0f, 0.5f, 0.0f));\r
graph_->set_color("buffer-count", diagnostics::color(0.7f, 0.4f, 0.4f));\r
- graph_->set_color("buffer-size", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
+ graph_->set_color("buffer-size", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
\r
- agent::start();\r
+ CASPAR_VERIFY(default_stream_index_ > -1, ffmpeg_error());\r
}\r
\r
~implementation()\r
while(!stop_)\r
{\r
read_next_packet();\r
-\r
- if(buffer_.empty())\r
- break;\r
-\r
- if(buffer_.size() < MIN_BUFFER_COUNT || (buffer_.size() < MAX_BUFFER_COUNT && buffer_size_ < MAX_BUFFER_SIZE))\r
- {\r
- if(Concurrency::asend(target_, buffer_.front()))\r
- buffer_.pop_front();\r
- Concurrency::wait(2);\r
- }\r
- else\r
- {\r
- Concurrency::send(target_, buffer_.front());\r
- buffer_.pop_front();\r
- } \r
-\r
- graph_->update_value("buffer-size", (static_cast<double>(buffer_size_)+0.001)/MAX_BUFFER_SIZE);\r
- graph_->update_value("buffer-count", (static_cast<double>(buffer_.size()+0.001)/MAX_BUFFER_COUNT)); \r
+ \r
+ graph_->update_value("buffer-count", (static_cast<double>(packets_count_)+0.001)/MAX_BUFFER_COUNT); \r
} \r
}\r
catch(...)\r
CASPAR_LOG_CURRENT_EXCEPTION();\r
} \r
\r
- Concurrency::send(target_, eof_packet()); \r
+ std::for_each(streams_.begin(), streams_.end(), [this](const AVStream* stream)\r
+ {\r
+ Concurrency::send(target_, eof_packet(stream->index)); \r
+ });\r
\r
done();\r
}\r
\r
int ret = 0;\r
\r
- auto read_packet = create_packet();\r
+ auto packet = create_packet();\r
\r
{\r
Concurrency::scoped_oversubcription_token oversubscribe;\r
- ret = av_read_frame(format_context_.get(), read_packet.get()); // read_packet is only valid until next call of av_read_frame. Use av_dup_packet to extend its life. \r
+ ret = av_read_frame(format_context_.get(), packet.get()); // packet is only valid until next call of av_read_frame. Use av_dup_packet to extend its life. \r
}\r
\r
if(is_eof(ret)) \r
{ \r
THROW_ON_ERROR(ret, print(), "av_read_frame");\r
\r
- if(read_packet->stream_index == default_stream_index_)\r
+ if(packet->stream_index == default_stream_index_)\r
{\r
if(nb_loops_ == 0)\r
++nb_frames_;\r
++frame_number_;\r
}\r
\r
- THROW_ON_ERROR2(av_dup_packet(read_packet.get()), print());\r
+ THROW_ON_ERROR2(av_dup_packet(packet.get()), print());\r
\r
// Make sure that the packet is correctly deallocated even if size and data is modified during decoding.\r
- auto size = read_packet->size;\r
- auto data = read_packet->data;\r
+ auto size = packet->size;\r
+ auto data = packet->data; \r
\r
- read_packet = std::shared_ptr<AVPacket>(read_packet.get(), [=](AVPacket*)\r
+ packet = std::shared_ptr<AVPacket>(packet.get(), [=](AVPacket*)\r
{\r
- read_packet->size = size;\r
- read_packet->data = data;\r
+ packet->size = size;\r
+ packet->data = data;\r
+ tickets_.enqueue(0);\r
+ --packets_count_;\r
});\r
- \r
- buffer_.push_back(read_packet);\r
- buffer_size_ += read_packet->size;\r
- \r
- graph_->update_value("buffer-size", (static_cast<double>(buffer_size_)+0.001)/MAX_BUFFER_SIZE);\r
- graph_->update_value("buffer-count", (static_cast<double>(buffer_.size()+0.001)/MAX_BUFFER_COUNT));\r
+\r
+ tickets_.dequeue();\r
+ ++packets_count_;\r
+\r
+ Concurrency::send(target_, packet);\r
+ \r
+ graph_->update_value("buffer-count", (static_cast<double>(packets_count_)+0.001)/MAX_BUFFER_COUNT);\r
} \r
}\r
\r
THROW_ON_ERROR2(av_seek_frame(format_context_.get(), default_stream_index_, frame, flags), print()); \r
auto packet = create_packet();\r
packet->size = 0;\r
- buffer_.push_back(loop_packet());\r
+\r
+ std::for_each(streams_.begin(), streams_.end(), [this](const AVStream* stream)\r
+ {\r
+ Concurrency::send(target_, loop_packet(stream->index)); \r
+ });\r
} \r
\r
bool is_eof(int ret)\r
if(ret == AVERROR_EOF)\r
CASPAR_LOG(trace) << print() << " Received EOF. " << nb_frames_;\r
\r
+ CASPAR_VERIFY(ret >= 0 || ret == AVERROR_EOF || ret == AVERROR(EIO), ffmpeg_error() << source_info(narrow(print())));\r
+\r
return ret == AVERROR_EOF || ret == AVERROR(EIO) || frame_number_ >= length_; // av_read_frame doesn't always correctly return AVERROR_EOF;\r
}\r
\r
return impl_->nb_loops_;\r
}\r
\r
+void input::start()\r
+{\r
+ impl_->start();\r
+}\r
+\r
void input::stop()\r
{\r
impl_->stop_ = true;\r
}\r
\r
namespace ffmpeg {\r
- \r
+ \r
class input : boost::noncopyable\r
{\r
public:\r
\r
safe_ptr<AVFormatContext> context();\r
\r
+ void start();\r
void stop();\r
private:\r
friend struct implemenation;\r
return packet;\r
}\r
\r
-const std::shared_ptr<AVPacket>& loop_packet()\r
+const std::shared_ptr<AVPacket>& loop_packet(int index)\r
{\r
- static auto packet1 = create_packet();\r
- return packet1;\r
+ static Concurrency::critical_section mutex;\r
+ static std::map<int, std::shared_ptr<AVPacket>> packets;\r
+\r
+ Concurrency::critical_section::scoped_lock lock(mutex);\r
+\r
+ auto& packet = packets[index];\r
+ if(!packet)\r
+ {\r
+ packet = create_packet();\r
+ packet->stream_index = index;\r
+ }\r
+\r
+ return packet;\r
}\r
\r
-const std::shared_ptr<AVPacket>& eof_packet()\r
+const std::shared_ptr<AVPacket>& eof_packet(int index)\r
{\r
- static auto packet2 = create_packet();\r
- return packet2;\r
+ static Concurrency::critical_section mutex;\r
+ static std::map<int, std::shared_ptr<AVPacket>> packets;\r
+\r
+ Concurrency::critical_section::scoped_lock lock(mutex);\r
+\r
+ auto& packet = packets[index];\r
+ if(!packet)\r
+ {\r
+ packet = create_packet();\r
+ packet->stream_index = index;\r
+ }\r
+\r
+ return packet;\r
}\r
\r
const std::shared_ptr<AVFrame>& loop_video()\r
void fix_meta_data(AVFormatContext& context);\r
\r
std::shared_ptr<AVPacket> create_packet();\r
-const std::shared_ptr<AVPacket>& loop_packet();\r
-const std::shared_ptr<AVPacket>& eof_packet();\r
+\r
+const std::shared_ptr<AVPacket>& loop_packet(int index);\r
+const std::shared_ptr<AVPacket>& eof_packet(int index);\r
+\r
const std::shared_ptr<AVFrame>& loop_video();\r
const std::shared_ptr<AVFrame>& empty_video();\r
const std::shared_ptr<AVFrame>& eof_video();\r
#include "../filter/filter.h"\r
\r
#include "../../ffmpeg_error.h"\r
-#include "../../tbb_avcodec.h"\r
\r
#include <core/producer/frame/basic_frame.h>\r
\r
struct video_decoder::implementation : public Concurrency::agent, boost::noncopyable\r
{\r
video_decoder::source_t& source_;\r
- video_decoder::forward_t& forward_;\r
video_decoder::target_t& target_;\r
\r
std::shared_ptr<AVCodecContext> codec_context_;\r
int index_;\r
-\r
- filter filter_;\r
-\r
+ \r
double fps_;\r
int64_t nb_frames_;\r
\r
size_t height_;\r
bool is_progressive_;\r
\r
+ Concurrency::event event_;\r
+\r
public:\r
explicit implementation(video_decoder::source_t& source,\r
- video_decoder::forward_t& forward,\r
video_decoder::target_t& target,\r
const safe_ptr<AVFormatContext>& context,\r
- double fps,\r
- const std::wstring& filter) \r
+ double fps) \r
: source_(source)\r
- , forward_(forward)\r
, target_(target)\r
- , filter_(filter.empty() ? L"copy" : filter)\r
, fps_(fps)\r
, nb_frames_(0)\r
, width_(0)\r
, height_(0)\r
, is_progressive_(true)\r
{\r
- try\r
- {\r
- AVCodec* dec;\r
- index_ = THROW_ON_ERROR2(av_find_best_stream(context.get(), AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0), "[video_decoder]");\r
+ event_.set();\r
+\r
+ AVCodec* dec;\r
+ index_ = THROW_ON_ERROR3(av_find_best_stream(context.get(), AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0), "[video_decoder]", ffmpeg_stream_not_found);\r
+ CASPAR_VERIFY(index_ > -1, ffmpeg_error());\r
\r
- THROW_ON_ERROR2(tbb_avcodec_open(context->streams[index_]->codec, dec), "[video_decoder]");\r
+ THROW_ON_ERROR2(avcodec_open(context->streams[index_]->codec, dec), "[video_decoder]");\r
+ CASPAR_VERIFY(context->streams[index_]->codec, ffmpeg_error())\r
\r
- codec_context_.reset(context->streams[index_]->codec, tbb_avcodec_close);\r
+ codec_context_.reset(context->streams[index_]->codec, avcodec_close);\r
\r
- CASPAR_LOG(debug) << "[video_decoder] " << context->streams[index_]->codec->codec->long_name;\r
+ CASPAR_LOG(debug) << "[video_decoder] " << context->streams[index_]->codec->codec->long_name;\r
\r
- // Some files give an invalid time_base numerator, try to fix it.\r
+ // Some files give an invalid time_base numerator, try to fix it.\r
\r
- fix_meta_data(*context);\r
+ fix_meta_data(*context);\r
\r
- fps_ = static_cast<double>(codec_context_->time_base.den) / static_cast<double>(codec_context_->time_base.num);\r
- nb_frames_ = context->streams[index_]->nb_frames;\r
+ fps_ = static_cast<double>(codec_context_->time_base.den) / static_cast<double>(codec_context_->time_base.num);\r
+ nb_frames_ = context->streams[index_]->nb_frames;\r
+ \r
+ width_ = codec_context_->width;\r
+ height_ = codec_context_->height;\r
\r
- if(double_rate(filter))\r
- fps_ *= 2;\r
-\r
- width_ = codec_context_->width;\r
- height_ = codec_context_->height;\r
- }\r
- catch(...)\r
- {\r
- index_ = THROW_ON_ERROR2(av_find_best_stream(context.get(), AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0), "[video_decoder]");\r
-\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- CASPAR_LOG(warning) << "[video_decoder] Failed to open video-stream. Running without video."; \r
- }\r
+ CASPAR_VERIFY(width_ > 0, ffmpeg_error());\r
+ CASPAR_VERIFY(height_ > 0, ffmpeg_error());\r
\r
start();\r
}\r
{\r
while(true)\r
{\r
- auto packet = Concurrency::receive(source_, 5000);\r
-\r
- if(packet == eof_packet() || packet == loop_packet() || packet->stream_index != index_)\r
- Concurrency::send(forward_, packet);\r
- \r
- if(packet == eof_packet()) \r
+ auto packet = Concurrency::receive(source_, [this](const std::shared_ptr<AVPacket>& packet)\r
+ {\r
+ return packet && packet->stream_index == index_;\r
+ });\r
+\r
+ //if(packet == eof_packet(index_) || packet == loop_packet(index_))\r
+ //{\r
+ // if(codec_context_->codec->capabilities & CODEC_CAP_DELAY)\r
+ // {\r
+ // AVPacket pkt;\r
+ // av_init_packet(&pkt);\r
+ // pkt.data = nullptr;\r
+ // pkt.size = 0;\r
+ //\r
+ // while(decode(pkt)){}\r
+ // }\r
+ //}\r
+ \r
+ if(packet == eof_packet(index_)) \r
break;\r
\r
- if(packet == loop_packet())\r
+ if(packet == loop_packet(index_))\r
{ \r
- if(codec_context_)\r
- {\r
- if(codec_context_->codec->capabilities & CODEC_CAP_DELAY)\r
- {\r
- AVPacket pkt;\r
- av_init_packet(&pkt);\r
- pkt.data = nullptr;\r
- pkt.size = 0;\r
- \r
- BOOST_FOREACH(auto& frame1, decode(pkt))\r
- {\r
- BOOST_FOREACH(auto& frame2, filter_.execute(frame1))\r
- Concurrency::send(target_, std::shared_ptr<AVFrame>(frame2));\r
- } \r
- }\r
-\r
- avcodec_flush_buffers(codec_context_.get());\r
- }\r
- \r
+ avcodec_flush_buffers(codec_context_.get()); \r
Concurrency::send(target_, loop_video()); \r
}\r
else if(packet->stream_index == index_)\r
- {\r
- if(!codec_context_)\r
- {\r
- Concurrency::send(target_, empty_video()); \r
- }\r
- else\r
- {\r
- while(packet->size > 0)\r
- {\r
- BOOST_FOREACH(auto& frame1, decode(*packet))\r
- {\r
- BOOST_FOREACH(auto& frame2, filter_.execute(frame1))\r
- Concurrency::send(target_, std::shared_ptr<AVFrame>(frame2));\r
- }\r
- }\r
- }\r
+ { \r
+ Concurrency::send(target_, decode(*packet)); \r
}\r
}\r
}\r
done();\r
}\r
\r
- std::vector<std::shared_ptr<AVFrame>> decode(AVPacket& pkt)\r
+ std::shared_ptr<AVFrame> decode(AVPacket& pkt)\r
{\r
- std::shared_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
+ event_.wait();\r
+\r
+ std::shared_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), [this](AVFrame* frame)\r
+ {\r
+ av_free(frame);\r
+ event_.set();\r
+ });\r
\r
int frame_finished = 0;\r
THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, &pkt), "[video_decocer]");\r
- \r
+ event_.reset();\r
+\r
// If a decoder consumes less then the whole packet then something is wrong\r
// that might be just harmless padding at the end, or a problem with the\r
// AVParser or demuxer which puted more then one frame in a AVPacket.\r
pkt.size = 0;\r
\r
if(frame_finished == 0) \r
- return std::vector<std::shared_ptr<AVFrame>>();\r
+ return nullptr;\r
\r
- if(decoded_frame->repeat_pict % 2 > 0)\r
+ if(decoded_frame->repeat_pict > 0)\r
CASPAR_LOG(warning) << "[video_decoder]: Field repeat_pict not implemented.";\r
\r
is_progressive_ = decoded_frame->interlaced_frame == 0;\r
-\r
- return std::vector<std::shared_ptr<AVFrame>>(1 + decoded_frame->repeat_pict/2, decoded_frame);\r
+ \r
+ return decoded_frame;\r
}\r
\r
double fps() const\r
};\r
\r
video_decoder::video_decoder(source_t& source,\r
- forward_t& forward,\r
target_t& target,\r
const safe_ptr<AVFormatContext>& context, \r
- double fps, \r
- const std::wstring& filter) \r
- : impl_(new implementation(source, forward, target, context, fps, filter))\r
+ double fps) \r
+ : impl_(new implementation(source, target, context, fps))\r
{\r
}\r
\r
public:\r
\r
typedef Concurrency::ISource<std::shared_ptr<AVPacket>> source_t;\r
- typedef Concurrency::ITarget<std::shared_ptr<AVPacket>> forward_t;\r
typedef Concurrency::ITarget<std::shared_ptr<AVFrame>> target_t;\r
\r
explicit video_decoder(source_t& source,\r
- forward_t& forward,\r
target_t& target,\r
const safe_ptr<AVFormatContext>& context, \r
- double fps, \r
- const std::wstring& filter); \r
+ double fps); \r
\r
size_t width() const;\r
size_t height() const;\r
+++ /dev/null
-// Author Robert Nagy\r
-\r
-#include "stdafx.h"\r
-\r
-#include "tbb_avcodec.h"\r
-\r
-#include <common/log/log.h>\r
-#include <common/env.h>\r
-#include <common/utility/assert.h>\r
-\r
-#include <ppl.h>\r
-\r
-#include <tbb/atomic.h>\r
-\r
-#if defined(_MSC_VER)\r
-#pragma warning (push)\r
-#pragma warning (disable : 4244)\r
-#endif\r
-extern "C" \r
-{\r
- #define __STDC_CONSTANT_MACROS\r
- #define __STDC_LIMIT_MACROS\r
- #include <libavformat/avformat.h>\r
-}\r
-#if defined(_MSC_VER)\r
-#pragma warning (pop)\r
-#endif\r
-\r
-namespace caspar { namespace ffmpeg {\r
- \r
-int thread_execute(AVCodecContext* s, int (*func)(AVCodecContext *c2, void *arg2), void* arg, int* ret, int count, int size)\r
-{\r
- Concurrency::parallel_for(0, count, [&](size_t n)\r
- {\r
- int r = func(s, reinterpret_cast<uint8_t*>(arg) + n*size);\r
- if(ret)\r
- ret[n] = r;\r
- });\r
-\r
- return 0;\r
-}\r
-\r
-int thread_execute2(AVCodecContext* s, int (*func)(AVCodecContext* c2, void* arg2, int, int), void* arg, int* ret, int count)\r
-{ \r
- tbb::atomic<int> counter; \r
- counter = 0; \r
-\r
- // Note: this will probably only work when tbb::task_scheduler_init::num_threads() < 16.\r
- Concurrency::parallel_for(0, count, 2, [&](int jobnr) \r
- { \r
- int threadnr = counter++; \r
- int r = func(s, arg, jobnr, threadnr); \r
- if (ret) \r
- ret[jobnr] = r; \r
- --counter;\r
- }); \r
-\r
- return 0; \r
-}\r
-\r
-void thread_init(AVCodecContext* s)\r
-{\r
- static const size_t MAX_THREADS = 16; // See mpegvideo.h\r
- static int dummy_opaque;\r
-\r
- s->active_thread_type = FF_THREAD_SLICE;\r
- s->thread_opaque = &dummy_opaque; \r
- s->execute = thread_execute;\r
- s->execute2 = thread_execute2;\r
- s->thread_count = MAX_THREADS; // We are using a task-scheduler, so use as many "threads/tasks" as possible. \r
-\r
- CASPAR_LOG(info) << "Initialized ffmpeg tbb context.";\r
-}\r
-\r
-void thread_free(AVCodecContext* s)\r
-{\r
- if(!s->thread_opaque)\r
- return;\r
-\r
- s->thread_opaque = nullptr;\r
- \r
- CASPAR_LOG(info) << "Released ffmpeg tbb context.";\r
-}\r
-\r
-int tbb_avcodec_open(AVCodecContext* avctx, AVCodec* codec)\r
-{\r
- avctx->thread_count = 1;\r
- // Some codecs don't like to have multiple multithreaded decoding instances. Only enable for those we know work.\r
- if((codec->id == CODEC_ID_MPEG2VIDEO) && \r
- (codec->capabilities & CODEC_CAP_SLICE_THREADS) && \r
- (avctx->thread_type & FF_THREAD_SLICE))\r
- {\r
- thread_init(avctx);\r
- } \r
- // ff_thread_init will not be executed since thread_opaque != nullptr || thread_count == 1.\r
- return avcodec_open(avctx, codec); \r
-}\r
-\r
-int tbb_avcodec_close(AVCodecContext* avctx)\r
-{\r
- thread_free(avctx);\r
- // ff_thread_free will not be executed since thread_opaque == nullptr.\r
- return avcodec_close(avctx); \r
-}\r
-\r
-}}
\ No newline at end of file
+++ /dev/null
-#pragma once\r
-\r
-struct AVCodecContext;\r
-struct AVCodec;\r
-\r
-namespace caspar { namespace ffmpeg {\r
- \r
-int tbb_avcodec_open(AVCodecContext *avctx, AVCodec *codec);\r
-int tbb_avcodec_close(AVCodecContext *avctx);\r
-\r
-}}
\ No newline at end of file
auto av_frame = get_av_frame();\r
av_frame->data[0] = const_cast<uint8_t*>(frame->image_data().begin());\r
\r
- auto frames = filter_.execute(av_frame);\r
-\r
+ std::vector<safe_ptr<AVFrame>> frames;\r
+ filter_.push(av_frame);\r
+ while(true)\r
+ {\r
+ auto frame = filter_.poll();\r
+ if(!frame)\r
+ break;\r
+ frames.push_back(make_safe_ptr(frame));\r
+ }\r
+ \r
if(frames.empty())\r
return;\r
\r
\r
bool auto_play = std::find(_parameters.begin(), _parameters.end(), L"AUTO") != _parameters.end();\r
\r
- auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().field_mode, create_destroy_producer_proxy(GetChannel()->context().destruction(), pFP), transitionInfo);\r
+ auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().field_mode, create_destroy_producer_proxy(pFP), transitionInfo);\r
GetChannel()->stage()->load(GetLayerIndex(), pFP2, false, auto_play ? transitionInfo.duration : -1); // TODO: LOOP\r
\r
CASPAR_LOG(info) << "Loaded " << _parameters[0] << TEXT(" successfully to background");\r
</producers>\r
<channels>\r
<channel>\r
- <video-mode>720p5000</video-mode>\r
+ <video-mode>1080p5000</video-mode>\r
<consumers>\r
<decklink>\r
<device>1</device>\r
<embedded-audio>true</embedded-audio>\r
- <low-latency>true</low-latency>\r
</decklink>\r
- <audio></audio>\r
</consumers>\r
</channel>\r
</channels>\r