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), std::forward<P6>(p6), std::forward<P7>(p7)));\r
}\r
\r
+template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>\r
+safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8)\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), std::forward<P6>(p6), std::forward<P7>(p7), std::forward<P8>(p8)));\r
+}\r
+\r
+template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>\r
+safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8, P9&& p9)\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), std::forward<P6>(p6), std::forward<P7>(p7), std::forward<P8>(p8), std::forward<P9>(p9)));\r
+}\r
+\r
template<typename T>\r
safe_ptr<T>::safe_ptr() \r
: p_(make_safe<T>())\r
auto producer = do_create_producer(my_frame_factory, upper_case_params, original_case_params, g_factories);\r
auto key_producer = frame_producer::empty();\r
\r
+ std::wstring resource_name = L"";\r
+ auto tokens = protocol_split(original_case_params[0]);\r
+ if (tokens[0].empty())\r
+ {\r
+ resource_name = original_case_params[0];\r
+ }\r
+\r
+ if(!resource_name.empty()) {\r
try // to find a key file.\r
{\r
auto upper_params_copy = upper_case_params;\r
}\r
}\r
catch(...){}\r
+ }\r
\r
if(producer != frame_producer::empty() && key_producer != frame_producer::empty())\r
return create_separated_producer(producer, key_producer);\r
return create_producer(factory, tokens, tokens);\r
}\r
\r
+std::vector<std::wstring> protocol_split(std::wstring const& s)\r
+{\r
+ std::vector<std::wstring> result;\r
+ size_t pos;\r
+ if ((pos = s.find_first_of(L"://")) != std::wstring::npos)\r
+ {\r
+ result.push_back(s.substr(0, pos));\r
+ result.push_back(s.substr(pos + 3));\r
+ } else\r
+ {\r
+ result.push_back(L"");\r
+ result.push_back(s);\r
+ }\r
+ return result;\r
+}\r
+\r
+\r
}}
\ No newline at end of file
safe_ptr<core::frame_producer> create_producer_print_proxy(safe_ptr<core::frame_producer> producer);\r
safe_ptr<core::frame_producer> create_thumbnail_producer(const safe_ptr<frame_factory>& factory, const std::wstring& media_file);\r
\r
+std::vector<std::wstring> protocol_split(std::wstring const& s);\r
+\r
}}\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
av_lockmgr_register(ffmpeg_lock_callback);\r
av_log_set_callback(log_for_thread);\r
\r
+ avdevice_register_all();\r
avfilter_register_all();\r
//fix_yadif_filter_format_query();\r
av_register_all();\r
avformat_network_init();\r
- avcodec_init();\r
+ //avcodec_init();\r
avcodec_register_all();\r
\r
core::register_consumer_factory([](const std::vector<std::wstring>& params){return create_consumer(params);});\r
<ClInclude Include="ffmpeg_error.h" />\r
<ClInclude Include="producer\audio\audio_decoder.h" />\r
<ClInclude Include="producer\audio\audio_resampler.h" />\r
+ <ClInclude Include="producer\ffmpeg_params.h" />\r
<ClInclude Include="producer\ffmpeg_producer.h" />\r
<ClInclude Include="producer\filter\filter.h" />\r
<ClInclude Include="producer\filter\parallel_yadif.h" />\r
<ClInclude Include="producer\tbb_avcodec.h">\r
<Filter>source\producer</Filter>\r
</ClInclude>\r
+ <ClInclude Include="producer\ffmpeg_params.h">\r
+ <Filter>source\producer</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
--- /dev/null
+#pragma once
+
+#include <string>
+
+namespace caspar {
+namespace ffmpeg {
+
+enum FFMPEG_Resource {
+ FFMPEG_FILE,
+ FFMPEG_DEVICE,
+ FFMPEG_STREAM
+};
+
+struct ffmpeg_params
+{
+ std::wstring size_str;
+ std::wstring pixel_format;
+ std::wstring frame_rate;
+
+
+ ffmpeg_params()
+ : size_str(L"")
+ , pixel_format(L"")
+ , frame_rate(L"")
+ {
+ }
+
+};
+
+}}
\ No newline at end of file
\r
#include "../ffmpeg_error.h"\r
#include "../ffmpeg.h"\r
+#include "ffmpeg_params.h"\r
\r
#include "muxer/frame_muxer.h"\r
#include "input/input.h"\r
#include <queue>\r
\r
namespace caspar { namespace ffmpeg {\r
+\r
\r
struct ffmpeg_producer : public core::frame_producer\r
{\r
core::monitor::subject monitor_subject_;\r
const std::wstring filename_;\r
+\r
+ FFMPEG_Resource resource_type_;\r
\r
const safe_ptr<diagnostics::graph> graph_;\r
boost::timer frame_timer_;\r
uint32_t file_frame_number_;\r
\r
public:\r
- explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, const std::wstring& filter, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const std::wstring& custom_channel_order)\r
+ explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, FFMPEG_Resource resource_type, const std::wstring& filter, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const std::wstring& custom_channel_order, const ffmpeg_params& vid_params)\r
: filename_(filename)\r
+ , resource_type_(resource_type)\r
, frame_factory_(frame_factory) \r
, format_desc_(frame_factory->get_video_format_desc())\r
, initial_logger_disabler_(temporary_disable_logging_for_thread(thumbnail_mode))\r
- , input_(graph_, filename_, loop, start, length, thumbnail_mode)\r
+ , input_(graph_, filename_, resource_type, loop, start, length, thumbnail_mode, vid_params)\r
, fps_(read_fps(*input_.context(), format_desc_.fps))\r
, start_(start)\r
, length_(length)\r
try_decode_frame(hints);\r
\r
graph_->set_value("frame-time", frame_timer_.elapsed()*format_desc_.fps*0.5);\r
- \r
- if(frame_buffer_.empty() && input_.eof())\r
- return std::make_pair(last_frame(), -1);\r
\r
if(frame_buffer_.empty())\r
{\r
- graph_->set_tag("underflow"); \r
- return std::make_pair(core::basic_frame::late(), -1);\r
+ if (input_.eof())\r
+ {\r
+ return std::make_pair(last_frame(), -1);\r
+ } else if (resource_type_ == FFMPEG_FILE)\r
+ {\r
+ graph_->set_tag("underflow"); \r
+ return std::make_pair(core::basic_frame::late(), -1); \r
+ } else\r
+ {\r
+ return std::make_pair(last_frame(), -1);\r
+ }\r
}\r
\r
auto frame = frame_buffer_.front(); \r
\r
virtual uint32_t nb_frames() const override\r
{\r
- if(input_.loop())\r
+ //if(input_.loop())\r
+ if(resource_type_ == FFMPEG_DEVICE || resource_type_ == FFMPEG_STREAM || input_.loop()) \r
return std::numeric_limits<uint32_t>::max();\r
\r
uint32_t nb_frames = file_nb_frames();\r
const std::vector<std::wstring>& original_case_params)\r
{ \r
static const std::vector<std::wstring> invalid_exts = boost::assign::list_of(L".png")(L".tga")(L".bmp")(L".jpg")(L".jpeg")(L".gif")(L".tiff")(L".tif")(L".jp2")(L".jpx")(L".j2k")(L".j2c")(L".swf")(L".ct");\r
- auto filename = probe_stem(env::media_folder() + L"\\" + params.at(0), invalid_exts);\r
+\r
+ // Infer the resource type from the resource_name\r
+ auto resource_type = FFMPEG_FILE;\r
+ auto tokens = caspar::core::protocol_split(original_case_params[0]);\r
+ auto filename = params[0];\r
+ if (!tokens[0].empty())\r
+ {\r
+ if (tokens[0] == L"dshow")\r
+ {\r
+ // Camera\r
+ resource_type = FFMPEG_DEVICE;\r
+ filename = tokens[1];\r
+ } else\r
+ {\r
+ // Stream\r
+ resource_type = FFMPEG_STREAM;\r
+ filename = original_case_params[0];\r
+ }\r
+ } else\r
+ {\r
+ // File\r
+ resource_type = FFMPEG_FILE;\r
+ filename = env::media_folder() + L"\\" + tokens[1];\r
+ if(!boost::filesystem::exists(filename))\r
+ filename = probe_stem(filename);\r
+\r
+ //TODO fix these?\r
+ //ffmpeg_params->loop = params.has(L"LOOP");\r
+ //ffmpeg_params->start = params.get(L"SEEK", static_cast<uint32_t>(0));\r
+ }\r
\r
if(filename.empty())\r
return core::frame_producer::empty();\r
\r
boost::replace_all(filter_str, L"DEINTERLACE", L"YADIF=0:-1");\r
boost::replace_all(filter_str, L"DEINTERLACE_BOB", L"YADIF=1:-1");\r
+\r
+ ffmpeg_params vid_params;\r
+ vid_params.size_str = get_param(L"SIZE", params, L"");\r
+ vid_params.pixel_format = get_param(L"PIXFMT", params, L"");\r
+ vid_params.frame_rate = get_param(L"FRAMERATE", params, L"");\r
\r
- return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, filter_str, loop, start, length, false, custom_channel_order));\r
+ return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, resource_type, filter_str, loop, start, length, false, custom_channel_order, vid_params));\r
}\r
\r
safe_ptr<core::frame_producer> create_thumbnail_producer(\r
auto length = std::numeric_limits<uint32_t>::max();\r
auto filter_str = L"";\r
\r
- return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, filter_str, loop, start, length, true, L""));\r
+ ffmpeg_params vid_params;\r
+\r
+ return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, FFMPEG_FILE, filter_str, loop, start, length, true, L"", vid_params));\r
}\r
\r
}}
\ No newline at end of file
#include "input.h"\r
\r
#include "../util/util.h"\r
+#include "../util/flv.h"\r
#include "../../ffmpeg_error.h"\r
#include "../../ffmpeg.h"\r
\r
\r
executor executor_;\r
\r
- explicit implementation(const safe_ptr<diagnostics::graph> graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode) \r
+ explicit implementation(const safe_ptr<diagnostics::graph> graph, const std::wstring& filename, FFMPEG_Resource resource_type, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const ffmpeg_params& vid_params) \r
: graph_(graph)\r
- , format_context_(open_input(filename)) \r
+ , format_context_(open_input(filename, resource_type, vid_params)) \r
, default_stream_index_(av_find_default_stream_index(format_context_.get()))\r
, filename_(filename)\r
, start_(start)\r
}\r
});\r
} \r
+\r
+ safe_ptr<AVFormatContext> open_input(const std::wstring resource_name, FFMPEG_Resource resource_type, const ffmpeg_params& vid_params)\r
+ {\r
+ AVFormatContext* weak_context = nullptr;\r
+\r
+ switch (resource_type) {\r
+ case FFMPEG_FILE:\r
+ THROW_ON_ERROR2(avformat_open_input(&weak_context, narrow(resource_name).c_str(), nullptr, nullptr), resource_name);\r
+ break;\r
+ case FFMPEG_DEVICE: {\r
+ AVDictionary* format_options = NULL;\r
+ if(vid_params.size_str != L"")\r
+ av_dict_set(&format_options, "video_size", narrow(vid_params.size_str).c_str(), 0); // 640x360 for 16:9\r
+ if(vid_params.pixel_format != L"")\r
+ av_dict_set(&format_options, "pixel_format", narrow(vid_params.pixel_format).c_str(), 0); // yuyv422 for sony\r
+ av_dict_set(&format_options, "avioflags", "direct", 0);\r
+\r
+ // Don't set framerate here, it is typically set to a fixed rate anyway. Better to insert a fps filter to adjust the frame rate.\r
+ // Im going to try anyway for now\r
+ if(vid_params.frame_rate != L"")\r
+ av_dict_set(&format_options, "framerate", narrow(vid_params.frame_rate).c_str(), 0);\r
+ AVInputFormat* input_format = av_find_input_format("dshow");\r
+ THROW_ON_ERROR2(avformat_open_input(&weak_context, narrow(resource_name).c_str(), input_format, &format_options), resource_name);\r
+ av_dict_free(&format_options);\r
+ } break;\r
+ case FFMPEG_STREAM:\r
+ THROW_ON_ERROR2(avformat_open_input(&weak_context, narrow(resource_name).c_str(), nullptr, nullptr), resource_name);\r
+ break;\r
+ };\r
+ safe_ptr<AVFormatContext> context(weak_context, av_close_input_file); \r
+ THROW_ON_ERROR2(avformat_find_stream_info(weak_context, nullptr), resource_name);\r
+ fix_meta_data(*context);\r
+ return context;\r
+ }\r
+\r
+ void fix_meta_data(AVFormatContext& context)\r
+ {\r
+ auto video_index = av_find_best_stream(&context, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);\r
+\r
+ if(video_index > -1)\r
+ {\r
+ auto video_stream = context.streams[video_index];\r
+ auto video_context = context.streams[video_index]->codec;\r
+ \r
+ if(boost::filesystem2::path(context.filename).extension() == ".flv")\r
+ {\r
+ try\r
+ {\r
+ auto meta = read_flv_meta_info(context.filename);\r
+ double fps = boost::lexical_cast<double>(meta["framerate"]);\r
+ video_stream->nb_frames = static_cast<int64_t>(boost::lexical_cast<double>(meta["duration"])*fps);\r
+ }\r
+ catch(...){}\r
+ }\r
+ else\r
+ {\r
+ auto stream_time = video_stream->time_base;\r
+ auto duration = video_stream->duration;\r
+ auto codec_time = video_context->time_base;\r
+ auto ticks = video_context->ticks_per_frame;\r
+\r
+ if(video_stream->nb_frames == 0)\r
+ video_stream->nb_frames = (duration*stream_time.num*codec_time.den)/(stream_time.den*codec_time.num*ticks); \r
+ }\r
+ }\r
+ }\r
\r
void queued_seek(const uint32_t target)\r
{ \r
}\r
};\r
\r
-input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode) \r
- : impl_(new implementation(graph, filename, loop, start, length, thumbnail_mode)){}\r
+input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, FFMPEG_Resource resource_type, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const ffmpeg_params& vid_params) \r
+ : impl_(new implementation(graph, filename, resource_type, loop, start, length, thumbnail_mode, vid_params)){}\r
bool input::eof() const {return !impl_->executor_.is_running();}\r
bool input::try_pop(std::shared_ptr<AVPacket>& packet){return impl_->try_pop(packet);}\r
safe_ptr<AVFormatContext> input::context(){return impl_->format_context_;}\r
*/\r
\r
#pragma once\r
+#include "../ffmpeg_params.h"\r
\r
#include <common/memory/safe_ptr.h>\r
\r
class input : boost::noncopyable\r
{\r
public:\r
- explicit input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode);\r
+ explicit input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, FFMPEG_Resource resource_type, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const ffmpeg_params& vid_params);\r
\r
bool try_pop(std::shared_ptr<AVPacket>& packet);\r
bool eof() const;\r
return fail_value; \r
}\r
\r
-void fix_meta_data(AVFormatContext& context)\r
-{\r
- auto video_index = av_find_best_stream(&context, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);\r
-\r
- if(video_index > -1)\r
- {\r
- auto video_stream = context.streams[video_index];\r
- auto video_context = context.streams[video_index]->codec;\r
- \r
- if(boost::filesystem2::path(context.filename).extension() == ".flv")\r
- {\r
- try\r
- {\r
- auto meta = read_flv_meta_info(context.filename);\r
- double fps = boost::lexical_cast<double>(meta["framerate"]);\r
- video_stream->nb_frames = static_cast<int64_t>(boost::lexical_cast<double>(meta["duration"])*fps);\r
- }\r
- catch(...){}\r
- }\r
- else\r
- {\r
- auto stream_time = video_stream->time_base;\r
- auto duration = video_stream->duration;\r
- auto codec_time = video_context->time_base;\r
- auto ticks = video_context->ticks_per_frame;\r
-\r
- if(video_stream->nb_frames == 0)\r
- video_stream->nb_frames = (duration*stream_time.num*codec_time.den)/(stream_time.den*codec_time.num*ticks); \r
- }\r
- }\r
-}\r
-\r
safe_ptr<AVPacket> create_packet()\r
{\r
safe_ptr<AVPacket> packet(new AVPacket, [](AVPacket* p)\r
return safe_ptr<AVCodecContext>(context.streams[index]->codec, tbb_avcodec_close);\r
}\r
\r
-safe_ptr<AVFormatContext> open_input(const std::wstring& filename)\r
-{\r
- AVFormatContext* weak_context = nullptr;\r
- THROW_ON_ERROR2(avformat_open_input(&weak_context, narrow(filename).c_str(), nullptr, nullptr), filename);\r
- safe_ptr<AVFormatContext> context(weak_context, av_close_input_file); \r
- THROW_ON_ERROR2(avformat_find_stream_info(weak_context, nullptr), filename);\r
- fix_meta_data(*context);\r
- return context;\r
-}\r
-\r
std::wstring print_mode(size_t width, size_t height, double fps, bool interlaced)\r
{\r
std::wostringstream fps_ss;\r
return L"";\r
}\r
\r
+std::wstring probe_stem(const std::wstring stem)\r
+{\r
+ auto stem2 = boost::filesystem2::wpath(stem);\r
+ auto dir = stem2.parent_path();\r
+ for(auto it = boost::filesystem2::wdirectory_iterator(dir); it != boost::filesystem2::wdirectory_iterator(); ++it)\r
+ {\r
+ if(boost::iequals(it->path().stem(), stem2.filename()) && is_valid_file(it->path().file_string()))\r
+ return it->path().file_string();\r
+ }\r
+ return L"";\r
+}\r
+\r
core::channel_layout get_audio_channel_layout(\r
const AVCodecContext& context, const std::wstring& custom_channel_order)\r
{\r
safe_ptr<AVPacket> create_packet();\r
\r
safe_ptr<AVCodecContext> open_codec(AVFormatContext& context, enum AVMediaType type, int& index);\r
-safe_ptr<AVFormatContext> open_input(const std::wstring& filename);\r
\r
bool is_sane_fps(AVRational time_base);\r
AVRational fix_time_base(AVRational time_base);\r
std::wstring print_mode(size_t width, size_t height, double fps, bool interlaced);\r
\r
std::wstring probe_stem(const std::wstring stem, const std::vector<std::wstring>& invalid_exts);\r
+std::wstring probe_stem(const std::wstring stem);\r
bool is_valid_file(const std::wstring filename, const std::vector<std::wstring>& invalid_exts);\r
bool is_valid_file(const std::wstring filename);\r
\r
\r
if(packet->data == nullptr)\r
{ \r
+ packets_.pop();\r
if(codec_context_->codec->capabilities & CODEC_CAP_DELAY)\r
{\r
- auto video = decode(*packet);\r
+ auto video = decode(packet.get());\r
if(video)\r
return video;\r
}\r
\r
- packets_.pop();\r
+ //packets_.pop();\r
file_frame_number_ = static_cast<size_t>(packet->pos);\r
avcodec_flush_buffers(codec_context_.get());\r
return flush_video(); \r
}\r
\r
packets_.pop();\r
- return decode(*packet);\r
+ return decode(packet.get());\r
}\r
\r
- 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
\r
int frame_finished = 0;\r
- THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, &pkt), "[video_decocer]");\r
+ THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, pkt), "[video_decocer]");\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
\r
++file_frame_number_;\r
\r
- return decoded_frame;\r
+ //return decoded_frame;\r
+ return std::shared_ptr<AVFrame>(decoded_frame.get(), [decoded_frame, pkt](AVFrame*){});\r
}\r
\r
bool ready() const\r
<ForcedIncludeFiles>common/compiler/vs/disable_silly_warnings.h</ForcedIncludeFiles>\r
</ClCompile>\r
<Link>\r
- <AdditionalDependencies>sfml-system-d.lib;sfml-audio-d.lib;sfml-window-d.lib;sfml-graphics-d.lib;OpenGL32.lib;FreeImage.lib;Winmm.lib;Ws2_32.lib;avformat.lib;avcodec.lib;avutil.lib;avfilter.lib;swscale.lib;tbb.lib;glew32.lib;zdll.lib</AdditionalDependencies>\r
+ <AdditionalDependencies>sfml-system-d.lib;sfml-audio-d.lib;sfml-window-d.lib;sfml-graphics-d.lib;OpenGL32.lib;FreeImage.lib;Winmm.lib;Ws2_32.lib;avformat.lib;avcodec.lib;avdevice.lib;avutil.lib;avfilter.lib;swscale.lib;tbb.lib;glew32.lib;zdll.lib</AdditionalDependencies>\r
<Version>\r
</Version>\r
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r