\r
#include <common/env.h>\r
\r
+#include <boost/range/algorithm_ext/push_back.hpp>\r
+\r
namespace caspar { namespace core {\r
\r
struct display_mode\r
\r
struct frame_muxer::implementation\r
{ \r
- std::queue<safe_ptr<write_frame>> video_frames_;\r
- std::queue<std::vector<int16_t>> audio_chunks_;\r
- std::queue<safe_ptr<basic_frame>> frame_buffer_;\r
- display_mode::type display_mode_;\r
- const double in_fps_;\r
- const double out_fps_;\r
- const video_mode::type out_mode_;\r
- bool auto_mode_;\r
-\r
- implementation(double in_fps, const core::video_mode::type out_mode, double out_fps)\r
+ std::queue<safe_ptr<write_frame>> video_frames_;\r
+ std::vector<int16_t> audio_samples_;\r
+ std::queue<safe_ptr<basic_frame>> frame_buffer_;\r
+ display_mode::type display_mode_;\r
+ const double in_fps_;\r
+ const video_format_desc format_desc_;\r
+ bool auto_mode_;\r
+ \r
+ implementation(double in_fps, const video_format_desc& format_desc)\r
: display_mode_(display_mode::invalid)\r
, in_fps_(in_fps)\r
- , out_fps_(out_fps)\r
- , out_mode_(out_mode)\r
+ , format_desc_(format_desc)\r
, auto_mode_(env::properties().get("configuration.auto-mode", false))\r
{\r
}\r
\r
- void push(const safe_ptr<write_frame>& video_frame)\r
+ void push(const std::shared_ptr<write_frame>& video_frame)\r
{ \r
+ if(!video_frame)\r
+ return;\r
+\r
// Fix field-order if needed\r
- if(video_frame->get_type() == core::video_mode::lower && out_mode_ == core::video_mode::upper)\r
+ if(video_frame->get_type() == core::video_mode::lower && format_desc_.mode == core::video_mode::upper)\r
video_frame->get_image_transform().set_fill_translation(0.0f, 0.5/static_cast<double>(video_frame->get_pixel_format_desc().planes[0].height));\r
- else if(video_frame->get_type() == core::video_mode::upper && out_mode_ == core::video_mode::lower)\r
+ else if(video_frame->get_type() == core::video_mode::upper && format_desc_.mode == core::video_mode::lower)\r
video_frame->get_image_transform().set_fill_translation(0.0f, -0.5/static_cast<double>(video_frame->get_pixel_format_desc().planes[0].height));\r
\r
- video_frames_.push(video_frame);\r
+ video_frames_.push(make_safe(video_frame));\r
\r
process();\r
}\r
\r
- void push(const std::vector<int16_t>& audio_chunk)\r
+ void push(const std::shared_ptr<std::vector<int16_t>>& audio_samples)\r
{\r
- audio_chunks_.push(audio_chunk);\r
+ if(!audio_samples)\r
+ return;\r
+\r
+ boost::range::push_back(audio_samples_, *audio_samples);\r
process();\r
}\r
\r
return frame_buffer_.size();\r
}\r
\r
+ safe_ptr<core::write_frame> pop_video()\r
+ {\r
+ auto frame = video_frames_.front();\r
+ video_frames_.pop();\r
+ return frame;\r
+ }\r
+\r
+ std::vector<int16_t> pop_audio()\r
+ {\r
+ CASPAR_VERIFY(audio_samples_.size() >= format_desc_.audio_samples_per_frame);\r
+\r
+ auto begin = audio_samples_.begin();\r
+ auto end = begin + format_desc_.audio_samples_per_frame;\r
+\r
+ auto samples = std::vector<int16_t>(begin, end);\r
+ audio_samples_ = std::vector<int16_t>(end, audio_samples_.end());\r
+\r
+ return samples;\r
+ }\r
+\r
void process()\r
{\r
- if(video_frames_.empty() || audio_chunks_.empty())\r
+ if(video_frames_.empty() || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
\r
if(display_mode_ == display_mode::invalid)\r
- display_mode_ = auto_mode_ ? get_display_mode(video_frames_.front()->get_type(), in_fps_, out_mode_, out_fps_) : display_mode::simple;\r
+ display_mode_ = auto_mode_ ? get_display_mode(video_frames_.front()->get_type(), in_fps_, format_desc_.mode, format_desc_.fps) : display_mode::simple;\r
\r
switch(display_mode_)\r
{\r
\r
void simple()\r
{\r
- if(video_frames_.empty() || audio_chunks_.empty())\r
+ if(video_frames_.empty() || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
\r
- auto frame1 = video_frames_.front();\r
- video_frames_.pop();\r
-\r
- frame1->audio_data() = audio_chunks_.front();\r
- audio_chunks_.pop();\r
+ auto frame1 = pop_video();\r
+ frame1->audio_data() = pop_audio();\r
\r
frame_buffer_.push(frame1);\r
}\r
\r
void duplicate()\r
{ \r
- if(video_frames_.empty() || audio_chunks_.size() < 2)\r
+ if(video_frames_.empty() || audio_samples_.size()/2 < format_desc_.audio_samples_per_frame)\r
return;\r
\r
- auto frame = video_frames_.front();\r
- video_frames_.pop();\r
+ auto frame = pop_video();\r
\r
auto frame1 = make_safe<core::write_frame>(*frame); // make a copy\r
- frame1->audio_data() = audio_chunks_.front();\r
- audio_chunks_.pop();\r
+ frame1->audio_data() = pop_audio();\r
\r
auto frame2 = frame;\r
- frame2->audio_data() = audio_chunks_.front();\r
- audio_chunks_.pop();\r
+ frame2->audio_data() = pop_audio();\r
\r
frame_buffer_.push(frame1);\r
frame_buffer_.push(frame2);\r
\r
void half()\r
{ \r
- if(video_frames_.size() < 2 || audio_chunks_.empty())\r
+ if(video_frames_.size() < 2 || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
\r
- auto frame1 = video_frames_.front();\r
- video_frames_.pop();\r
- frame1->audio_data() = audio_chunks_.front();\r
- audio_chunks_.pop();\r
+ auto frame1 = pop_video();\r
+ frame1->audio_data() = pop_audio();\r
\r
video_frames_.pop(); // Throw away\r
\r
\r
void interlace()\r
{ \r
- if(video_frames_.size() < 2 || audio_chunks_.empty())\r
+ if(video_frames_.size() < 2 || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
return;\r
\r
- auto frame1 = video_frames_.front();\r
- video_frames_.pop();\r
+ auto frame1 = pop_video();\r
\r
- frame1->audio_data() = audio_chunks_.front();\r
- audio_chunks_.pop();\r
+ frame1->audio_data() = pop_audio();\r
\r
- auto frame2 = video_frames_.front();\r
- video_frames_.pop();\r
+ auto frame2 = pop_video();\r
\r
- frame_buffer_.push(core::basic_frame::interlace(frame1, frame2, out_mode_)); \r
+ frame_buffer_.push(core::basic_frame::interlace(frame1, frame2, format_desc_.mode)); \r
}\r
\r
void deinterlace()\r
}\r
};\r
\r
-frame_muxer::frame_muxer(double in_fps, const core::video_mode::type out_mode, double out_fps)\r
- : impl_(implementation(in_fps, out_mode, out_fps)){}\r
-void frame_muxer::push(const safe_ptr<write_frame>& video_frame){impl_->push(video_frame);}\r
-void frame_muxer::push(const std::vector<int16_t>& audio_chunk){return impl_->push(audio_chunk);}\r
+frame_muxer::frame_muxer(double in_fps, const video_format_desc& format_desc)\r
+ : impl_(implementation(in_fps, format_desc)){}\r
+void frame_muxer::push(const std::shared_ptr<write_frame>& video_frame){impl_->push(video_frame);}\r
+void frame_muxer::push(const std::shared_ptr<std::vector<int16_t>>& audio_samples){return impl_->push(audio_samples);}\r
safe_ptr<basic_frame> frame_muxer::pop(){return impl_->pop();}\r
size_t frame_muxer::size() const {return impl_->size();}\r
bool frame_muxer::empty() const {return impl_->size() == 0;}\r
size_t frame_muxer::video_frames() const{return impl_->video_frames_.size();}\r
-size_t frame_muxer::audio_chunks() const{return impl_->audio_chunks_.size();}\r
+size_t frame_muxer::audio_chunks() const{return impl_->audio_samples_.size() / impl_->format_desc_.audio_samples_per_frame;}\r
\r
}}
\ No newline at end of file
class frame_muxer\r
{\r
public:\r
- frame_muxer(double in_fps, const video_mode::type out_mode, double out_fps);\r
-\r
- void push(const safe_ptr<write_frame>& video_frame);\r
- void push(const std::vector<int16_t>& audio_chunk);\r
+ frame_muxer(double in_fps, const video_format_desc& format_desc);\r
+ \r
+ void push(const std::shared_ptr<write_frame>& video_frame);\r
+ void push(const std::shared_ptr<std::vector<int16_t>>& audio_samples);\r
\r
size_t video_frames() const;\r
size_t audio_chunks() const;\r
std::shared_ptr<diagnostics::graph> graph_;\r
boost::timer tick_timer_;\r
boost::timer frame_timer_;\r
-\r
- std::vector<int16_t> audio_samples_;\r
- \r
+ \r
safe_ptr<core::frame_factory> frame_factory_;\r
\r
tbb::concurrent_bounded_queue<safe_ptr<core::basic_frame>> frame_buffer_;\r
, frame_factory_(frame_factory)\r
, tail_(core::basic_frame::empty())\r
, filter_(filter)\r
- , muxer_(double_rate(filter) ? format_desc.fps * 2.0 : format_desc.fps, frame_factory->get_video_format_desc().mode, frame_factory->get_video_format_desc().fps)\r
+ , muxer_(double_rate(filter) ? format_desc.fps * 2.0 : format_desc.fps, frame_factory->get_video_format_desc())\r
{\r
frame_buffer_.set_capacity(2);\r
\r
{\r
auto sample_frame_count = audio->GetSampleFrameCount();\r
auto audio_data = reinterpret_cast<short*>(bytes);\r
- audio_samples_.insert(audio_samples_.end(), audio_data, audio_data + sample_frame_count*2);\r
-\r
- if(audio_samples_.size() > frame_factory_->get_video_format_desc().audio_samples_per_frame)\r
- {\r
- const auto begin = audio_samples_.begin();\r
- const auto end = begin + frame_factory_->get_video_format_desc().audio_samples_per_frame;\r
- muxer_.push(std::vector<int16_t>(begin, end));\r
- audio_samples_.erase(begin, end);\r
- }\r
+ muxer_.push(std::make_shared<std::vector<int16_t>>(audio_data, audio_data + sample_frame_count*2));\r
}\r
else\r
- muxer_.push(std::vector<int16_t>(frame_factory_->get_video_format_desc().audio_samples_per_frame, 0));\r
+ muxer_.push(std::make_shared<std::vector<int16_t>>(frame_factory_->get_video_format_desc().audio_samples_per_frame, 0));\r
\r
while(!muxer_.empty())\r
{\r
packets_.push(packet);\r
} \r
\r
- std::vector<std::vector<int16_t>> poll()\r
+ std::vector<std::shared_ptr<std::vector<int16_t>>> poll()\r
{\r
- std::vector<std::vector<int16_t>> result;\r
+ std::vector<std::shared_ptr<std::vector<int16_t>>> result;\r
\r
if(!codec_context_)\r
- result.push_back(std::vector<int16_t>(format_desc_.audio_samples_per_frame, 0));\r
+ result.push_back(std::make_shared<std::vector<int16_t>>(format_desc_.audio_samples_per_frame, 0));\r
else if(!packets_.empty())\r
{ \r
if(packets_.front()) \r
pkt.size = packets_.front()->size;\r
\r
for(int n = 0; n < 64 && pkt.size > 0; ++n)\r
- decode(pkt);\r
+ result.push_back(decode(pkt));\r
+ }\r
+ else \r
+ { \r
+ avcodec_flush_buffers(codec_context_.get());\r
+ result.push_back(nullptr);\r
}\r
- else\r
- flush();\r
\r
packets_.pop();\r
-\r
- while(audio_samples_.size() > format_desc_.audio_samples_per_frame)\r
- {\r
- const auto begin = audio_samples_.begin();\r
- const auto end = audio_samples_.begin() + format_desc_.audio_samples_per_frame;\r
-\r
- result.push_back(std::vector<int16_t>(begin, end));\r
- audio_samples_.erase(begin, end);\r
- }\r
}\r
\r
return result;\r
}\r
\r
- void decode(AVPacket& pkt)\r
+ std::shared_ptr<std::vector<int16_t>> decode(AVPacket& pkt)\r
{ \r
buffer1_.resize(AVCODEC_MAX_AUDIO_FRAME_SIZE*2, 0);\r
int written_bytes = buffer1_.size() - FF_INPUT_BUFFER_PADDING_SIZE;\r
const auto n_samples = buffer1_.size() / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);\r
const auto samples = reinterpret_cast<int16_t*>(buffer1_.data());\r
\r
- audio_samples_.insert(audio_samples_.end(), samples, samples + n_samples);\r
- }\r
-\r
- void flush()\r
- {\r
- auto truncate = audio_samples_.size() % format_desc_.audio_samples_per_frame;\r
- if(truncate > 0)\r
- {\r
- audio_samples_.resize(audio_samples_.size() - truncate); \r
- CASPAR_LOG(info) << L"Truncating " << truncate << L" audio-samples."; \r
- }\r
- avcodec_flush_buffers(codec_context_.get());\r
+ return std::make_shared<std::vector<int16_t>>(samples, samples + n_samples);\r
}\r
\r
bool ready() const\r
audio_decoder::audio_decoder(const std::shared_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc) : impl_(new implementation(context, format_desc)){}\r
void audio_decoder::push(const std::shared_ptr<AVPacket>& packet){impl_->push(packet);}\r
bool audio_decoder::ready() const{return impl_->ready();}\r
-std::vector<std::vector<int16_t>> audio_decoder::poll(){return impl_->poll();}\r
+std::vector<std::shared_ptr<std::vector<int16_t>>> audio_decoder::poll(){return impl_->poll();}\r
}
\ No newline at end of file
\r
void push(const std::shared_ptr<AVPacket>& packet);\r
bool ready() const;\r
- std::vector<std::vector<int16_t>> poll();\r
+ std::vector<std::shared_ptr<std::vector<int16_t>>> poll();\r
\r
private:\r
struct implementation;\r
, input_(safe_ptr<diagnostics::graph>(graph_), filename_, loop, start, length)\r
, video_decoder_(input_.context(), frame_factory, filter)\r
, audio_decoder_(input_.context(), frame_factory->get_video_format_desc())\r
- , muxer_(video_decoder_.fps(), format_desc_.mode, format_desc_.fps)\r
+ , muxer_(video_decoder_.fps(), format_desc_)\r
{\r
graph_->add_guide("frame-time", 0.5);\r
graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
{\r
if(output_buffer_.empty())\r
{ \r
- tasks_.wait();\r
+ //tasks_.wait();\r
\r
while(muxer_.size() > 0)\r
output_buffer_.push(muxer_.pop());\r
\r
- tasks_.run([=]\r
- {\r
+ //tasks_.run([=]\r
+ //{\r
frame_timer_.restart();\r
\r
for(int n = 0; n < 64 && muxer_.empty(); ++n)\r
decode_frame();\r
\r
graph_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));\r
- });\r
+ //});\r
}\r
\r
auto frame = core::basic_frame::late();\r
audio_decoder_.push(pkt);\r
}\r
}\r
+\r
+ decltype(video_decoder_.poll()) video_frames;\r
+ decltype(audio_decoder_.poll()) audio_samples;\r
\r
tbb::parallel_invoke(\r
- [=]\r
+ [&]\r
{\r
if(muxer_.video_frames() < 2)\r
- {\r
- BOOST_FOREACH(auto& video_frame, video_decoder_.poll())\r
- muxer_.push(video_frame);\r
- }\r
+ video_frames = video_decoder_.poll();\r
},\r
- [=]\r
+ [&]\r
{\r
if(muxer_.audio_chunks() < 2)\r
- {\r
- BOOST_FOREACH(auto& audio_chunk, audio_decoder_.poll())\r
- muxer_.push(audio_chunk);\r
- }\r
+ audio_samples = audio_decoder_.poll();\r
});\r
+\r
+ BOOST_FOREACH(auto& video, video_frames)\r
+ muxer_.push(video); \r
+\r
+ BOOST_FOREACH(auto& audio, audio_samples)\r
+ muxer_.push(audio);\r
}\r
\r
virtual std::wstring print() const\r
packet_buffer_.push(packet);\r
}\r
\r
- std::vector<safe_ptr<core::write_frame>> poll()\r
+ std::vector<std::shared_ptr<core::write_frame>> poll()\r
{ \r
- std::vector<safe_ptr<core::write_frame>> result;\r
+ std::vector<std::shared_ptr<core::write_frame>> result;\r
\r
if(!codec_context_)\r
result.push_back(make_safe<core::write_frame>(reinterpret_cast<int>(this)));\r
if(packet) // eof\r
decode(*packet, av_frames); \r
else\r
- flush(av_frames);\r
+ {\r
+ if(codec_context_->codec->capabilities | CODEC_CAP_DELAY)\r
+ {\r
+ // FIXME: This might cause bad performance.\r
+ AVPacket pkt = {0};\r
+ for(int n = 0; n < 8 && decode(pkt, av_frames); ++n){}\r
+ }\r
+\r
+ avcodec_flush_buffers(codec_context_.get());\r
+ }\r
\r
if(filter_)\r
{\r
\r
BOOST_FOREACH(auto& frame, av_frames)\r
result.push_back(make_write_frame(this, frame, frame_factory_));\r
+\r
+ if(!packet)\r
+ result.push_back(nullptr);\r
}\r
\r
return result;\r
}\r
\r
- void decode(AVPacket& packet, std::vector<safe_ptr<AVFrame>>& av_frames)\r
+ bool decode(AVPacket& packet, std::vector<safe_ptr<AVFrame>>& av_frames)\r
{\r
std::shared_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
\r
\r
if(frame_finished != 0) \r
av_frames.push_back(make_safe(decoded_frame));\r
- }\r
\r
- void flush(std::vector<safe_ptr<AVFrame>>& av_frames)\r
- {\r
- if(codec_context_->codec->capabilities | CODEC_CAP_DELAY)\r
- {\r
- // FIXME: This might cause bad performance.\r
- AVPacket pkt = {0};\r
- decode(pkt, av_frames);\r
- }\r
-\r
- avcodec_flush_buffers(codec_context_.get());\r
+ return frame_finished != 0;\r
}\r
-\r
+ \r
bool ready() const\r
{\r
return !codec_context_ || !packet_buffer_.empty();\r
\r
video_decoder::video_decoder(const std::shared_ptr<AVFormatContext>& context, const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filter) : impl_(new implementation(context, frame_factory, filter)){}\r
void video_decoder::push(const std::shared_ptr<AVPacket>& packet){impl_->push(packet);}\r
-std::vector<safe_ptr<core::write_frame>> video_decoder::poll(){return impl_->poll();}\r
+std::vector<std::shared_ptr<core::write_frame>> video_decoder::poll(){return impl_->poll();}\r
bool video_decoder::ready() const{return impl_->ready();}\r
core::video_mode::type video_decoder::mode(){return impl_->mode();}\r
double video_decoder::fps() const{return impl_->fps();}\r
\r
void push(const std::shared_ptr<AVPacket>& packet);\r
bool ready() const;\r
- std::vector<safe_ptr<core::write_frame>> poll();\r
+ std::vector<std::shared_ptr<core::write_frame>> poll();\r
\r
core::video_mode::type mode();\r
\r