]> git.sesse.net Git - casparcg/blobdiff - modules/decklink/consumer/decklink_consumer.cpp
Added support for more than 2 audio channels
[casparcg] / modules / decklink / consumer / decklink_consumer.cpp
index e18afee94950c59502c5b8480951c3475f7cb0f6..4eec12ed22aa75e9a787eb0960e04c1d439c5c95 100644 (file)
@@ -38,6 +38,7 @@
 #include <common/memory/memshfl.h>\r
 \r
 #include <core/consumer/frame_consumer.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <tbb/concurrent_queue.h>\r
 #include <tbb/cache_aligned_allocator.h>\r
@@ -45,6 +46,7 @@
 #include <boost/circular_buffer.hpp>\r
 #include <boost/timer.hpp>\r
 #include <boost/property_tree/ptree.hpp>\r
+#include <boost/algorithm/string.hpp>\r
 \r
 namespace caspar { namespace decklink { \r
        \r
@@ -64,16 +66,18 @@ struct configuration
                default_latency\r
        };\r
 \r
-       size_t          device_index;\r
-       bool            embedded_audio;\r
-       keyer_t         keyer;\r
-       latency_t       latency;\r
-       bool            key_only;\r
-       size_t          base_buffer_depth;\r
+       size_t                                  device_index;\r
+       bool                                    embedded_audio;\r
+       core::channel_layout    audio_layout;\r
+       keyer_t                                 keyer;\r
+       latency_t                               latency;\r
+       bool                                    key_only;\r
+       size_t                                  base_buffer_depth;\r
        \r
        configuration()\r
                : device_index(1)\r
                , embedded_audio(false)\r
+               , audio_layout(core::default_channel_layout_repository().get_by_name(L"STEREO"))\r
                , keyer(default_keyer)\r
                , latency(default_latency)\r
                , key_only(false)\r
@@ -85,6 +89,17 @@ struct configuration
        {\r
                return base_buffer_depth + (latency == low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);\r
        }\r
+\r
+       int num_out_channels() const\r
+       {\r
+               if (audio_layout.num_channels <= 2)\r
+                       return 2;\r
+               \r
+               if (audio_layout.num_channels <= 8)\r
+                       return 8;\r
+\r
+               return 16;\r
+       }\r
 };\r
 \r
 class decklink_frame : public IDeckLinkVideoFrame\r
@@ -323,7 +338,7 @@ public:
        \r
        void enable_audio()\r
        {\r
-               if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))\r
+               if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, config_.num_out_channels(), bmdAudioOutputStreamTimestamped)))\r
                                BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output."));\r
                                \r
                if(FAILED(output_->SetAudioCallback(this)))\r
@@ -371,7 +386,8 @@ public:
                        {\r
                                graph_->set_tag("late-frame");\r
                                video_scheduled_ += format_desc_.duration;\r
-                               audio_scheduled_ += reinterpret_cast<decklink_frame*>(completed_frame)->audio_data().size()/format_desc_.audio_channels;\r
+                               auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);\r
+                               audio_scheduled_ += dframe->audio_data().size()/config_.num_out_channels();\r
                                //++video_scheduled_;\r
                                //audio_scheduled_ += format_desc_.audio_cadence[0];\r
                                //++audio_scheduled_;\r
@@ -415,7 +431,11 @@ public:
                                        start_playback();                               \r
                                }\r
                                else\r
-                                       schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));      \r
+                               {\r
+                                       core::audio_buffer silent_audio(format_desc_.audio_cadence[preroll_count_ % format_desc_.audio_cadence.size()] * config_.num_out_channels(), 0);\r
+                                       auto view = core::make_multichannel_view<int32_t>(silent_audio.begin(), silent_audio.end(), config_.audio_layout, config_.num_out_channels());\r
+                                       schedule_next_audio(view);\r
+                               }\r
                        }\r
                        else\r
                        {\r
@@ -424,13 +444,13 @@ public:
                                while (audio_frame_buffer_.try_pop(frame))\r
                                {\r
                                        send_completion_.try_completion();\r
-                                       schedule_next_audio(frame->audio_data());\r
+                                       schedule_next_audio(frame->multichannel_view());\r
                                }\r
                        }\r
 \r
                        unsigned long buffered;\r
                        output_->GetBufferedAudioSampleFrameCount(&buffered);\r
