<ItemGroup>\r
<ClInclude Include="mixer\gpu\shader.h" />\r
<ClInclude Include="mixer\image\blending_glsl.h" />\r
- <ClInclude Include="producer\frame_muxer.h" />\r
<ClInclude Include="video_channel.h" />\r
<ClInclude Include="video_channel_context.h" />\r
<ClInclude Include="consumer\output.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
- <ClCompile Include="producer\frame_muxer.cpp">\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- </ClCompile>\r
<ClCompile Include="video_channel.cpp" />\r
<ClCompile Include="consumer\frame_consumer.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<ClInclude Include="mixer\gpu\shader.h">\r
<Filter>source\mixer\gpu</Filter>\r
</ClInclude>\r
- <ClInclude Include="producer\frame_muxer.h">\r
- <Filter>source\producer</Filter>\r
- </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<ClCompile Include="mixer\gpu\shader.cpp">\r
<Filter>source\mixer\gpu</Filter>\r
</ClCompile>\r
- <ClCompile Include="producer\frame_muxer.cpp">\r
- <Filter>source\producer</Filter>\r
- </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
std::deque<std::vector<int16_t>> audio_data_;\r
std::stack<core::audio_transform> transform_stack_;\r
\r
- std::map<int, core::audio_transform> prev_audio_transforms_;\r
- std::map<int, core::audio_transform> next_audio_transforms_;\r
+ std::map<const void*, core::audio_transform> prev_audio_transforms_;\r
+ std::map<const void*, core::audio_transform> next_audio_transforms_;\r
\r
const core::video_format_desc format_desc_;\r
\r
pixel_format_desc desc;\r
std::vector<safe_ptr<device_buffer>> textures;\r
core::image_transform transform;\r
- int tag;\r
+ const void* tag;\r
};\r
\r
bool operator==(const render_item& lhs, const render_item& rhs)\r
\r
safe_ptr<write_frame> create_frame(const void* tag, const core::pixel_format_desc& desc)\r
{\r
- return make_safe<write_frame>(channel_.ogl(), reinterpret_cast<int>(tag), desc);\r
+ return make_safe<write_frame>(channel_.ogl(), tag, desc);\r
}\r
};\r
\r
std::vector<safe_ptr<device_buffer>> textures_;\r
std::vector<int16_t> audio_data_;\r
const core::pixel_format_desc desc_;\r
- int tag_;\r
+ const void* tag_;\r
core::video_mode::type mode_;\r
\r
- implementation(int tag)\r
+ implementation(const void* tag)\r
: tag_(tag)\r
{\r
}\r
\r
- implementation(ogl_device& ogl, int tag, const core::pixel_format_desc& desc) \r
+ implementation(ogl_device& ogl, const void* tag, const core::pixel_format_desc& desc) \r
: ogl_(&ogl)\r
, desc_(desc)\r
, tag_(tag)\r
}\r
};\r
\r
-write_frame::write_frame(int tag) : impl_(new implementation(tag)){}\r
-write_frame::write_frame(ogl_device& ogl, int32_t tag, const core::pixel_format_desc& desc) \r
+write_frame::write_frame(const void* tag) : impl_(new implementation(tag)){}\r
+write_frame::write_frame(ogl_device& ogl, const void* tag, const core::pixel_format_desc& desc) \r
: impl_(new implementation(ogl, tag, desc)){}\r
write_frame::write_frame(const write_frame& other) : impl_(new implementation(*other.impl_)){}\r
void write_frame::accept(core::frame_visitor& visitor){impl_->accept(*this, visitor);}\r
{\r
return boost::iterator_range<const int16_t*>(impl_->audio_data_.data(), impl_->audio_data_.data() + impl_->audio_data_.size());\r
}\r
-int write_frame::tag() const {return impl_->tag_;}\r
+const void* write_frame::tag() const {return impl_->tag_;}\r
const core::pixel_format_desc& write_frame::get_pixel_format_desc() const{return impl_->desc_;}\r
const std::vector<safe_ptr<device_buffer>>& write_frame::get_textures() const{return impl_->textures_;}\r
void write_frame::commit(size_t plane_index){impl_->commit(plane_index);}\r
class write_frame : public core::basic_frame, boost::noncopyable\r
{\r
public: \r
- write_frame(int tag);\r
- explicit write_frame(ogl_device& ogl, int tag, const core::pixel_format_desc& desc);\r
+ write_frame(const void* tag);\r
+ explicit write_frame(ogl_device& ogl, const void* tag, const core::pixel_format_desc& desc);\r
write_frame(const write_frame& other);\r
\r
virtual boost::iterator_range<uint8_t*> image_data(size_t plane_index = 0); \r
\r
virtual void accept(core::frame_visitor& visitor);\r
\r
- virtual int tag() const;\r
+ virtual const void* tag() const;\r
\r
const core::pixel_format_desc& get_pixel_format_desc() const;\r
\r
+++ /dev/null
-#pragma once\r
-\r
-#include "../video_format.h"\r
-\r
-#include <common/memory/safe_ptr.h>\r
-\r
-#include <vector>\r
-\r
-namespace caspar { namespace core {\r
-\r
-class write_frame;\r
-class basic_frame;\r
-\r
-class frame_muxer\r
-{\r
-public:\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
-\r
- size_t size() const;\r
- bool empty() const;\r
-\r
- safe_ptr<basic_frame> pop();\r
-private:\r
- struct implementation;\r
- safe_ptr<implementation> impl_;\r
-};\r
-\r
-}}
\ No newline at end of file
\r
#include "../../ffmpeg/producer/filter/filter.h"\r
#include "../../ffmpeg/producer/util.h"\r
+#include "../../ffmpeg/producer/frame_muxer.h"\r
\r
#include <common/diagnostics/graph.h>\r
#include <common/concurrency/com_context.h>\r
#include <core/mixer/write_frame.h>\r
#include <core/producer/frame/audio_transform.h>\r
#include <core/producer/frame/frame_factory.h>\r
-#include <core/producer/frame_muxer.h>\r
\r
#include <tbb/concurrent_queue.h>\r
#include <tbb/atomic.h>\r
\r
namespace caspar { \r
\r
-class decklink_producer : public IDeckLinkInputCallback\r
+class decklink_producer : boost::noncopyable, public IDeckLinkInputCallback\r
{ \r
CComPtr<IDeckLink> decklink_;\r
CComQIPtr<IDeckLinkInput> input_;\r
std::exception_ptr exception_;\r
filter filter_;\r
\r
- core::frame_muxer muxer_;\r
+ frame_muxer muxer_;\r
\r
public:\r
decklink_producer(const core::video_format_desc& format_desc, size_t device_index, const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filter)\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())\r
+ , muxer_(double_rate(filter) ? format_desc.fps * 2.0 : format_desc.fps, frame_factory->get_video_format_desc(), frame_factory)\r
{\r
frame_buffer_.set_capacity(2);\r
\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="producer\frame_muxer.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="producer\input.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<ClInclude Include="producer\audio\audio_decoder.h" />\r
<ClInclude Include="producer\ffmpeg_producer.h" />\r
<ClInclude Include="producer\filter\filter.h" />\r
+ <ClInclude Include="producer\frame_muxer.h" />\r
<ClInclude Include="producer\input.h" />\r
<ClInclude Include="producer\util.h" />\r
<ClInclude Include="producer\video\video_decoder.h" />\r
<ClCompile Include="producer\filter\filter.cpp">\r
<Filter>source\producer\filter</Filter>\r
</ClCompile>\r
+ <ClCompile Include="producer\frame_muxer.cpp">\r
+ <Filter>source\producer</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="producer\ffmpeg_producer.h">\r
<ClInclude Include="producer\util.h">\r
<Filter>source\producer</Filter>\r
</ClInclude>\r
+ <ClInclude Include="producer\frame_muxer.h">\r
+ <Filter>source\producer</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
\r
#include "ffmpeg_producer.h"\r
\r
+#include "frame_muxer.h"\r
#include "input.h"\r
#include "audio/audio_decoder.h"\r
#include "video/video_decoder.h"\r
#include <core/producer/frame/audio_transform.h>\r
#include <core/producer/frame/basic_frame.h>\r
#include <core/producer/color/color_producer.h>\r
-#include <core/producer/frame_muxer.h>\r
\r
#include <common/env.h>\r
\r
\r
std::queue<safe_ptr<core::basic_frame>> output_buffer_;\r
\r
- core::frame_muxer muxer_;\r
+ frame_muxer muxer_;\r
\r
tbb::task_group tasks_;\r
\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_)\r
+ , muxer_(video_decoder_.fps(), format_desc_, frame_factory)\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
#include "frame_muxer.h"\r
\r
-#include "frame/basic_frame.h"\r
-#include "frame/image_transform.h"\r
-#include "frame/pixel_format.h"\r
-#include "../mixer/write_frame.h"\r
+#include "filter/filter.h"\r
+\r
+#include "util.h"\r
+\r
+#include <core/producer/frame/basic_frame.h>\r
+#include <core/producer/frame/image_transform.h>\r
+#include <core/producer/frame/pixel_format.h>\r
+#include <core/producer/frame/frame_factory.h>\r
+#include <core/mixer/write_frame.h>\r
\r
#include <common/env.h>\r
#include <common/log/log.h>\r
\r
#include <boost/range/algorithm_ext/push_back.hpp>\r
\r
-namespace caspar { namespace core {\r
- \r
+using namespace caspar::core;\r
+\r
+namespace caspar {\r
+\r
struct display_mode\r
{\r
enum type\r
return display_mode::invalid;\r
}\r
\r
-struct frame_muxer::implementation\r
+struct frame_muxer::implementation : boost::noncopyable\r
{ \r
std::queue<safe_ptr<write_frame>> video_frames_;\r
std::vector<int16_t> audio_samples_;\r
\r
size_t audio_sample_count_;\r
size_t video_frame_count_;\r
+\r
+ std::unique_ptr<filter> filter_;\r
+ safe_ptr<core::frame_factory> frame_factory_;\r
\r
- implementation(double in_fps, const video_format_desc& format_desc)\r
+ implementation(double in_fps, const video_format_desc& format_desc, const safe_ptr<core::frame_factory>& frame_factory)\r
: display_mode_(display_mode::invalid)\r
, in_fps_(in_fps)\r
, format_desc_(format_desc)\r
, auto_mode_(env::properties().get("configuration.auto-mode", false))\r
, audio_sample_count_(0)\r
, video_frame_count_(0)\r
+ , frame_factory_(frame_factory)\r
{\r
}\r
\r
video_frame_count_ = 0;\r
return;\r
}\r
+ \r
+ if(display_mode_ == display_mode::invalid)\r
+ display_mode_ = auto_mode_ ? get_display_mode(video_frame->get_type(), in_fps_, format_desc_.mode, format_desc_.fps) : display_mode::simple;\r
+\r
+ if(display_mode_ != display_mode::deinterlace && display_mode_ != display_mode::deinterlace_bob) \r
+ video_frame->commit();\r
\r
++video_frame_count_;\r
\r
\r
return samples;\r
}\r
-\r
+ \r
void process()\r
{\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_, format_desc_.mode, format_desc_.fps) : display_mode::simple;\r
-\r
+ \r
switch(display_mode_)\r
{\r
case display_mode::simple:\r
\r
void deinterlace_bob()\r
{\r
- BOOST_THROW_EXCEPTION(not_implemented() << msg_info("deinterlace_bob"));\r
+ if(video_frames_.empty() || audio_samples_.size()/2 < format_desc_.audio_samples_per_frame)\r
+ return;\r
+\r
+ if(!filter_)\r
+ filter_.reset(new filter(L"YADIF=1:-1"));\r
+ \r
+ auto frame = pop_video();\r
+\r
+ filter_->push(as_av_frame(frame));\r
+ auto av_frames = filter_->poll();\r
+\r
+ if(av_frames.size() < 2)\r
+ return;\r
+\r
+ auto frame1 = make_write_frame(frame->tag(), av_frames.at(0), frame_factory_);\r
+ frame1->commit();\r
+ frame1->audio_data() = pop_audio();\r
+ \r
+ auto frame2 = make_write_frame(frame->tag(), av_frames.at(1), frame_factory_);\r
+ frame2->commit();\r
+ frame2->audio_data() = pop_audio();\r
+ \r
+ frame_buffer_.push(frame1);\r
+ frame_buffer_.push(frame2);\r
}\r
\r
void deinterlace()\r
{\r
- BOOST_THROW_EXCEPTION(not_implemented() << msg_info("deinterlace"));\r
+ if(video_frames_.empty() || audio_samples_.size() < format_desc_.audio_samples_per_frame)\r
+ return;\r
+\r
+ if(!filter_)\r
+ filter_.reset(new filter(L"YADIF=0:-1"));\r
+ \r
+ auto frame = pop_video();\r
+\r
+ filter_->push(as_av_frame(frame));\r
+ auto av_frames = filter_->poll();\r
+\r
+ if(av_frames.empty())\r
+ return;\r
+\r
+ auto frame1 = make_write_frame(frame->tag(), av_frames.at(0), frame_factory_);\r
+ frame1->commit();\r
+ frame1->audio_data() = pop_audio();\r
+ \r
+ frame_buffer_.push(frame1);\r
}\r
};\r
\r
-frame_muxer::frame_muxer(double in_fps, const video_format_desc& format_desc)\r
- : impl_(implementation(in_fps, format_desc)){}\r
+frame_muxer::frame_muxer(double in_fps, const video_format_desc& format_desc, const safe_ptr<core::frame_factory>& frame_factory)\r
+ : impl_(new implementation(in_fps, format_desc, frame_factory)){}\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::video_frames() const{return impl_->video_frames_.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
+}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <common/memory/safe_ptr.h>\r
+\r
+#include <core/video_format.h>\r
+\r
+#include <boost/noncopyable.hpp>\r
+\r
+#include <vector>\r
+\r
+namespace caspar { \r
+ \r
+namespace core {\r
+\r
+class write_frame;\r
+class basic_frame;\r
+struct frame_factory;\r
+\r
+}\r
+\r
+class frame_muxer : boost::noncopyable\r
+{\r
+public:\r
+ frame_muxer(double in_fps, const core::video_format_desc& format_desc, const safe_ptr<core::frame_factory>& frame_factory);\r
+ \r
+ void push(const std::shared_ptr<core::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
+\r
+ size_t size() const;\r
+ bool empty() const;\r
+\r
+ safe_ptr<core::basic_frame> pop();\r
+private:\r
+ struct implementation;\r
+ safe_ptr<implementation> impl_;\r
+};\r
+\r
+}
\ No newline at end of file
\r
sws_scale(sws_context.get(), decoded_frame->data, decoded_frame->linesize, 0, height, av_frame->data, av_frame->linesize); \r
pool.push(sws_context);\r
-\r
- write->commit();\r
}\r
else\r
{\r
for(size_t y = r.begin(); y != r.end(); ++y)\r
fast_memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize);\r
});\r
-\r
- write->commit(n);\r
});\r
}\r
\r
std::queue<std::shared_ptr<AVPacket>> packet_buffer_;\r
\r
std::unique_ptr<filter> filter_;\r
- tbb::task_group filter_tasks_;\r
\r
double fps_;\r
public:\r
fps_ *= 2;\r
}\r
\r
- ~implementation()\r
- {\r
- filter_tasks_.cancel();\r
- filter_tasks_.wait();\r
- }\r
- \r
void push(const std::shared_ptr<AVPacket>& packet)\r
{\r
if(!codec_context_)\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
+ result.push_back(make_safe<core::write_frame>(this));\r
else if(!packet_buffer_.empty())\r
{\r
std::vector<safe_ptr<AVFrame>> av_frames;\r
\r
if(filter_)\r
{\r
- auto av_frames2 = std::move(av_frames);\r
-\r
- filter_tasks_.wait();\r
-\r
+ BOOST_FOREACH(auto& frame, av_frames) \r
+ filter_->push(frame);\r
+ \r
av_frames = filter_->poll();\r
-\r
- filter_tasks_.run([=]\r
- {\r
- BOOST_FOREACH(auto& frame, av_frames2) \r
- filter_->push(frame);\r
- });\r
}\r
\r
BOOST_FOREACH(auto& frame, av_frames)\r