\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
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
, 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
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
\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
\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, 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
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
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
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
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
{\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
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