-                       graph_->set_value("buffered-audio", static_cast<double>(buffered)/(format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));\r
+                       graph_->set_value("buffered-audio", static_cast<double>(buffered)/(format_desc_.audio_cadence[0] * config_.num_out_channels() * 2));\r
                }\r
                catch(...)\r
                {\r
@@ -442,14 +462,46 @@ public:
                return S_OK;\r
        }\r
 \r
-       template<typename T>\r
-       void schedule_next_audio(const T& audio_data)\r
+       template<typename View>\r
+       void schedule_next_audio(const View& view)\r
        {\r
-               const int sample_frame_count = audio_data.size()/format_desc_.audio_channels;\r
+               const int sample_frame_count = view.num_samples();\r
+\r
+               if (core::needs_rearranging(\r
+                               view, config_.audio_layout, config_.num_out_channels()))\r
+               {\r
+                       std::vector<int32_t> resulting_audio_data;\r
+                       resulting_audio_data.resize(\r
+                                       sample_frame_count * config_.num_out_channels());\r
+\r
+                       auto dest_view = core::make_multichannel_view<int32_t>(\r
+                                       resulting_audio_data.begin(), \r
+                                       resulting_audio_data.end(),\r
+                                       config_.audio_layout,\r
+                                       config_.num_out_channels());\r
+\r
+                       core::rearrange_or_rearrange_and_mix(\r
+                                       view, dest_view, core::default_mix_config_repository());\r
+\r
+                       if (config_.audio_layout.num_channels == 1) // mono\r
+                               boost::copy(                            // duplicate L to R\r
+                                               dest_view.channel(0),\r
+                                               dest_view.channel(1).begin());\r
 \r
-               audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));\r
+                       audio_container_.push_back(std::move(resulting_audio_data));\r
+               }\r
+               else\r
+               {\r
+                       audio_container_.push_back(\r
+                                       std::vector<int32_t>(view.raw_begin(), view.raw_end()));\r
+               }\r
 \r
-               if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))\r
+               if(FAILED(output_->ScheduleAudioSamples(\r
+                               audio_container_.back().data(),\r
+                               sample_frame_count,\r
+                               audio_scheduled_,\r
+                               format_desc_.audio_sample_rate,\r
+                               nullptr)))\r
                        CASPAR_LOG(error) << print() << L" Failed to schedule audio.";\r
 \r
                audio_scheduled_ += sample_frame_count;\r
@@ -572,7 +624,7 @@ public:
        \r
        virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override\r
        {\r
-               CASPAR_VERIFY(audio_cadence_.front() * format_desc_.audio_channels == static_cast<size_t>(frame->audio_data().size()));\r
+               CASPAR_VERIFY(audio_cadence_.front() * frame->num_channels() == static_cast<size_t>(frame->audio_data().size()));\r
                boost::range::rotate(audio_cadence_, std::begin(audio_cadence_)+1);\r
 \r
                return context_->send(frame);\r
@@ -653,6 +705,9 @@ safe_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptre
        config.device_index                     = ptree.get(L"device",                  config.device_index);\r
        config.embedded_audio           = ptree.get(L"embedded-audio",  config.embedded_audio);\r
        config.base_buffer_depth        = ptree.get(L"buffer-depth",    config.base_buffer_depth);\r
+       config.audio_layout =\r
+               core::default_channel_layout_repository().get_by_name(\r
+                               boost::to_upper_copy(ptree.get(L"channel-layout", L"STEREO")));\r
 \r
        return make_safe<decklink_consumer_proxy>(config);\r
 }\r