#include "../StdAfx.h"\r
\r
#include "../ffmpeg_error.h"\r
-#include "../producer/tbb_avcodec.h"\r
\r
#include "ffmpeg_consumer.h"\r
\r
#include <core/video_format.h>\r
\r
#include <common/concurrency/executor.h>\r
+#include <common/diagnostics/graph.h>\r
#include <common/utility/string.h>\r
#include <common/env.h>\r
\r
+#include <boost/timer.hpp>\r
#include <boost/thread/once.hpp>\r
+#include <boost/thread.hpp>\r
#include <boost/algorithm/string.hpp>\r
\r
#include <tbb/cache_aligned_allocator.h>\r
const std::shared_ptr<AVFormatContext> oc_;\r
const core::video_format_desc format_desc_;\r
\r
+ const safe_ptr<diagnostics::graph> graph_;\r
+ boost::timer frame_timer_;\r
+ boost::timer write_timer_;\r
+\r
executor executor_;\r
+ executor file_write_executor_;\r
\r
// Audio\r
std::shared_ptr<AVStream> audio_st_;\r
, oc_(avformat_alloc_context(), av_free)\r
, format_desc_(format_desc)\r
, executor_(print())\r
+ , file_write_executor_(print() + L"/output")\r
{\r
- executor_.set_capacity(25);\r
- \r
+ graph_->add_guide("frame-time", 0.5);\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
+ graph_->set_text(print());\r
+ diagnostics::register_graph(graph_);\r
+\r
+ executor_.set_capacity(8);\r
+ file_write_executor_.set_capacity(8);\r
+\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
~ffmpeg_consumer()\r
{ \r
+ file_write_executor_.stop();\r
+ file_write_executor_.join();\r
+\r
executor_.stop();\r
executor_.join();\r
\r
<< msg_info("Could not alloc video-stream") \r
<< boost::errinfo_api_function("av_new_stream"));\r
}\r
+\r
+ bitrate *= 1000000;\r
\r
st->codec->codec_id = codec_id;\r
+ st->codec->bit_rate = bitrate > 0 ? bitrate : format_desc_.width < 1280 ? 42*1000000 : 147*1000000;\r
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;\r
st->codec->width = format_desc_.width;\r
st->codec->height = format_desc_.height;\r
}\r
else if(st->codec->codec_id == CODEC_ID_DNXHD)\r
{\r
+ if(format_desc_.width < 1280 || format_desc_.height < 720)\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("unsupported dimension"));\r
+\r
st->codec->bit_rate = bitrate > 0 ? bitrate : 145*1000000;\r
- st->codec->width = std::min<size_t>(1280, format_desc_.width);\r
- st->codec->height = std::min<size_t>(720, format_desc_.height);\r
+ st->codec->pix_fmt = PIX_FMT_YUV422P;\r
+\r
+ }\r
+ else if(st->codec->codec_id == CODEC_ID_DVVIDEO)\r
+ {\r
+ st->codec->bit_rate = bitrate > 0 ? bitrate : format_desc_.width < 1280 ? 50*1000000 : 100*1000000;\r
st->codec->pix_fmt = PIX_FMT_YUV422P;\r
}\r
+ //else if(st->codec->codec_id == CODEC_ID_H264)\r
+ //{ \r
+ // st->codec->pix_fmt = PIX_FMT_YUV422P;\r
+ // st->codec->me_range = 16;\r
+ // st->codec->max_qdiff = 4;\r
+ // st->codec->qmin = 10;\r
+ // st->codec->qmax = 51;\r
+ // st->codec->qcompress = 0.6;\r
+ // st->codec->gop_size = 25;\r
+ //}\r
else\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("unsupported codec"));\r
\r
if (!codec)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("codec not found"));\r
\r
- THROW_ON_ERROR2(tbb_avcodec_open(st->codec, codec), "[ffmpeg_consumer]");\r
+ st->codec->thread_count = boost::thread::hardware_concurrency();\r
+ THROW_ON_ERROR2(avcodec_open(st->codec, codec), "[ffmpeg_consumer]");\r
\r
return std::shared_ptr<AVStream>(st, [](AVStream* st)\r
{\r
- tbb_avcodec_close(st->codec);\r
+ avcodec_close(st->codec);\r
});\r
}\r
\r
<< boost::errinfo_api_function("av_new_stream"));\r
}\r
\r
- st->codec->codec_id = CODEC_ID_PCM_S16LE;\r
- st->codec->codec_type = AVMEDIA_TYPE_AUDIO;\r
- st->codec->sample_rate = 48000;\r
- st->codec->channels = 2;\r
- st->codec->sample_fmt = SAMPLE_FMT_S16;\r
+ st->codec->codec_id = CODEC_ID_PCM_S16LE;\r
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;\r
+ st->codec->sample_rate = 48000;\r
+ st->codec->channels = 2;\r
+ st->codec->sample_fmt = SAMPLE_FMT_S16;\r
\r
if(oc_->oformat->flags & AVFMT_GLOBALHEADER)\r
st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;\r
});\r
}\r
\r
- void encode_video_frame(const safe_ptr<core::read_frame>& frame)\r
+ std::shared_ptr<AVPacket> encode_video_frame(const safe_ptr<core::read_frame>& frame)\r
{ \r
auto c = video_st_->codec;\r
\r
int out_size = THROW_ON_ERROR2(avcodec_encode_video(c, video_outbuf_.data(), video_outbuf_.size(), local_av_frame.get()), "[ffmpeg_consumer]");\r
if(out_size > 0)\r
{\r
- AVPacket pkt;\r
- av_init_packet(&pkt);\r
+ safe_ptr<AVPacket> pkt(new AVPacket, [](AVPacket* p)\r
+ {\r
+ av_free_packet(p);\r
+ delete p;\r
+ });\r
+ av_init_packet(pkt.get());\r
\r
if (c->coded_frame->pts != AV_NOPTS_VALUE)\r
- pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, video_st_->time_base);\r
+ pkt->pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_st_->time_base);\r
\r
if(c->coded_frame->key_frame)\r
- pkt.flags |= AV_PKT_FLAG_KEY;\r
+ pkt->flags |= AV_PKT_FLAG_KEY;\r
\r
- pkt.stream_index = video_st_->index;\r
- pkt.data = video_outbuf_.data();\r
- pkt.size = out_size;\r
+ pkt->stream_index = video_st_->index;\r
+ pkt->data = video_outbuf_.data();\r
+ pkt->size = out_size;\r
\r
- THROW_ON_ERROR2(av_interleaved_write_frame(oc_.get(), &pkt), L"[ffmpeg_consumer]");\r
+ av_dup_packet(pkt.get());\r
+ return pkt;\r
} \r
+ return nullptr;\r
}\r
\r
- void encode_audio_frame(const safe_ptr<core::read_frame>& frame)\r
+ std::shared_ptr<AVPacket> encode_audio_frame(const safe_ptr<core::read_frame>& frame)\r
{ \r
auto c = audio_st_->codec;\r
\r
auto audio_data = core::audio_32_to_16(frame->audio_data());\r
-\r
- AVPacket pkt;\r
- av_init_packet(&pkt);\r
+ \r
+ safe_ptr<AVPacket> pkt(new AVPacket, [](AVPacket* p)\r
+ {\r
+ av_free_packet(p);\r
+ delete p;\r
+ });\r
+ av_init_packet(pkt.get());\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
+ 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 = 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 = audio_data.size()*2;\r
+ pkt->data = reinterpret_cast<uint8_t*>(audio_data.data());\r
\r
- THROW_ON_ERROR2(av_interleaved_write_frame(oc_.get(), &pkt), L"[ffmpeg_consumer]");\r
+ av_dup_packet(pkt.get());\r
+ return pkt;\r
}\r
\r
void send(const safe_ptr<core::read_frame>& frame)\r
{\r
executor_.begin_invoke([=]\r
- { \r
- encode_video_frame(frame);\r
- encode_audio_frame(frame);\r
+ { \r
+ frame_timer_.restart();\r
+ auto video = encode_video_frame(frame);\r
+ auto audio = encode_audio_frame(frame);\r
+ graph_->update_value("frame-time", frame_timer_.elapsed()*format_desc_.fps*0.5);\r
+ \r
+ file_write_executor_.begin_invoke([=]\r
+ {\r
+ write_timer_.restart();\r
+ if(video)\r
+ av_write_frame(oc_.get(), video.get());\r
+ if(audio)\r
+ av_write_frame(oc_.get(), audio.get());\r
+ graph_->update_value("write-time", write_timer_.elapsed()*format_desc_.fps*0.5);\r
+ });\r
});\r
}\r
};\r
\r
std::string codec = "dnxhd";\r
auto codec_it = std::find(params.begin(), params.end(), L"CODEC");\r
- if(codec_it++ != params.end())\r
+ if(codec_it != params.end() && codec_it++ != params.end())\r
codec = narrow(*codec_it);\r
\r
int bitrate = 0; \r
auto bitrate_it = std::find(params.begin(), params.end(), L"BITRATE");\r
- if(bitrate_it++ != params.end())\r
- bitrate = boost::lexical_cast<int>(*codec_it);\r
+ if(bitrate_it != params.end() && bitrate_it++ != params.end())\r
+ bitrate = boost::lexical_cast<int>(*bitrate_it);\r
\r
return make_safe<ffmpeg_consumer_proxy>(env::media_folder() + params[1], key_only, codec, bitrate);\r
}\r