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

modules/ffmpeg/consumer/ffmpeg_consumer.cpp

index bf68377fe1e92af0667d2a45e131d5b50429b407..a025598987cc2f825e7e8c2fc7c6b64953049521 100644 (file)
@@ -60,9 +60,96 @@ extern "C"
 \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::wstring                                              filename_;\r
+       const std::string                                               filename_;\r
                \r
        const std::shared_ptr<AVFormatContext>  oc_;\r
        const core::video_format_desc                   format_desc_;\r
@@ -87,7 +174,7 @@ struct ffmpeg_consumer : boost::noncopyable
        int64_t                                                                 frame_number_;\r
        \r
 public:\r
-       ffmpeg_consumer(const std::wstring& filename, const core::video_format_desc& format_desc, const std::wstring& codec, const std::vector<std::wstring>& 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
@@ -97,7 +184,7 @@ public:
                , frame_number_(0)\r
        {\r
                // TODO: Ask stakeholders about case where file already exists.\r
-               boost::filesystem::remove(boost::filesystem::path(env::media_folder() + filename)); // Delete the file if it exists\r
+               boost::filesystem::remove(boost::filesystem::path(env::media_folder() + u16(filename))); // Delete the file if it exists\r
 \r
                graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));\r
                graph_->set_color("write-time", diagnostics::color(0.5f, 0.5f, 0.1f));\r
@@ -107,38 +194,39 @@ public:
                executor_.set_capacity(8);\r
                file_write_executor_.set_capacity(8);\r
 \r
-               oc_->oformat = av_guess_format(nullptr, u8(filename_).c_str(), nullptr);\r
+               oc_->oformat = av_guess_format(nullptr, filename_.c_str(), nullptr);\r
                if (!oc_->oformat)\r
                        BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Could not find suitable output format."));\r
                \r
-               THROW_ON_ERROR2(av_set_parameters(oc_.get(), nullptr), "[ffmpeg_consumer]");\r
+               parse_format(oc_->oformat, options);\r
 \r
-               strcpy_s(oc_->filename, u8(filename_).c_str());\r
+               auto vcodec = oc_->oformat->video_codec;\r
+               parse_vcodec(vcodec, options);\r
                \r
-               auto video_codec = avcodec_find_encoder_by_name(u8(codec).c_str());\r
-               if(video_codec == nullptr)\r
-                       BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info(codec));\r
+               THROW_ON_ERROR2(av_set_parameters(oc_.get(), nullptr), "[ffmpeg_consumer]");\r
 \r
+               strcpy_s(oc_->filename, filename_.c_str());\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, u8(filename_).c_str(), 1);\r
+               dump_format(oc_.get(), 0, filename_.c_str(), 1);\r
                 \r
                // Open the output ffmpeg, if needed.\r
                if (!(oc_->oformat->flags & AVFMT_NOFILE)) \r
-                       THROW_ON_ERROR2(avio_open(&oc_->pb, u8(filename_).c_str(), URL_WRONLY), "[ffmpeg_consumer]");\r
+                       THROW_ON_ERROR2(avio_open(&oc_->pb, filename_.c_str(), URL_WRONLY), "[ffmpeg_consumer]");\r
                                \r
                THROW_ON_ERROR2(av_write_header(oc_.get()), "[ffmpeg_consumer]");\r
+\r
+               CASPAR_LOG(info) << print() << L" Successfully Initialized.";   \r
        }\r
 \r
        ~ffmpeg_consumer()\r
        {    \r
-               executor_.wait();\r
                executor_.stop();\r
                executor_.join();\r
 \r
-               file_write_executor_.wait();\r
                file_write_executor_.stop();\r
                file_write_executor_.join();\r
                \r
@@ -149,20 +237,22 @@ public:
                          \r
                if (!(oc_->oformat->flags & AVFMT_NOFILE)) \r
                        LOG_ON_ERROR2(avio_close(oc_->pb), "[ffmpeg_consumer]"); // Close the output ffmpeg.\r
+\r
+               CASPAR_LOG(info) << print() << L" Successfully Uninitialized."; \r
        }\r
                        \r
        std::wstring print() const\r
        {\r
-               return L"ffmpeg[" + filename_ + L"]";\r
+               return L"ffmpeg[" + u16(filename_) + L"]";\r
        }\r
 \r
