#include <core/consumer/frame_consumer.h>\r
#include <core/video_format.h>\r
\r
+#include <common/array.h>\r
#include <common/env.h>\r
-#include <common/utf.h>\r
-#include <common/param.h>\r
+#include <common/except.h>\r
#include <common/executor.h>\r
#include <common/diagnostics/graph.h>\r
-#include <common/array.h>\r
+#include <common/lock.h>\r
#include <common/memory.h>\r
+#include <common/param.h>\r
+#include <common/utf.h>\r
\r
#include <boost/algorithm/string.hpp>\r
#include <boost/timer.hpp>\r
#include <boost/range/algorithm_ext.hpp>\r
#include <boost/lexical_cast.hpp>\r
\r
+#include <tbb/spin_mutex.h>\r
+\r
#if defined(_MSC_VER)\r
#pragma warning (push)\r
#pragma warning (disable : 4244)\r
const core::video_format_desc format_desc_;\r
\r
const spl::shared_ptr<diagnostics::graph> graph_;\r
-\r
+ \r
+ tbb::spin_mutex exception_mutex_;\r
+ std::exception_ptr exception_;\r
\r
std::shared_ptr<AVStream> audio_st_;\r
std::shared_ptr<AVStream> video_st_;\r
return;\r
\r
auto enc = video_st_->codec;\r
-\r
- try\r
- { \r
- auto av_frame = convert_video(frame, enc);\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
- AVPacket pkt;\r
- av_init_packet(&pkt);\r
- pkt.data = nullptr;\r
- pkt.size = 0;\r
-\r
- int got_packet = 0;\r
- THROW_ON_ERROR2(avcodec_encode_video2(enc, &pkt, av_frame.get(), &got_packet), "[ffmpeg_consumer]");\r
- std::shared_ptr<AVPacket> guard(&pkt, av_free_packet);\r
-\r
- if(!got_packet)\r
- return;\r
+ \r
+ auto av_frame = convert_video(frame, enc);\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
+ AVPacket pkt;\r
+ av_init_packet(&pkt);\r
+ pkt.data = nullptr;\r
+ pkt.size = 0;\r
+\r
+ int got_packet = 0;\r
+ THROW_ON_ERROR2(avcodec_encode_video2(enc, &pkt, av_frame.get(), &got_packet), "[ffmpeg_consumer]");\r
+ std::shared_ptr<AVPacket> guard(&pkt, av_free_packet);\r
+\r
+ if(!got_packet)\r
+ return;\r
\r
- if (pkt.pts != AV_NOPTS_VALUE)\r
- pkt.pts = av_rescale_q(pkt.pts, enc->time_base, video_st_->time_base);\r
- if (pkt.dts != AV_NOPTS_VALUE)\r
- pkt.dts = av_rescale_q(pkt.dts, enc->time_base, video_st_->time_base);\r
+ if (pkt.pts != AV_NOPTS_VALUE)\r
+ pkt.pts = av_rescale_q(pkt.pts, enc->time_base, video_st_->time_base);\r
+ if (pkt.dts != AV_NOPTS_VALUE)\r
+ pkt.dts = av_rescale_q(pkt.dts, enc->time_base, video_st_->time_base);\r
\r
- pkt.stream_index = video_st_->index;\r
+ pkt.stream_index = video_st_->index;\r
\r
- THROW_ON_ERROR2(av_interleaved_write_frame(oc_.get(), &pkt), "[ffmpeg_consumer]");\r
- }\r
- catch(...)\r
- {\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- executor_.stop();\r
- }\r
+ THROW_ON_ERROR2(av_interleaved_write_frame(oc_.get(), &pkt), "[ffmpeg_consumer]");\r
}\r
\r
uint64_t get_channel_layout(AVCodecContext* dec)\r
\r
auto enc = audio_st_->codec;\r
\r
- try\r
- {\r
- boost::push_back(audio_buffer_, convert_audio(frame, enc));\r
+ boost::push_back(audio_buffer_, convert_audio(frame, enc));\r
\r
- auto frame_size = enc->frame_size != 0 ? enc->frame_size * enc->channels * av_get_bytes_per_sample(enc->sample_fmt) : static_cast<int>(audio_buffer_.size());\r
+ auto frame_size = enc->frame_size != 0 ? enc->frame_size * enc->channels * av_get_bytes_per_sample(enc->sample_fmt) : static_cast<int>(audio_buffer_.size());\r
\r
- while(audio_buffer_.size() >= frame_size)\r
- { \r
- std::shared_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);\r
- avcodec_get_frame_defaults(av_frame.get()); \r
- av_frame->nb_samples = frame_size / (enc->channels * av_get_bytes_per_sample(enc->sample_fmt));\r
-\r
- AVPacket pkt;\r
- av_init_packet(&pkt);\r
- pkt.data = nullptr;\r
- pkt.size = 0; \r
+ while(audio_buffer_.size() >= frame_size)\r
+ { \r
+ std::shared_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);\r
+ avcodec_get_frame_defaults(av_frame.get()); \r
+ av_frame->nb_samples = frame_size / (enc->channels * av_get_bytes_per_sample(enc->sample_fmt));\r
+\r
+ AVPacket pkt;\r
+ av_init_packet(&pkt);\r
+ pkt.data = nullptr;\r
+ pkt.size = 0; \r
\r
- THROW_ON_ERROR2(avcodec_fill_audio_frame(av_frame.get(), enc->channels, enc->sample_fmt, audio_buffer_.data(), frame_size, 1), "[ffmpeg_consumer]");\r
+ THROW_ON_ERROR2(avcodec_fill_audio_frame(av_frame.get(), enc->channels, enc->sample_fmt, audio_buffer_.data(), frame_size, 1), "[ffmpeg_consumer]");\r
\r
- int got_packet = 0;\r
- THROW_ON_ERROR2(avcodec_encode_audio2(enc, &pkt, av_frame.get(), &got_packet), "[ffmpeg_consumer]");\r
- std::shared_ptr<AVPacket> guard(&pkt, av_free_packet);\r
+ int got_packet = 0;\r
+ THROW_ON_ERROR2(avcodec_encode_audio2(enc, &pkt, av_frame.get(), &got_packet), "[ffmpeg_consumer]");\r
+ std::shared_ptr<AVPacket> guard(&pkt, av_free_packet);\r
\r
- audio_buffer_.erase(audio_buffer_.begin(), audio_buffer_.begin() + frame_size);\r
+ audio_buffer_.erase(audio_buffer_.begin(), audio_buffer_.begin() + frame_size);\r
\r
- if(!got_packet)\r
- return;\r
+ if(!got_packet)\r
+ return;\r
\r
- if (pkt.pts != AV_NOPTS_VALUE)\r
- pkt.pts = av_rescale_q(pkt.pts, enc->time_base, audio_st_->time_base);\r
- if (pkt.dts != AV_NOPTS_VALUE)\r
- pkt.dts = av_rescale_q(pkt.dts, enc->time_base, audio_st_->time_base);\r
- if (pkt.duration > 0)\r
- pkt.duration = static_cast<int>(av_rescale_q(pkt.duration, enc->time_base, audio_st_->time_base));\r
+ if (pkt.pts != AV_NOPTS_VALUE)\r
+ pkt.pts = av_rescale_q(pkt.pts, enc->time_base, audio_st_->time_base);\r
+ if (pkt.dts != AV_NOPTS_VALUE)\r
+ pkt.dts = av_rescale_q(pkt.dts, enc->time_base, audio_st_->time_base);\r
+ if (pkt.duration > 0)\r
+ pkt.duration = static_cast<int>(av_rescale_q(pkt.duration, enc->time_base, audio_st_->time_base));\r
\r
- pkt.stream_index = audio_st_->index;\r
+ pkt.stream_index = audio_st_->index;\r
\r
- THROW_ON_ERROR2(av_interleaved_write_frame(oc_.get(), &pkt), "[ffmpeg_consumer]");\r
- }\r
- }\r
- catch(...)\r
- {\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- executor_.stop();\r
+ THROW_ON_ERROR2(av_interleaved_write_frame(oc_.get(), &pkt), "[ffmpeg_consumer]");\r
}\r
} \r
\r
return buffer;\r
}\r
\r
- void send(core::const_frame& frame)\r
+ void encode(const core::const_frame& frame)\r
{\r
- executor_.begin_invoke([=]\r
- { \r
+ try\r
+ {\r
boost::timer frame_timer;\r
\r
encode_video_frame(frame);\r
encode_audio_frame(frame);\r
\r
graph_->set_value("frame-time", frame_timer.elapsed()*format_desc_.fps*0.5);\r
+ }\r
+ catch(...)\r
+ { \r
+ lock(exception_mutex_, [&]\r
+ {\r
+ exception_ = std::current_exception();\r
+ });\r
+ executor_.stop();\r
+ }\r
+ }\r
+\r
+ bool send(core::const_frame& frame)\r
+ {\r
+ auto exception = lock(exception_mutex_, [&]\r
+ {\r
+ return exception_;\r
});\r
+\r
+ if(exception != nullptr)\r
+ std::rethrow_exception(exception);\r
+ \r
+ executor_.begin_invoke([=]\r
+ { \r
+ encode(frame);\r
+ });\r
+ \r
+ return true;\r
}\r
};\r
\r
\r
bool send(core::const_frame frame) override\r
{\r
- consumer_->send(frame);\r
- return true;\r
+ return consumer_->send(frame);\r
}\r
\r
std::wstring print() const override\r