/*\r
-* copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
+* Copyright 2013 Sveriges Television AB http://casparcg.com/\r
*\r
-* This file is part of CasparCG.\r
+* This file is part of CasparCG (www.casparcg.com).\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
+* 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
+* 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
+* Author: Robert Nagy, ronag89@gmail.com\r
*/\r
+\r
#include "StdAfx.h"\r
\r
#include "consumer/ffmpeg_consumer.h"\r
+#include "consumer/streaming_consumer.h"\r
#include "producer/ffmpeg_producer.h"\r
+#include "producer/util/util.h"\r
\r
+#include <common/log/log.h>\r
+#include <common/exception/win32_exception.h>\r
+\r
+#include <core/parameters/parameters.h>\r
#include <core/consumer/frame_consumer.h>\r
#include <core/producer/frame_producer.h>\r
+#include <core/producer/media_info/media_info.h>\r
+#include <core/producer/media_info/media_info_repository.h>\r
+\r
+#include <tbb/recursive_mutex.h>\r
+\r
+#include <boost/thread.hpp>\r
\r
#if defined(_MSC_VER)\r
#pragma warning (disable : 4244)\r
+#pragma warning (disable : 4603)\r
+#pragma warning (disable : 4996)\r
#endif\r
\r
extern "C" \r
#include <libswscale/swscale.h>\r
#include <libavutil/avutil.h>\r
#include <libavfilter/avfilter.h>\r
+ #include <libavdevice/avdevice.h>\r
+}\r
+\r
+namespace caspar { namespace ffmpeg {\r
+ \r
+int ffmpeg_lock_callback(void **mutex, enum AVLockOp op) \r
+{ \r
+ win32_exception::ensure_handler_installed_for_thread("ffmpeg-thread");\r
+ if(!mutex)\r
+ return 0;\r
+\r
+ auto my_mutex = reinterpret_cast<tbb::recursive_mutex*>(*mutex);\r
+ \r
+ switch(op) \r
+ { \r
+ case AV_LOCK_CREATE: \r
+ { \r
+ *mutex = new tbb::recursive_mutex(); \r
+ break; \r
+ } \r
+ case AV_LOCK_OBTAIN: \r
+ { \r
+ if(my_mutex)\r
+ my_mutex->lock(); \r
+ break; \r
+ } \r
+ case AV_LOCK_RELEASE: \r
+ { \r
+ if(my_mutex)\r
+ my_mutex->unlock(); \r
+ break; \r
+ } \r
+ case AV_LOCK_DESTROY: \r
+ { \r
+ delete my_mutex;\r
+ *mutex = nullptr;\r
+ break; \r
+ } \r
+ } \r
+ return 0; \r
+} \r
+\r
+static void sanitize(uint8_t *line)\r
+{\r
+ while(*line)\r
+ {\r
+ if(*line < 0x08 || (*line > 0x0D && *line < 0x20))\r
+ *line='?';\r
+ line++;\r
+ }\r
+}\r
+\r
+void log_callback(void* ptr, int level, const char* fmt, va_list vl)\r
+{\r
+ static int print_prefix=1;\r
+ static int count;\r
+ static char prev[1024];\r
+ char line[8192];\r
+ static int is_atty;\r
+ AVClass* avc= ptr ? *(AVClass**)ptr : NULL;\r
+ if(level > av_log_get_level())\r
+ return;\r
+ line[0]=0;\r
+ \r
+#undef fprintf\r
+ if(print_prefix && avc) \r
+ {\r
+ if (avc->parent_log_context_offset) \r
+ {\r
+ AVClass** parent= *(AVClass***)(((uint8_t*)ptr) + avc->parent_log_context_offset);\r
+ if(parent && *parent)\r
+ std::sprintf(line, "[%s @ %p] ", (*parent)->item_name(parent), parent); \r
+ }\r
+ std::sprintf(line + strlen(line), "[%s @ %p] ", avc->item_name(ptr), ptr);\r
+ }\r
+\r
+ std::vsprintf(line + strlen(line), fmt, vl);\r
+\r
+ print_prefix = strlen(line) && line[strlen(line)-1] == '\n';\r
+ \r
+ //if(print_prefix && !strcmp(line, prev)){\r
+ // count++;\r
+ // if(is_atty==1)\r
+ // fprintf(stderr, " Last message repeated %d times\r", count);\r
+ // return;\r
+ //}\r
+ //if(count>0){\r
+ // fprintf(stderr, " Last message repeated %d times\n", count);\r
+ // count=0;\r
+ //}\r
+ strcpy(prev, line);\r
+ sanitize((uint8_t*)line);\r
+\r
+ int len = strlen(line);\r
+ if(len > 0)\r
+ line[len-1] = 0;\r
+ \r
+ if(level == AV_LOG_DEBUG)\r
+ CASPAR_LOG(debug) << L"[ffmpeg] " << line;\r
+ else if(level == AV_LOG_INFO)\r
+ CASPAR_LOG(info) << L"[ffmpeg] " << line;\r
+ else if(level == AV_LOG_WARNING)\r
+ CASPAR_LOG(warning) << L"[ffmpeg] " << line;\r
+ else if(level == AV_LOG_ERROR)\r
+ CASPAR_LOG(error) << L"[ffmpeg] " << line;\r
+ else if(level == AV_LOG_FATAL)\r
+ CASPAR_LOG(fatal) << L"[ffmpeg] " << line;\r
+ else\r
+ CASPAR_LOG(trace) << L"[ffmpeg] " << line;\r
+\r
+ //colored_fputs(av_clip(level>>3, 0, 6), line);\r
}\r
\r
-namespace caspar {\r
+boost::thread_specific_ptr<bool>& get_disable_logging_for_thread()\r
+{\r
+ static boost::thread_specific_ptr<bool> disable_logging_for_thread;\r
+\r
+ return disable_logging_for_thread;\r
+}\r
+\r
+void disable_logging_for_thread()\r
+{\r
+ if (get_disable_logging_for_thread().get() == nullptr)\r
+ get_disable_logging_for_thread().reset(new bool); // bool value does not matter\r
+}\r
\r
-void init_ffmpeg()\r
+bool is_logging_already_disabled_for_thread()\r
{\r
- av_register_all();\r
- avcodec_init();\r
+ return get_disable_logging_for_thread().get() != nullptr;\r
+}\r
+\r
+std::shared_ptr<void> temporary_disable_logging_for_thread(bool disable)\r
+{\r
+ if (!disable || is_logging_already_disabled_for_thread())\r
+ return std::shared_ptr<void>();\r
+\r
+ disable_logging_for_thread();\r
+\r
+ return std::shared_ptr<void>(nullptr, [] (void*)\r
+ {\r
+ get_disable_logging_for_thread().release(); // Only works correctly if destructed in same thread as original caller.\r
+ });\r
+}\r
+\r
+void log_for_thread(void* ptr, int level, const char* fmt, va_list vl)\r
+{\r
+ win32_exception::ensure_handler_installed_for_thread("ffmpeg-thread");\r
+ if (get_disable_logging_for_thread().get() == nullptr) // It does not matter what the value of the bool is\r
+ log_callback(ptr, level, fmt, vl);\r
+}\r
+\r
+//static int query_yadif_formats(AVFilterContext *ctx)\r
+//{\r
+// static const int pix_fmts[] = {\r
+// PIX_FMT_YUV444P,\r
+// PIX_FMT_YUV422P,\r
+// PIX_FMT_YUV420P,\r
+// PIX_FMT_YUV410P,\r
+// PIX_FMT_YUV411P,\r
+// PIX_FMT_GRAY8,\r
+// PIX_FMT_YUVJ444P,\r
+// PIX_FMT_YUVJ422P,\r
+// PIX_FMT_YUVJ420P,\r
+// AV_NE( PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE ),\r
+// PIX_FMT_YUV440P,\r
+// PIX_FMT_YUVJ440P,\r
+// AV_NE( PIX_FMT_YUV444P16BE, PIX_FMT_YUV444P16LE ),\r
+// AV_NE( PIX_FMT_YUV422P16BE, PIX_FMT_YUV422P16LE ),\r
+// AV_NE( PIX_FMT_YUV420P16BE, PIX_FMT_YUV420P16LE ),\r
+// PIX_FMT_YUVA420P,\r
+// PIX_FMT_NONE\r
+// };\r
+// avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
+//\r
+// return 0;\r
+//}\r
+//\r
+//#pragma warning (push)\r
+//#pragma warning (disable : 4706)\r
+//void fix_yadif_filter_format_query()\r
+//{\r
+// AVFilter** filter = nullptr;\r
+// while((filter = av_filter_next(filter)) && *filter)\r
+// {\r
+// if(strstr((*filter)->name, "yadif") != 0)\r
+// (*filter)->query_formats = query_yadif_formats;\r
+// }\r
+//}\r
+//#pragma warning (pop)\r
+\r
+void init(const safe_ptr<core::media_info_repository>& media_info_repo)\r
+{\r
+ av_lockmgr_register(ffmpeg_lock_callback);\r
+ av_log_set_callback(log_for_thread);\r
+ \r
+ avcodec_register_all(); \r
+ avdevice_register_all();\r
+ avfilter_register_all();\r
+ av_register_all();\r
+ avformat_network_init();\r
\r
- //core::register_consumer_factory([](const std::vector<std::wstring>& params){return create_ffmpeg_consumer(params);});\r
- core::register_producer_factory(create_ffmpeg_producer);\r
+ core::register_consumer_factory([](const core::parameters& params){return ffmpeg::create_consumer(params);});\r
+ core::register_consumer_factory([](const core::parameters& params){return ffmpeg::create_streaming_consumer(params);});\r
+ core::register_producer_factory(create_producer);\r
+ core::register_thumbnail_producer_factory(create_thumbnail_producer);\r
+\r
+ media_info_repo->register_extractor(\r
+ [](const std::wstring& file, core::media_info& info) -> bool\r
+ {\r
+ auto disable_logging = temporary_disable_logging_for_thread(true);\r
+\r
+ return is_valid_file(file) && try_get_duration(file, info.duration, info.time_base);\r
+ });\r
+}\r
+\r
+void uninit()\r
+{\r
+ avfilter_uninit();\r
+ avformat_network_deinit();\r
+ av_lockmgr_register(nullptr);\r
}\r
\r
std::wstring make_version(unsigned int ver)\r
return make_version(swscale_version());\r
}\r
\r
-}
\ No newline at end of file
+}}
\ No newline at end of file