]> git.sesse.net Git - casparcg/commitdiff
2.0: -ffmpeg_consumer: Added support for -f, -pix_fmt options.
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Mon, 13 Feb 2012 18:28:43 +0000 (18:28 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Mon, 13 Feb 2012 18:28:43 +0000 (18:28 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/trunk@2390 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

modules/ffmpeg/consumer/ffmpeg_consumer.cpp

index ddf08795e0ff19b96784d918c1a08dc87b18a579..37d290a66abd6a7bac67ac6c51930e6389d63021 100644 (file)
@@ -43,6 +43,8 @@
 #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
@@ -54,13 +56,101 @@ extern "C"
        #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
@@ -88,7 +178,7 @@ struct ffmpeg_consumer : boost::noncopyable
        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
@@ -112,16 +202,17 @@ public:
                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
@@ -159,13 +250,13 @@ public:
                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
@@ -173,7 +264,7 @@ public:
 \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
@@ -198,10 +289,18 @@ public:
                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
@@ -225,12 +324,14 @@ public:
                {\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
@@ -384,18 +485,14 @@ public:
 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
@@ -403,7 +500,7 @@ public:
        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
@@ -421,9 +518,7 @@ public:
        {\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
@@ -449,42 +544,37 @@ safe_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>&
                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