-       std::shared_ptr<AVStream> add_video_stream(enum CodecID codec_id, const std::vector<std::wstring>& 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
@@ -170,7 +260,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
@@ -195,10 +285,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
@@ -222,12 +320,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, u8(options[n*2+0]).c_str(), u8(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
@@ -300,7 +400,7 @@ public:
                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;\r
                av_frame->pts                           = frame_number_++;\r
-               \r
+\r
                int out_size = THROW_ON_ERROR2(avcodec_encode_video(c, video_outbuf_.data(), static_cast<int>(video_outbuf_.size()), av_frame.get()), "[ffmpeg_consumer]");\r
                if(out_size > 0)\r
                {\r
@@ -343,10 +443,10 @@ public:
                if (c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE)\r
                        pkt->pts = av_rescale_q(c->coded_frame->pts, c->time_base, audio_st_->time_base);\r
 \r
-               pkt->flags               |= AV_PKT_FLAG_KEY;\r
-               pkt->stream_index = audio_st_->index;\r
-               pkt->size                = static_cast<int>(audio_data.size()*2);\r
-               pkt->data                = reinterpret_cast<uint8_t*>(audio_data.data());\r
+               pkt->flags                      |= AV_PKT_FLAG_KEY;\r
+               pkt->stream_index       = audio_st_->index;\r
+               pkt->size                       = static_cast<int>(audio_data.size()*2);\r
+               pkt->data                       = reinterpret_cast<uint8_t*>(audio_data.data());\r
                \r
                av_dup_packet(pkt.get());\r
                return pkt;\r
@@ -381,18 +481,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::wstring> 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::wstring>& options)\r
+       ffmpeg_consumer_proxy(const std::wstring& filename, const std::vector<option>& options)\r
                : filename_(filename)\r
-               , key_only_(key_only)\r
-               , codec_(std::move(codec))\r
                , options_(options)\r
        {\r
        }\r
@@ -400,7 +496,7 @@ public:
        virtual void initialize(const core::video_format_desc& format_desc, int)\r
        {\r
                consumer_.reset();\r
-               consumer_.reset(new ffmpeg_consumer(filename_, format_desc, codec_, options_));\r
+               consumer_.reset(new ffmpeg_consumer(u8(filename_), format_desc, options_));\r
        }\r
        \r
        virtual bool send(const spl::shared_ptr<const core::data_frame>& frame) override\r
@@ -423,9 +519,7 @@ public:
        {\r
                boost::property_tree::wptree info;\r
                info.add(L"type", L"file");\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
@@ -451,42 +545,37 @@ spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wst
                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::wstring> 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 = *opt_it++;\r
-                       if(str.size() > 0 && str.at(0) == L'-')\r
-                               str = str.substr(1);\r
-                       options.push_back(boost::to_lower_copy(str));           \r
+                       auto name  = u8(boost::trim_copy(boost::to_lower_copy(*opt_it++))).substr(1);\r
+                       auto value = u8(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 spl::make_shared<ffmpeg_consumer_proxy>(env::media_folder() + filename, key_only, codec, options);\r
+       return spl::make_shared<ffmpeg_consumer_proxy>(env::media_folder() + filename, options);\r
 }\r
 \r
 spl::shared_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", u8(codec)));\r
        \r
-       return spl::make_shared<ffmpeg_consumer_proxy>(env::media_folder() + filename, key_only, codec, std::vector<std::wstring>());\r
+       return spl::make_shared<ffmpeg_consumer_proxy>(env::media_folder() + filename, options);\r
 }\r
 \r
 }}\r