#include <tbb/cache_aligned_allocator.h>\r
#include <tbb/parallel_invoke.h>\r
\r
+#include <string>\r
+\r
#if defined(_MSC_VER)\r
#pragma warning (push)\r
#pragma warning (disable : 4244)\r
#include <libavformat/avformat.h>\r
#include <libswscale/swscale.h>\r
#include <libavutil/opt.h>\r
+ #include <libavutil/pixdesc.h>\r
}\r
#if defined(_MSC_VER)\r
#pragma warning (pop)\r
#endif\r
\r
namespace caspar { namespace ffmpeg {\r
+\r
+struct option\r
+{\r
+ std::string name;\r
+ std::string value;\r
+\r
+ option(std::string name, std::string value)\r
+ : name(std::move(name))\r
+ , value(std::move(value))\r
+ {\r
+ }\r
+};\r
+ \r
+void set_format(AVOutputFormat*& format, const std::string& value)\r
+{\r
+ format = av_guess_format(value.c_str(), nullptr, nullptr);\r
+\r
+ if(format == nullptr)\r
+ BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("f")); \r
+}\r
+\r
+bool parse_format(AVOutputFormat*& format, std::vector<option>& options)\r
+{ \r
+ auto format_it = std::find_if(options.begin(), options.end(), [](const option& o)\r
+ {\r
+ return o.name == "f" || o.name == "format";\r
+ });\r
+\r
+ if(format_it == options.end())\r
+ return false;\r
+\r
+ set_format(format, format_it->value);\r
+\r
+ options.erase(format_it);\r
+\r
+ return true;\r
+}\r
+\r
+void set_vcodec(CodecID& vcodec, const std::string& value)\r
+{ \r
+ vcodec = avcodec_find_encoder_by_name(value.c_str())->id;\r
+ if(vcodec == CODEC_ID_NONE)\r
+ BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("vcodec"));\r
+}\r
+\r
+bool parse_vcodec(CodecID& vcodec, std::vector<option>& options)\r
+{\r
+ auto vcodec_it = std::find_if(options.begin(), options.end(), [](const option& o)\r
+ {\r
+ return o.name == "vcodec";\r
+ });\r
+\r
+ if(vcodec_it == options.end())\r
+ return false;\r
+ \r
+ set_vcodec(vcodec, vcodec_it->value);\r
+\r
+ options.erase(vcodec_it);\r
+\r
+ return true;\r
+}\r
+\r
+void set_pix_fmt(AVCodecContext* vcodec, const std::string& value)\r
+{\r
+ auto pix_fmt = av_get_pix_fmt(value.c_str());\r
+ if(pix_fmt == PIX_FMT_NONE)\r
+ BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("pix_fmt"));\r
+\r
+ vcodec->pix_fmt = pix_fmt;\r
+}\r
+\r
+bool parse_pix_fmt(AVCodecContext* vcodec, std::vector<option>& options)\r
+{\r
+ auto pix_fmt_it = std::find_if(options.begin(), options.end(), [](const option& o)\r
+ {\r
+ return o.name == "pix_fmt" || o.name == "pixel_format";\r
+ });\r
+ \r
+ if(pix_fmt_it == options.end())\r
+ return false;\r
+\r
+ set_pix_fmt(vcodec, pix_fmt_it->value);\r
\r
+ options.erase(pix_fmt_it);\r
+\r
+ return true;\r
+}\r
+\r
struct ffmpeg_consumer : boost::noncopyable\r
{ \r
const std::string filename_;\r
int64_t frame_number_;\r
\r
public:\r
- ffmpeg_consumer(const std::string& filename, const core::video_format_desc& format_desc, const std::string& codec, const std::vector<std::string>& options)\r
+ ffmpeg_consumer(const std::string& filename, const core::video_format_desc& format_desc, std::vector<option> options)\r
: filename_(filename)\r
, video_outbuf_(1920*1080*8)\r
, oc_(avformat_alloc_context(), av_free)\r
if (!oc_->oformat)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Could not find suitable output format."));\r
\r
+ parse_format(oc_->oformat, options);\r
+\r
+ auto vcodec = oc_->oformat->video_codec;\r
+ parse_vcodec(vcodec, options);\r
+ \r
THROW_ON_ERROR2(av_set_parameters(oc_.get(), nullptr), "[ffmpeg_consumer]");\r
\r
strcpy_s(oc_->filename, filename_.c_str());\r
\r
- auto video_codec = avcodec_find_encoder_by_name(codec.c_str());\r
- if(video_codec == nullptr)\r
- BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info(codec));\r
-\r
// Add the audio and video streams using the default format codecs and initialize the codecs .\r
- video_st_ = add_video_stream(video_codec->id, options);\r
+ video_st_ = add_video_stream(vcodec, options);\r
audio_st_ = add_audio_stream();\r
\r
dump_format(oc_.get(), 0, filename_.c_str(), 1);\r
return L"ffmpeg[" + widen(filename_) + L"]";\r
}\r
\r
- std::shared_ptr<AVStream> add_video_stream(enum CodecID codec_id, const std::vector<std::string>& options)\r
+ std::shared_ptr<AVStream> add_video_stream(CodecID vcodec, std::vector<option>& options)\r
{ \r
auto st = av_new_stream(oc_.get(), 0);\r
if (!st) \r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Could not allocate video-stream") << boost::errinfo_api_function("av_new_stream")); \r
\r
- auto encoder = avcodec_find_encoder(codec_id);\r
+ auto encoder = avcodec_find_encoder(vcodec);\r
if (!encoder)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("codec not found"));\r
\r
\r
avcodec_get_context_defaults3(c, encoder);\r
\r
- c->codec_id = codec_id;\r
+ c->codec_id = vcodec;\r
c->codec_type = AVMEDIA_TYPE_VIDEO;\r
c->width = format_desc_.width;\r
c->height = format_desc_.height;\r
else if(c->codec_id == CODEC_ID_DVVIDEO)\r
{\r
c->bit_rate = format_desc_.width < 1280 ? 50*1000000 : 100*1000000;\r
- c->pix_fmt = PIX_FMT_YUV422P;\r
- \r
c->width = format_desc_.height == 1280 ? 960 : c->width;\r
+ \r
+ //if(format_desc_.format == core::video_format::ntsc)\r
+ // c->pix_fmt = PIX_FMT_YUV411P;\r
+ //else if(format_desc_.format == core::video_format::pal)\r
+ // c->pix_fmt = PIX_FMT_YUV420P;\r
+\r
+ c->pix_fmt = PIX_FMT_YUV411P;\r
\r
+ if(c->bit_rate >= 50*1000000) // dv50\r
+ c->pix_fmt = PIX_FMT_YUV422P;\r
+ \r
if(format_desc_.duration == 1001) \r
c->width = format_desc_.height == 1080 ? 1280 : c->width; \r
else\r
{\r
BOOST_THROW_EXCEPTION(invalid_argument() << msg_info("Unsupported output parameters."));\r
}\r
+\r
+ parse_pix_fmt(c, options);\r
\r
c->max_b_frames = 0; // b-frames not supported.\r
\r
- for(size_t n = 0; n < options.size()/2; ++n)\r
- THROW_ON_ERROR2(av_opt_set(c, options[n*2+0].c_str(), options[n*2+1].c_str(), AV_OPT_SEARCH_CHILDREN), "[ffmpeg_consumer]");\r
- \r
+ BOOST_FOREACH(auto& option, options)\r
+ THROW_ON_ERROR2(av_opt_set(c, option.name.c_str(), option.value.c_str(), AV_OPT_SEARCH_CHILDREN), "[ffmpeg_consumer]");\r
+ \r
if(oc_->oformat->flags & AVFMT_GLOBALHEADER)\r
c->flags |= CODEC_FLAG_GLOBAL_HEADER;\r
\r
struct ffmpeg_consumer_proxy : public core::frame_consumer\r
{\r
const std::wstring filename_;\r
- const bool key_only_;\r
- const std::wstring codec_;\r
- const std::vector<std::string> options_;\r
+ const std::vector<option> options_;\r
\r
std::unique_ptr<ffmpeg_consumer> consumer_;\r
\r
public:\r
\r
- ffmpeg_consumer_proxy(const std::wstring& filename, bool key_only, const std::wstring codec, const std::vector<std::string>& options)\r
+ ffmpeg_consumer_proxy(const std::wstring& filename, const std::vector<option>& options)\r
: filename_(filename)\r
- , key_only_(key_only)\r
- , codec_(boost::to_lower_copy(codec))\r
, options_(options)\r
{\r
}\r
virtual void initialize(const core::video_format_desc& format_desc, int)\r
{\r
consumer_.reset();\r
- consumer_.reset(new ffmpeg_consumer(narrow(filename_), format_desc, narrow(codec_), options_));\r
+ consumer_.reset(new ffmpeg_consumer(narrow(filename_), format_desc, options_));\r
}\r
\r
virtual bool send(const safe_ptr<core::read_frame>& frame) override\r
{\r
boost::property_tree::wptree info;\r
info.add(L"type", L"ffmpeg-consumer");\r
- info.add(L"key-only", key_only_);\r
info.add(L"filename", filename_);\r
- info.add(L"vcodec", codec_);\r
return info;\r
}\r
\r
return core::frame_consumer::empty();\r
\r
auto filename = (params.size() > 1 ? params[1] : L"");\r
- bool key_only = get_param(L"KEY_ONLY", params, false);\r
- auto codec = get_param(L"-VCODEC", params, get_param(L"CODEC", params, L"libx264"));\r
-\r
- std::vector<std::string> options;\r
- auto opt_it = std::find(params.begin(), params.end(), L"-VCODEC");\r
-\r
- if(std::distance(opt_it, params.end()) > 2)\r
+ \r
+ std::vector<option> options;\r
+ \r
+ if(params.size() >= 3)\r
{\r
- std::advance(opt_it, 2);\r
- while(opt_it != params.end())\r
+ for(auto opt_it = params.begin()+2; opt_it != params.end();)\r
{\r
- auto str = boost::trim_copy(boost::to_lower_copy(*opt_it++));\r
- if(str.size() > 0 && str.at(0) == L'-')\r
- str = str.substr(1);\r
- options.push_back(narrow(str)); \r
+ auto name = narrow(boost::trim_copy(boost::to_lower_copy(*opt_it++))).substr(1);\r
+ auto value = narrow(boost::trim_copy(boost::to_lower_copy(*opt_it++)));\r
+ \r
+ if(value == "h264")\r
+ value = "libx264";\r
+ else if(value == "dvcpro")\r
+ value = "dvvideo";\r
+\r
+ options.push_back(option(name, value));\r
}\r
}\r
\r
- if(codec == L"H264")\r
- codec = L"libx264";\r
-\r
- if(codec == L"DVCPRO")\r
- codec = L"dvvideo";\r
-\r
- codec = boost::to_lower_copy(codec);\r
- \r
- return make_safe<ffmpeg_consumer_proxy>(env::media_folder() + filename, key_only, codec, options);\r
+ return make_safe<ffmpeg_consumer_proxy>(env::media_folder() + filename, options);\r
}\r
\r
safe_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptree& ptree)\r
{\r
auto filename = ptree.get<std::wstring>(L"path");\r
- auto key_only = ptree.get(L"key-only", false);\r
auto codec = ptree.get(L"vcodec", L"libx264");\r
+\r
+ std::vector<option> options;\r
+ options.push_back(option("vcodec", narrow(codec)));\r
\r
- return make_safe<ffmpeg_consumer_proxy>(env::media_folder() + filename, key_only, codec, std::vector<std::string>());\r
+ return make_safe<ffmpeg_consumer_proxy>(env::media_folder() + filename, options);\r
}\r
\r
}}\r